diff --git a/src/com/limelight/nvstream/NvConnection.java b/src/com/limelight/nvstream/NvConnection.java index 9004f428..1b1f6a43 100644 --- a/src/com/limelight/nvstream/NvConnection.java +++ b/src/com/limelight/nvstream/NvConnection.java @@ -27,7 +27,7 @@ public class NvConnection { private NvControl controlStream; private NvController inputStream; private Surface video; - private NvVideoStream videoStream = new NvVideoStream(); + private NvVideoStream videoStream; private NvAudioStream audioStream = new NvAudioStream(); private ThreadPoolExecutor threadPool; @@ -100,8 +100,12 @@ public class NvConnection { { threadPool.shutdownNow(); - videoStream.abort(); - audioStream.abort(); + if (videoStream != null) { + videoStream.abort(); + } + if (audioStream != null) { + audioStream.abort(); + } if (controlStream != null) { controlStream.abort(); @@ -131,9 +135,10 @@ public class NvConnection { try { startSteamBigPicture(); performHandshake(); + beginControlStream(); + videoStream = new NvVideoStream(controlStream); videoStream.startVideoStream(host, video); audioStream.startAudioStream(host); - beginControlStream(); controlStream.startJitterPackets(); startController(); activity.hideSystemUi(); diff --git a/src/com/limelight/nvstream/NvControl.java b/src/com/limelight/nvstream/NvControl.java index 3cfaf47d..4a4a4402 100644 --- a/src/com/limelight/nvstream/NvControl.java +++ b/src/com/limelight/nvstream/NvControl.java @@ -8,7 +8,9 @@ import java.net.UnknownHostException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -public class NvControl { +import com.limelight.nvstream.av.ConnectionStatusListener; + +public class NvControl implements ConnectionStatusListener { public static final int PORT = 47995; @@ -31,27 +33,8 @@ public class NvControl { public static final short PTYPE_1405 = 0x1405; public static final short PPAYLEN_1405 = 0x0000; - public static final short PTYPE_1404 = 0x1404; - public static final short PPAYLEN_1404 = 0x0010; - public static final byte[] PPAYLOAD_1404 = new byte[] - { - 0x02, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x02, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00, - 0x00 - }; + public static final short PTYPE_RESYNC = 0x1404; + public static final short PPAYLEN_RESYNC = 16; public static final short PTYPE_CONFIG = 0x1205; public static final short PPAYLEN_CONFIG = 0x0004; @@ -217,6 +200,12 @@ public class NvControl { } catch (IOException e) {} } + public void requestResync() throws IOException + { + System.out.println("CTL: Requesting IDR frame"); + sendResync(); + } + public void start() throws IOException { System.out.println("CTL: Sending hello"); @@ -227,8 +216,6 @@ public class NvControl { pingPong(); System.out.println("CTL: Sending and waiting for 1405"); send1405AndGetResponse(); - //System.out.println("CTL: Sending 1404"); - //send1404(); System.out.println("CTL: Launching heartbeat thread"); heartbeatThread = new Thread() { @@ -243,6 +230,7 @@ public class NvControl { return; } + try { Thread.sleep(3000); } catch (InterruptedException e) { @@ -291,6 +279,16 @@ public class NvControl { sendPacket(new NvCtlPacket(PTYPE_HELLO, PPAYLEN_HELLO, PPAYLOAD_HELLO)); } + private void sendResync() throws IOException + { + ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLEN_RESYNC]).order(ByteOrder.LITTLE_ENDIAN); + + conf.putLong(0); + conf.putLong(0xFFFF); + + sendAndGetReply(new NvCtlPacket(PTYPE_RESYNC, PPAYLEN_RESYNC, conf.array())); + } + private void sendConfig() throws IOException { ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLOAD_CONFIG.length * 4 + 3]).order(ByteOrder.LITTLE_ENDIAN); @@ -453,4 +451,24 @@ public class NvControl { return status; } } + + @Override + public void connectionTerminated() { + abort(); + } + + @Override + public void connectionNeedsResync() { + new Thread(new Runnable() { + @Override + public void run() { + try { + requestResync(); + } catch (IOException e1) { + abort(); + return; + } + } + }).start(); + } } diff --git a/src/com/limelight/nvstream/NvVideoStream.java b/src/com/limelight/nvstream/NvVideoStream.java index 3ff549a1..22c7f2db 100644 --- a/src/com/limelight/nvstream/NvVideoStream.java +++ b/src/com/limelight/nvstream/NvVideoStream.java @@ -14,6 +14,7 @@ import java.util.concurrent.LinkedBlockingQueue; import com.limelight.nvstream.av.AvByteBufferDescriptor; import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvRtpPacket; +import com.limelight.nvstream.av.ConnectionStatusListener; import com.limelight.nvstream.av.video.AvVideoDepacketizer; import com.limelight.nvstream.av.video.AvVideoPacket; import com.limelight.nvstream.av.video.CpuDecoderRenderer; @@ -35,13 +36,20 @@ public class NvVideoStream { private LinkedList threads = new LinkedList(); - private AvVideoDepacketizer depacketizer = new AvVideoDepacketizer(); + private ConnectionStatusListener listener; + private AvVideoDepacketizer depacketizer; private DecoderRenderer decrend; private boolean startedRendering; private boolean aborting = false; + public NvVideoStream(ConnectionStatusListener listener) + { + this.listener = listener; + depacketizer = new AvVideoDepacketizer(listener); + } + public void abort() { if (aborting) { diff --git a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java index 59343212..24f0f94b 100644 --- a/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java +++ b/src/com/limelight/nvstream/av/video/AvVideoDepacketizer.java @@ -6,6 +6,7 @@ import java.util.concurrent.LinkedBlockingQueue; import com.limelight.nvstream.av.AvByteBufferDescriptor; import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvRtpPacket; +import com.limelight.nvstream.av.ConnectionStatusListener; import android.media.MediaCodec; @@ -19,8 +20,15 @@ public class AvVideoDepacketizer { // Sequencing state private short lastSequenceNumber; + private ConnectionStatusListener controlListener; + private LinkedBlockingQueue decodedUnits = new LinkedBlockingQueue(); + public AvVideoDepacketizer(ConnectionStatusListener controlListener) + { + this.controlListener = controlListener; + } + private void clearAvcNalState() { avcNalDataChain = null; @@ -191,6 +199,9 @@ public class AvVideoDepacketizer { // Reset the depacketizer state currentlyDecoding = AvDecodeUnit.TYPE_UNKNOWN; clearAvcNalState(); + + // Request an IDR frame + controlListener.connectionNeedsResync(); } lastSequenceNumber = seq; diff --git a/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java b/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java index 67d5be24..fc560b58 100644 --- a/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java +++ b/src/com/limelight/nvstream/av/video/CpuDecoderRenderer.java @@ -144,13 +144,13 @@ public class CpuDecoderRenderer implements DecoderRenderer { } @Override - public void submitDecodeUnit(AvDecodeUnit decodeUnit) { + public boolean submitDecodeUnit(AvDecodeUnit decodeUnit) { decoderBuffer.clear(); for (AvByteBufferDescriptor bbd : decodeUnit.getBufferList()) { decoderBuffer.put(bbd.data, bbd.offset, bbd.length); } - AvcDecoder.decode(decoderBuffer.array(), 0, decodeUnit.getDataLength()); + return (AvcDecoder.decode(decoderBuffer.array(), 0, decodeUnit.getDataLength()) == 0); } } diff --git a/src/com/limelight/nvstream/av/video/DecoderRenderer.java b/src/com/limelight/nvstream/av/video/DecoderRenderer.java index 18b6c273..eb77b6c8 100644 --- a/src/com/limelight/nvstream/av/video/DecoderRenderer.java +++ b/src/com/limelight/nvstream/av/video/DecoderRenderer.java @@ -13,5 +13,5 @@ public interface DecoderRenderer { public void release(); - public void submitDecodeUnit(AvDecodeUnit decodeUnit); + public boolean submitDecodeUnit(AvDecodeUnit decodeUnit); } diff --git a/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java b/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java index c479f89e..096cedde 100644 --- a/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java +++ b/src/com/limelight/nvstream/av/video/MediaCodecDecoderRenderer.java @@ -121,10 +121,10 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer { } @Override - public void submitDecodeUnit(AvDecodeUnit decodeUnit) { + public boolean submitDecodeUnit(AvDecodeUnit decodeUnit) { if (decodeUnit.getType() != AvDecodeUnit.TYPE_H264) { System.err.println("Unknown decode unit type"); - return; + return false; } int inputIndex = videoDecoder.dequeueInputBuffer(-1); @@ -145,5 +145,7 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer { 0, decodeUnit.getDataLength(), 0, decodeUnit.getFlags()); } + + return true; } }