diff options
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy')
| -rw-r--r-- | app/src/main/java/invalid/lena/scrcpy/Session.java | 2 | ||||
| -rw-r--r-- | app/src/main/java/invalid/lena/scrcpy/VideoSink.java | 28 |
2 files changed, 21 insertions, 9 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Session.java b/app/src/main/java/invalid/lena/scrcpy/Session.java index 39ef38a..73cecb7 100644 --- a/app/src/main/java/invalid/lena/scrcpy/Session.java +++ b/app/src/main/java/invalid/lena/scrcpy/Session.java @@ -394,7 +394,7 @@ public final class Session { ak = new AudioSink(); as = new AudioStream(s.audioIn, ak); - vk = new VideoSink(surface, ended); + vk = new VideoSink(surface, ended, ctrl::resetVideo); Controller ctrlRef = ctrl; vs = new VideoStream(s.videoIn, vk, (w, h) -> reportConnected(ctrlRef, w, h)); diff --git a/app/src/main/java/invalid/lena/scrcpy/VideoSink.java b/app/src/main/java/invalid/lena/scrcpy/VideoSink.java index 8c69b31..225eded 100644 --- a/app/src/main/java/invalid/lena/scrcpy/VideoSink.java +++ b/app/src/main/java/invalid/lena/scrcpy/VideoSink.java @@ -37,6 +37,7 @@ public final class VideoSink implements VideoFrames { private volatile Surface surface; private final Runnable onFatalError; + private final Runnable requestVideoReset; private final AtomicBoolean fatalReported = new AtomicBoolean(); private long renderedFrames; private volatile MediaCodec codec; @@ -60,9 +61,10 @@ public final class VideoSink implements VideoFrames { // so holding the reference is enough. private byte[] lastConfig; - public VideoSink(Surface surface, Runnable onFatalError) { + public VideoSink(Surface surface, Runnable onFatalError, Runnable requestVideoReset) { this.surface = surface; this.onFatalError = onFatalError; + this.requestVideoReset = requestVideoReset; } // Swap the output Surface. A destroyed Surface cannot remain attached to @@ -202,6 +204,7 @@ public final class VideoSink implements VideoFrames { // Called by VideoStream for every encoded frame, in order. @Override public void feed(byte[] data, long ptsUs, boolean isConfig, boolean isKeyframe) { + boolean reset = false; synchronized (lock) { if (released) return; if (data == null || data.length == 0 || data.length > MAX_FRAME_BYTES) { @@ -209,21 +212,30 @@ public final class VideoSink implements VideoFrames { return; } if (isConfig) lastConfig = data; + // Keep draining the socket while backgrounded, but do not build a + // queue that no decoder can consume. Surface attachment resets the + // encoder and starts a fresh decodable generation. + if (codec == null) return; // Try to drain immediately if there's a free input. while (!pending.isEmpty() && !freeInputs.isEmpty()) { submit(codec, pending.poll(), freeInputs.pollFirst()); } boolean waiting = pending.needsKeyframe(); if (!pending.offer(new VideoQueue.Frame(data, ptsUs, isConfig, isKeyframe))) { - return; - } - if (waiting && isKeyframe && !isConfig) { - Log.i("video sink: accepted keyframe after configure or overflow"); - } - while (!pending.isEmpty() && !freeInputs.isEmpty()) { - submit(codec, pending.poll(), freeInputs.pollFirst()); + reset = !waiting && pending.needsKeyframe(); + } else { + if (waiting && isKeyframe && !isConfig) { + Log.i("video sink: accepted keyframe after configure or overflow"); + } + while (!pending.isEmpty() && !freeInputs.isEmpty()) { + submit(codec, pending.poll(), freeInputs.pollFirst()); + } } } + if (reset && requestVideoReset != null) { + Log.w("video sink: input queue overflow, resetting encoder"); + requestVideoReset.run(); + } } @Override |