Request an IDR frame if a previous frame was lost or

This commit is contained in:
Cameron Gutman 2013-11-21 19:55:40 -05:00
parent e52b85a883
commit 80fdae3673
7 changed files with 78 additions and 34 deletions

View File

@ -27,7 +27,7 @@ public class NvConnection {
private NvControl controlStream; private NvControl controlStream;
private NvController inputStream; private NvController inputStream;
private Surface video; private Surface video;
private NvVideoStream videoStream = new NvVideoStream(); private NvVideoStream videoStream;
private NvAudioStream audioStream = new NvAudioStream(); private NvAudioStream audioStream = new NvAudioStream();
private ThreadPoolExecutor threadPool; private ThreadPoolExecutor threadPool;
@ -100,8 +100,12 @@ public class NvConnection {
{ {
threadPool.shutdownNow(); threadPool.shutdownNow();
videoStream.abort(); if (videoStream != null) {
audioStream.abort(); videoStream.abort();
}
if (audioStream != null) {
audioStream.abort();
}
if (controlStream != null) { if (controlStream != null) {
controlStream.abort(); controlStream.abort();
@ -131,9 +135,10 @@ public class NvConnection {
try { try {
startSteamBigPicture(); startSteamBigPicture();
performHandshake(); performHandshake();
beginControlStream();
videoStream = new NvVideoStream(controlStream);
videoStream.startVideoStream(host, video); videoStream.startVideoStream(host, video);
audioStream.startAudioStream(host); audioStream.startAudioStream(host);
beginControlStream();
controlStream.startJitterPackets(); controlStream.startJitterPackets();
startController(); startController();
activity.hideSystemUi(); activity.hideSystemUi();

View File

@ -8,7 +8,9 @@ import java.net.UnknownHostException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
public class NvControl { import com.limelight.nvstream.av.ConnectionStatusListener;
public class NvControl implements ConnectionStatusListener {
public static final int PORT = 47995; public static final int PORT = 47995;
@ -31,27 +33,8 @@ public class NvControl {
public static final short PTYPE_1405 = 0x1405; public static final short PTYPE_1405 = 0x1405;
public static final short PPAYLEN_1405 = 0x0000; public static final short PPAYLEN_1405 = 0x0000;
public static final short PTYPE_1404 = 0x1404; public static final short PTYPE_RESYNC = 0x1404;
public static final short PPAYLEN_1404 = 0x0010; public static final short PPAYLEN_RESYNC = 16;
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_CONFIG = 0x1205; public static final short PTYPE_CONFIG = 0x1205;
public static final short PPAYLEN_CONFIG = 0x0004; public static final short PPAYLEN_CONFIG = 0x0004;
@ -217,6 +200,12 @@ public class NvControl {
} catch (IOException e) {} } catch (IOException e) {}
} }
public void requestResync() throws IOException
{
System.out.println("CTL: Requesting IDR frame");
sendResync();
}
public void start() throws IOException public void start() throws IOException
{ {
System.out.println("CTL: Sending hello"); System.out.println("CTL: Sending hello");
@ -227,8 +216,6 @@ public class NvControl {
pingPong(); pingPong();
System.out.println("CTL: Sending and waiting for 1405"); System.out.println("CTL: Sending and waiting for 1405");
send1405AndGetResponse(); send1405AndGetResponse();
//System.out.println("CTL: Sending 1404");
//send1404();
System.out.println("CTL: Launching heartbeat thread"); System.out.println("CTL: Launching heartbeat thread");
heartbeatThread = new Thread() { heartbeatThread = new Thread() {
@ -243,6 +230,7 @@ public class NvControl {
return; return;
} }
try { try {
Thread.sleep(3000); Thread.sleep(3000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
@ -291,6 +279,16 @@ public class NvControl {
sendPacket(new NvCtlPacket(PTYPE_HELLO, PPAYLEN_HELLO, PPAYLOAD_HELLO)); 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 private void sendConfig() throws IOException
{ {
ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLOAD_CONFIG.length * 4 + 3]).order(ByteOrder.LITTLE_ENDIAN); ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLOAD_CONFIG.length * 4 + 3]).order(ByteOrder.LITTLE_ENDIAN);
@ -453,4 +451,24 @@ public class NvControl {
return status; 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();
}
} }

View File

@ -14,6 +14,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import com.limelight.nvstream.av.AvByteBufferDescriptor; import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvDecodeUnit;
import com.limelight.nvstream.av.AvRtpPacket; 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.AvVideoDepacketizer;
import com.limelight.nvstream.av.video.AvVideoPacket; import com.limelight.nvstream.av.video.AvVideoPacket;
import com.limelight.nvstream.av.video.CpuDecoderRenderer; import com.limelight.nvstream.av.video.CpuDecoderRenderer;
@ -35,13 +36,20 @@ public class NvVideoStream {
private LinkedList<Thread> threads = new LinkedList<Thread>(); private LinkedList<Thread> threads = new LinkedList<Thread>();
private AvVideoDepacketizer depacketizer = new AvVideoDepacketizer(); private ConnectionStatusListener listener;
private AvVideoDepacketizer depacketizer;
private DecoderRenderer decrend; private DecoderRenderer decrend;
private boolean startedRendering; private boolean startedRendering;
private boolean aborting = false; private boolean aborting = false;
public NvVideoStream(ConnectionStatusListener listener)
{
this.listener = listener;
depacketizer = new AvVideoDepacketizer(listener);
}
public void abort() public void abort()
{ {
if (aborting) { if (aborting) {

View File

@ -6,6 +6,7 @@ import java.util.concurrent.LinkedBlockingQueue;
import com.limelight.nvstream.av.AvByteBufferDescriptor; import com.limelight.nvstream.av.AvByteBufferDescriptor;
import com.limelight.nvstream.av.AvDecodeUnit; import com.limelight.nvstream.av.AvDecodeUnit;
import com.limelight.nvstream.av.AvRtpPacket; import com.limelight.nvstream.av.AvRtpPacket;
import com.limelight.nvstream.av.ConnectionStatusListener;
import android.media.MediaCodec; import android.media.MediaCodec;
@ -19,8 +20,15 @@ public class AvVideoDepacketizer {
// Sequencing state // Sequencing state
private short lastSequenceNumber; private short lastSequenceNumber;
private ConnectionStatusListener controlListener;
private LinkedBlockingQueue<AvDecodeUnit> decodedUnits = new LinkedBlockingQueue<AvDecodeUnit>(); private LinkedBlockingQueue<AvDecodeUnit> decodedUnits = new LinkedBlockingQueue<AvDecodeUnit>();
public AvVideoDepacketizer(ConnectionStatusListener controlListener)
{
this.controlListener = controlListener;
}
private void clearAvcNalState() private void clearAvcNalState()
{ {
avcNalDataChain = null; avcNalDataChain = null;
@ -191,6 +199,9 @@ public class AvVideoDepacketizer {
// Reset the depacketizer state // Reset the depacketizer state
currentlyDecoding = AvDecodeUnit.TYPE_UNKNOWN; currentlyDecoding = AvDecodeUnit.TYPE_UNKNOWN;
clearAvcNalState(); clearAvcNalState();
// Request an IDR frame
controlListener.connectionNeedsResync();
} }
lastSequenceNumber = seq; lastSequenceNumber = seq;

View File

@ -144,13 +144,13 @@ public class CpuDecoderRenderer implements DecoderRenderer {
} }
@Override @Override
public void submitDecodeUnit(AvDecodeUnit decodeUnit) { public boolean submitDecodeUnit(AvDecodeUnit decodeUnit) {
decoderBuffer.clear(); decoderBuffer.clear();
for (AvByteBufferDescriptor bbd : decodeUnit.getBufferList()) { for (AvByteBufferDescriptor bbd : decodeUnit.getBufferList()) {
decoderBuffer.put(bbd.data, bbd.offset, bbd.length); decoderBuffer.put(bbd.data, bbd.offset, bbd.length);
} }
AvcDecoder.decode(decoderBuffer.array(), 0, decodeUnit.getDataLength()); return (AvcDecoder.decode(decoderBuffer.array(), 0, decodeUnit.getDataLength()) == 0);
} }
} }

View File

@ -13,5 +13,5 @@ public interface DecoderRenderer {
public void release(); public void release();
public void submitDecodeUnit(AvDecodeUnit decodeUnit); public boolean submitDecodeUnit(AvDecodeUnit decodeUnit);
} }

View File

@ -121,10 +121,10 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer {
} }
@Override @Override
public void submitDecodeUnit(AvDecodeUnit decodeUnit) { public boolean submitDecodeUnit(AvDecodeUnit decodeUnit) {
if (decodeUnit.getType() != AvDecodeUnit.TYPE_H264) { if (decodeUnit.getType() != AvDecodeUnit.TYPE_H264) {
System.err.println("Unknown decode unit type"); System.err.println("Unknown decode unit type");
return; return false;
} }
int inputIndex = videoDecoder.dequeueInputBuffer(-1); int inputIndex = videoDecoder.dequeueInputBuffer(-1);
@ -145,5 +145,7 @@ public class MediaCodecDecoderRenderer implements DecoderRenderer {
0, decodeUnit.getDataLength(), 0, decodeUnit.getDataLength(),
0, decodeUnit.getFlags()); 0, decodeUnit.getFlags());
} }
return true;
} }
} }