diff --git a/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java b/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java index 0fb63dc2..f7b80f82 100644 --- a/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java +++ b/moonlight-common/src/com/limelight/nvstream/NvConnectionListener.java @@ -29,4 +29,5 @@ public interface NvConnectionListener { public void connectionTerminated(Exception e); public void displayMessage(String message); + public void displayTransientMessage(String message); } diff --git a/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java b/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java index 35262ddf..fe58cf63 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java +++ b/moonlight-common/src/com/limelight/nvstream/av/ConnectionStatusListener.java @@ -3,5 +3,7 @@ package com.limelight.nvstream.av; public interface ConnectionStatusListener { public void connectionTerminated(); - public void connectionNeedsResync(); + public void connectionDetectedPacketLoss(); + + public void connectionSinkTooSlow(); } diff --git a/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java index a102f8ec..60ea4852 100644 --- a/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java +++ b/moonlight-common/src/com/limelight/nvstream/av/video/VideoDepacketizer.java @@ -23,7 +23,7 @@ public class VideoDepacketizer { private ConnectionStatusListener controlListener; - private static final int DU_LIMIT = 15; + private static final int DU_LIMIT = 7; private LinkedBlockingQueue decodedUnits = new LinkedBlockingQueue(DU_LIMIT); public VideoDepacketizer(ConnectionStatusListener controlListener) @@ -47,7 +47,7 @@ public class VideoDepacketizer { // 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"); decodedUnits.clear(); - controlListener.connectionNeedsResync(); + controlListener.connectionSinkTooSlow(); } // Clear old state @@ -208,7 +208,7 @@ public class VideoDepacketizer { clearAvcNalState(); // Request an IDR frame - controlListener.connectionNeedsResync(); + controlListener.connectionDetectedPacketLoss(); } lastSequenceNumber = seq; diff --git a/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java b/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java index 9d40ded5..cdb7e945 100644 --- a/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java +++ b/moonlight-common/src/com/limelight/nvstream/control/ControlStream.java @@ -44,6 +44,15 @@ public class ControlStream implements ConnectionStatusListener { private InetAddress host; private Config config; + public static final int LOSS_PERIOD_MS = 5000; + public static final int MAX_LOSS_COUNT_IN_PERIOD = 5; + public static final int MAX_SLOW_SINK_COUNT = 3; + public static final int MESSAGE_DELAY_FACTOR = 5; + + private long lossTimestamp; + private int lossCount; + private int slowSinkCount; + private Socket s; private InputStream in; private OutputStream out; @@ -404,10 +413,35 @@ public class ControlStream implements ConnectionStatusListener { abort(); } - public void connectionNeedsResync() { + private void resyncConnection() { synchronized (resyncNeeded) { // Wake up the resync thread resyncNeeded.notify(); } } + + public void connectionDetectedPacketLoss() { + if (System.currentTimeMillis() > LOSS_PERIOD_MS + lossTimestamp) { + lossCount++; + lossTimestamp = System.currentTimeMillis(); + } + else { + if (++lossCount == MAX_LOSS_COUNT_IN_PERIOD) { + listener.displayTransientMessage("Detected excessive A/V data loss. Try improving your network connection or lowering stream settings."); + lossCount = -MAX_LOSS_COUNT_IN_PERIOD * MESSAGE_DELAY_FACTOR; + lossTimestamp = 0; + } + } + + resyncConnection(); + } + + public void connectionSinkTooSlow() { + 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(); + } }