aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/Session.java2
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/VideoSink.java28
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