mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-21 12:03:02 +00:00
Add support for direct submission of buffers to the renderers without a separate thread
This commit is contained in:
parent
cf3ac50d22
commit
63ee6ef79a
@ -11,9 +11,16 @@ public class AudioDepacketizer {
|
|||||||
private LinkedBlockingQueue<ByteBufferDescriptor> decodedUnits =
|
private LinkedBlockingQueue<ByteBufferDescriptor> decodedUnits =
|
||||||
new LinkedBlockingQueue<ByteBufferDescriptor>(DU_LIMIT);
|
new LinkedBlockingQueue<ByteBufferDescriptor>(DU_LIMIT);
|
||||||
|
|
||||||
|
private AudioRenderer directSubmitRenderer;
|
||||||
|
|
||||||
// Sequencing state
|
// Sequencing state
|
||||||
private short lastSequenceNumber;
|
private short lastSequenceNumber;
|
||||||
|
|
||||||
|
public AudioDepacketizer(AudioRenderer directSubmitRenderer)
|
||||||
|
{
|
||||||
|
this.directSubmitRenderer = directSubmitRenderer;
|
||||||
|
}
|
||||||
|
|
||||||
private void decodeData(byte[] data, int off, int len)
|
private void decodeData(byte[] data, int off, int len)
|
||||||
{
|
{
|
||||||
// Submit this data to the decoder
|
// Submit this data to the decoder
|
||||||
@ -24,8 +31,10 @@ public class AudioDepacketizer {
|
|||||||
// Return value of decode is frames (shorts) decoded per channel
|
// Return value of decode is frames (shorts) decoded per channel
|
||||||
decodeLen *= 2*OpusDecoder.getChannelCount();
|
decodeLen *= 2*OpusDecoder.getChannelCount();
|
||||||
|
|
||||||
// Put it on the decoded queue
|
if (directSubmitRenderer != null) {
|
||||||
if (!decodedUnits.offer(new ByteBufferDescriptor(pcmData, 0, decodeLen))) {
|
directSubmitRenderer.playDecodedAudio(pcmData, 0, decodeLen);
|
||||||
|
}
|
||||||
|
else if (!decodedUnits.offer(new ByteBufferDescriptor(pcmData, 0, decodeLen))) {
|
||||||
System.out.println("Audio player too slow! Forced to drop decoded samples");
|
System.out.println("Audio player too slow! Forced to drop decoded samples");
|
||||||
// Clear out the queue
|
// Clear out the queue
|
||||||
decodedUnits.clear();
|
decodedUnits.clear();
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
package com.limelight.nvstream.av.audio;
|
package com.limelight.nvstream.av.audio;
|
||||||
|
|
||||||
public interface AudioRenderer {
|
public interface AudioRenderer {
|
||||||
|
// playDecodedAudio() is lightweight, so don't use an extra thread for playback
|
||||||
|
public static final int CAPABILITY_DIRECT_SUBMIT = 0x1;
|
||||||
|
|
||||||
|
public int getCapabilities();
|
||||||
|
|
||||||
public void streamInitialized(int channelCount, int sampleRate);
|
public void streamInitialized(int channelCount, int sampleRate);
|
||||||
|
|
||||||
public void playDecodedAudio(byte[] audioData, int offset, int length);
|
public void playDecodedAudio(byte[] audioData, int offset, int length);
|
||||||
|
@ -23,7 +23,7 @@ public class AudioStream {
|
|||||||
|
|
||||||
private DatagramSocket rtp;
|
private DatagramSocket rtp;
|
||||||
|
|
||||||
private AudioDepacketizer depacketizer = new AudioDepacketizer();
|
private AudioDepacketizer depacketizer;
|
||||||
|
|
||||||
private LinkedList<Thread> threads = new LinkedList<Thread>();
|
private LinkedList<Thread> threads = new LinkedList<Thread>();
|
||||||
|
|
||||||
@ -79,7 +79,9 @@ public class AudioStream {
|
|||||||
|
|
||||||
startDepacketizerThread();
|
startDepacketizerThread();
|
||||||
|
|
||||||
startDecoderThread();
|
if ((streamListener.getCapabilities() & AudioRenderer.CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||||
|
startDecoderThread();
|
||||||
|
}
|
||||||
|
|
||||||
startUdpPingThread();
|
startUdpPingThread();
|
||||||
}
|
}
|
||||||
@ -102,6 +104,13 @@ public class AudioStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
streamListener.streamInitialized(OpusDecoder.getChannelCount(), OpusDecoder.getSampleRate());
|
streamListener.streamInitialized(OpusDecoder.getChannelCount(), OpusDecoder.getSampleRate());
|
||||||
|
|
||||||
|
if ((streamListener.getCapabilities() & AudioRenderer.CAPABILITY_DIRECT_SUBMIT) != 0) {
|
||||||
|
depacketizer = new AudioDepacketizer(streamListener);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
depacketizer = new AudioDepacketizer(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startDepacketizerThread()
|
private void startDepacketizerThread()
|
||||||
|
@ -7,6 +7,11 @@ public interface VideoDecoderRenderer {
|
|||||||
public static final int FLAG_FORCE_HARDWARE_DECODING = 0x2;
|
public static final int FLAG_FORCE_HARDWARE_DECODING = 0x2;
|
||||||
public static final int FLAG_FORCE_SOFTWARE_DECODING = 0x4;
|
public static final int FLAG_FORCE_SOFTWARE_DECODING = 0x4;
|
||||||
|
|
||||||
|
// SubmitDecodeUnit() is lightweight, so don't use an extra thread for decoding
|
||||||
|
public static final int CAPABILITY_DIRECT_SUBMIT = 0x1;
|
||||||
|
|
||||||
|
public int getCapabilities();
|
||||||
|
|
||||||
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags);
|
public void setup(int width, int height, int redrawRate, Object renderTarget, int drFlags);
|
||||||
|
|
||||||
public void start();
|
public void start();
|
||||||
|
@ -22,12 +22,14 @@ public class VideoDepacketizer {
|
|||||||
private ByteBufferDescriptor cachedDesc = new ByteBufferDescriptor(null, 0, 0);
|
private ByteBufferDescriptor cachedDesc = new ByteBufferDescriptor(null, 0, 0);
|
||||||
|
|
||||||
private ConnectionStatusListener controlListener;
|
private ConnectionStatusListener controlListener;
|
||||||
|
private VideoDecoderRenderer directSubmitDr;
|
||||||
|
|
||||||
private static final int DU_LIMIT = 7;
|
private static final int DU_LIMIT = 15;
|
||||||
private LinkedBlockingQueue<DecodeUnit> decodedUnits = new LinkedBlockingQueue<DecodeUnit>(DU_LIMIT);
|
private LinkedBlockingQueue<DecodeUnit> decodedUnits = new LinkedBlockingQueue<DecodeUnit>(DU_LIMIT);
|
||||||
|
|
||||||
public VideoDepacketizer(ConnectionStatusListener controlListener)
|
public VideoDepacketizer(VideoDecoderRenderer directSubmitDr, ConnectionStatusListener controlListener)
|
||||||
{
|
{
|
||||||
|
this.directSubmitDr = directSubmitDr;
|
||||||
this.controlListener = controlListener;
|
this.controlListener = controlListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +45,11 @@ public class VideoDepacketizer {
|
|||||||
if (avcNalDataChain != null && avcNalDataLength != 0) {
|
if (avcNalDataChain != null && avcNalDataLength != 0) {
|
||||||
// Construct the H264 decode unit
|
// Construct the H264 decode unit
|
||||||
DecodeUnit du = new DecodeUnit(DecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, 0);
|
DecodeUnit du = new DecodeUnit(DecodeUnit.TYPE_H264, avcNalDataChain, avcNalDataLength, 0);
|
||||||
if (!decodedUnits.offer(du)) {
|
if (directSubmitDr != null) {
|
||||||
|
// Submit directly to the decoder
|
||||||
|
directSubmitDr.submitDecodeUnit(du);
|
||||||
|
}
|
||||||
|
else if (!decodedUnits.offer(du)) {
|
||||||
// We need a new IDR frame since we're discarding data now
|
// We need a new IDR frame since we're discarding data now
|
||||||
System.out.println("Video decoder is too slow! Forced to drop decode units");
|
System.out.println("Video decoder is too slow! Forced to drop decode units");
|
||||||
decodedUnits.clear();
|
decodedUnits.clear();
|
||||||
|
@ -35,6 +35,7 @@ public class VideoStream {
|
|||||||
private LinkedList<Thread> threads = new LinkedList<Thread>();
|
private LinkedList<Thread> threads = new LinkedList<Thread>();
|
||||||
|
|
||||||
private NvConnectionListener listener;
|
private NvConnectionListener listener;
|
||||||
|
private ConnectionStatusListener avConnListener;
|
||||||
private VideoDepacketizer depacketizer;
|
private VideoDepacketizer depacketizer;
|
||||||
private StreamConfiguration streamConfig;
|
private StreamConfiguration streamConfig;
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ public class VideoStream {
|
|||||||
{
|
{
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
this.depacketizer = new VideoDepacketizer(avConnListener);
|
this.avConnListener = avConnListener;
|
||||||
this.streamConfig = streamConfig;
|
this.streamConfig = streamConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,6 +135,13 @@ public class VideoStream {
|
|||||||
if (decRend != null) {
|
if (decRend != null) {
|
||||||
decRend.setup(streamConfig.getWidth(), streamConfig.getHeight(),
|
decRend.setup(streamConfig.getWidth(), streamConfig.getHeight(),
|
||||||
60, renderTarget, drFlags);
|
60, renderTarget, drFlags);
|
||||||
|
|
||||||
|
if ((decRend.getCapabilities() & VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT) != 0) {
|
||||||
|
depacketizer = new VideoDepacketizer(decRend, avConnListener);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
depacketizer = new VideoDepacketizer(null, avConnListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,8 +172,10 @@ public class VideoStream {
|
|||||||
// Start the depacketizer thread to deal with the RTP data
|
// Start the depacketizer thread to deal with the RTP data
|
||||||
startDepacketizerThread();
|
startDepacketizerThread();
|
||||||
|
|
||||||
// Start decoding the data we're receiving
|
// Start a decode thread if we're not doing direct submit
|
||||||
startDecoderThread();
|
if ((decRend.getCapabilities() & VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT) == 0) {
|
||||||
|
startDecoderThread();
|
||||||
|
}
|
||||||
|
|
||||||
// Start the renderer
|
// Start the renderer
|
||||||
decRend.start();
|
decRend.start();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user