Refactor the video decoding path so the DecoderRenderer handles pulling decode units instead of dedicating a separate thread for this

This commit is contained in:
Cameron Gutman 2014-06-19 18:28:48 -07:00
parent 7b10e52808
commit 38423a9f37
3 changed files with 13 additions and 59 deletions

View File

@ -1,24 +1,17 @@
package com.limelight.nvstream.av.video; package com.limelight.nvstream.av.video;
import com.limelight.nvstream.av.DecodeUnit;
public interface VideoDecoderRenderer { public interface VideoDecoderRenderer {
public static final int FLAG_PREFER_QUALITY = 0x1; public static final int FLAG_PREFER_QUALITY = 0x1;
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 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(VideoDepacketizer depacketizer);
public void stop(); public void stop();
public void release(); public void release();
public boolean submitDecodeUnit(DecodeUnit decodeUnit);
} }

View File

@ -28,14 +28,12 @@ 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 = 15; 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(VideoDecoderRenderer directSubmitDr, ConnectionStatusListener controlListener) public VideoDepacketizer(ConnectionStatusListener controlListener)
{ {
this.directSubmitDr = directSubmitDr;
this.controlListener = controlListener; this.controlListener = controlListener;
} }
@ -68,11 +66,7 @@ public class VideoDepacketizer {
// Construct the H264 decode unit // Construct the H264 decode unit
DecodeUnit du = new DecodeUnit(DecodeUnit.TYPE_H264, avcFrameDataChain, avcFrameDataLength, flags, frameNumber); DecodeUnit du = new DecodeUnit(DecodeUnit.TYPE_H264, avcFrameDataChain, avcFrameDataLength, flags, frameNumber);
if (directSubmitDr != null) { if (!decodedUnits.offer(du)) {
// Submit directly to the decoder
directSubmitDr.submitDecodeUnit(du);
}
else if (!decodedUnits.offer(du)) {
LimeLog.warning("Video decoder is too slow! Forced to drop decode units"); LimeLog.warning("Video decoder is too slow! Forced to drop decode units");
// Invalidate all frames from the start of the DU queue // Invalidate all frames from the start of the DU queue
@ -92,7 +86,7 @@ public class VideoDepacketizer {
} }
} }
public void addInputDataSlow(VideoPacket packet, ByteBufferDescriptor location) private void addInputDataSlow(VideoPacket packet, ByteBufferDescriptor location)
{ {
while (location.length != 0) while (location.length != 0)
{ {
@ -175,7 +169,7 @@ public class VideoDepacketizer {
} }
} }
public void addInputDataFast(VideoPacket packet, ByteBufferDescriptor location, boolean firstPacket) private void addInputDataFast(VideoPacket packet, ByteBufferDescriptor location, boolean firstPacket)
{ {
if (firstPacket) { if (firstPacket) {
// Setup state for the new frame // Setup state for the new frame
@ -340,10 +334,15 @@ public class VideoDepacketizer {
addInputData(new VideoPacket(rtpPayload)); addInputData(new VideoPacket(rtpPayload));
} }
public DecodeUnit getNextDecodeUnit() throws InterruptedException public DecodeUnit takeNextDecodeUnit() throws InterruptedException
{ {
return decodedUnits.take(); return decodedUnits.take();
} }
public DecodeUnit pollNextDecodeUnit()
{
return decodedUnits.poll();
}
} }
class NAL { class NAL {

View File

@ -13,7 +13,6 @@ import java.util.LinkedList;
import com.limelight.nvstream.NvConnectionListener; import com.limelight.nvstream.NvConnectionListener;
import com.limelight.nvstream.StreamConfiguration; import com.limelight.nvstream.StreamConfiguration;
import com.limelight.nvstream.av.ByteBufferDescriptor; import com.limelight.nvstream.av.ByteBufferDescriptor;
import com.limelight.nvstream.av.DecodeUnit;
import com.limelight.nvstream.av.RtpPacket; import com.limelight.nvstream.av.RtpPacket;
import com.limelight.nvstream.av.ConnectionStatusListener; import com.limelight.nvstream.av.ConnectionStatusListener;
@ -140,12 +139,7 @@ public class VideoStream {
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(avConnListener);
depacketizer = new VideoDepacketizer(decRend, avConnListener);
}
else {
depacketizer = new VideoDepacketizer(null, avConnListener);
}
} }
} }
@ -173,44 +167,12 @@ public class VideoStream {
// early packets // early packets
startReceiveThread(); startReceiveThread();
// Start a decode thread if we're not doing direct submit
if ((decRend.getCapabilities() & VideoDecoderRenderer.CAPABILITY_DIRECT_SUBMIT) == 0) {
startDecoderThread();
}
// Start the renderer // Start the renderer
decRend.start(); decRend.start(depacketizer);
startedRendering = true; startedRendering = true;
} }
} }
private void startDecoderThread()
{
Thread t = new Thread() {
@Override
public void run() {
// Read the decode units generated from the RTP stream
while (!isInterrupted())
{
DecodeUnit du;
try {
du = depacketizer.getNextDecodeUnit();
} catch (InterruptedException e) {
listener.connectionTerminated(e);
return;
}
decRend.submitDecodeUnit(du);
}
}
};
threads.add(t);
t.setName("Video - Decoder");
t.setPriority(Thread.MAX_PRIORITY);
t.start();
}
private void startReceiveThread() private void startReceiveThread()
{ {
// Receive thread // Receive thread