Add support for direct submission of buffers to the renderers without a separate thread

This commit is contained in:
Cameron Gutman 2014-02-19 20:36:53 -05:00
parent cf3ac50d22
commit 63ee6ef79a
6 changed files with 55 additions and 11 deletions

View File

@ -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();

View File

@ -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);

View File

@ -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();
if ((streamListener.getCapabilities() & AudioRenderer.CAPABILITY_DIRECT_SUBMIT) == 0) {
startDecoderThread(); 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()

View File

@ -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();

View File

@ -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();

View File

@ -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
if ((decRend.getCapabilities() & VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT) == 0) {
startDecoderThread(); startDecoderThread();
}
// Start the renderer // Start the renderer
decRend.start(); decRend.start();