mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-04-19 23:10:11 +00:00
Increase resilience to packet loss. IDR frames are no longer requested if error correction data was lost. A maximum of one IDR frame is requested per corrupt frame. Error correction data is used to recover from the loss of a single-packet frame.
This commit is contained in:
@@ -8,6 +8,7 @@ import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
import com.limelight.nvstream.NvConnectionListener;
|
||||
import com.limelight.nvstream.StreamConfiguration;
|
||||
@@ -60,7 +61,7 @@ public class ControlStream implements ConnectionStatusListener {
|
||||
private Thread heartbeatThread;
|
||||
private Thread jitterThread;
|
||||
private Thread resyncThread;
|
||||
private Object resyncNeeded = new Object();
|
||||
private LinkedBlockingQueue<int[]> invalidReferenceFrameTuples = new LinkedBlockingQueue<int[]>();
|
||||
private boolean aborting = false;
|
||||
|
||||
public ControlStream(InetAddress host, NvConnectionListener listener, StreamConfiguration streamConfig)
|
||||
@@ -140,12 +141,6 @@ public class ControlStream implements ConnectionStatusListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void requestResync() throws IOException
|
||||
{
|
||||
System.out.println("CTL: Requesting IDR frame");
|
||||
sendResync();
|
||||
}
|
||||
|
||||
public void start() throws IOException
|
||||
{
|
||||
// Use a finite timeout during the handshake process
|
||||
@@ -188,18 +183,36 @@ public class ControlStream implements ConnectionStatusListener {
|
||||
public void run() {
|
||||
while (!isInterrupted())
|
||||
{
|
||||
int[] tuple;
|
||||
|
||||
// Wait for a tuple
|
||||
try {
|
||||
// Wait for notification of a resync needed
|
||||
synchronized (resyncNeeded) {
|
||||
resyncNeeded.wait();
|
||||
}
|
||||
tuple = invalidReferenceFrameTuples.take();
|
||||
} catch (InterruptedException e) {
|
||||
listener.connectionTerminated(e);
|
||||
return;
|
||||
}
|
||||
|
||||
// Aggregate all lost frames into one range
|
||||
int[] lastTuple = null;
|
||||
for (;;) {
|
||||
int[] nextTuple = lastTuple = invalidReferenceFrameTuples.poll();
|
||||
if (nextTuple == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
lastTuple = nextTuple;
|
||||
}
|
||||
|
||||
// Update the end of the range to the latest tuple
|
||||
if (lastTuple != null) {
|
||||
tuple[1] = lastTuple[1];
|
||||
}
|
||||
|
||||
try {
|
||||
requestResync();
|
||||
System.err.println("Invalidating reference frames from "+tuple[0]+" to "+tuple[1]);
|
||||
ControlStream.this.sendResync(tuple[0], tuple[1]);
|
||||
System.err.println("Frames invalidated");
|
||||
} catch (IOException e) {
|
||||
listener.connectionTerminated(e);
|
||||
return;
|
||||
@@ -243,12 +256,14 @@ public class ControlStream implements ConnectionStatusListener {
|
||||
return sendAndGetReply(new NvCtlPacket(PTYPE_1405, PPAYLEN_1405));
|
||||
}
|
||||
|
||||
private void sendResync() throws IOException
|
||||
private void sendResync(int firstLostFrame, int lastLostFrame) throws IOException
|
||||
{
|
||||
ByteBuffer conf = ByteBuffer.wrap(new byte[PPAYLEN_RESYNC]).order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
conf.putLong(0);
|
||||
conf.putLong(0xFFFFF);
|
||||
//conf.putLong(firstLostFrame);
|
||||
//conf.putLong(lastLostFrame);
|
||||
|
||||
sendAndGetReply(new NvCtlPacket(PTYPE_RESYNC, PPAYLEN_RESYNC, conf.array()));
|
||||
}
|
||||
@@ -413,14 +428,11 @@ public class ControlStream implements ConnectionStatusListener {
|
||||
abort();
|
||||
}
|
||||
|
||||
private void resyncConnection() {
|
||||
synchronized (resyncNeeded) {
|
||||
// Wake up the resync thread
|
||||
resyncNeeded.notify();
|
||||
}
|
||||
private void resyncConnection(int firstLostFrame, int lastLostFrame) {
|
||||
invalidReferenceFrameTuples.add(new int[]{firstLostFrame, lastLostFrame});
|
||||
}
|
||||
|
||||
public void connectionDetectedPacketLoss() {
|
||||
public void connectionDetectedFrameLoss(int firstLostFrame, int lastLostFrame) {
|
||||
if (System.currentTimeMillis() > LOSS_PERIOD_MS + lossTimestamp) {
|
||||
lossCount++;
|
||||
lossTimestamp = System.currentTimeMillis();
|
||||
@@ -433,15 +445,15 @@ public class ControlStream implements ConnectionStatusListener {
|
||||
}
|
||||
}
|
||||
|
||||
resyncConnection();
|
||||
resyncConnection(firstLostFrame, lastLostFrame);
|
||||
}
|
||||
|
||||
public void connectionSinkTooSlow() {
|
||||
public void connectionSinkTooSlow(int firstLostFrame, int lastLostFrame) {
|
||||
if (++slowSinkCount == MAX_SLOW_SINK_COUNT) {
|
||||
listener.displayTransientMessage("Your device is processing the A/V data too slowly. Try lowering stream settings.");
|
||||
slowSinkCount = -MAX_SLOW_SINK_COUNT * MESSAGE_DELAY_FACTOR;
|
||||
}
|
||||
|
||||
resyncConnection();
|
||||
resyncConnection(firstLostFrame, lastLostFrame);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user