diff --git a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java index a5fa4b06..92663f79 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java @@ -68,7 +68,7 @@ public class CameraCapture extends SurfaceCapture { } @Override - public void init() throws IOException { + protected void init() throws IOException { cameraThread = new HandlerThread("camera"); cameraThread.start(); cameraHandler = new Handler(cameraThread.getLooper()); @@ -256,7 +256,7 @@ public class CameraCapture extends SurfaceCapture { public void onDisconnected(CameraDevice camera) { Ln.w("Camera disconnected"); disconnected.set(true); - requestReset(); + invalidate(); } @Override diff --git a/server/src/main/java/com/genymobile/scrcpy/video/CaptureReset.java b/server/src/main/java/com/genymobile/scrcpy/video/CaptureReset.java new file mode 100644 index 00000000..20256d1e --- /dev/null +++ b/server/src/main/java/com/genymobile/scrcpy/video/CaptureReset.java @@ -0,0 +1,21 @@ +package com.genymobile.scrcpy.video; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class CaptureReset implements SurfaceCapture.CaptureListener { + + private final AtomicBoolean reset = new AtomicBoolean(); + + public boolean consumeReset() { + return reset.getAndSet(false); + } + + public void reset() { + reset.set(true); + } + + @Override + public void onInvalidated() { + reset(); + } +} diff --git a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java index 5cbdb792..5d61c4bd 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -46,7 +46,7 @@ public class NewDisplayCapture extends SurfaceCapture { } @Override - public void init() { + protected void init() { size = newDisplay.getSize(); dpi = newDisplay.getDpi(); if (size == null || dpi == 0) { diff --git a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java index 04e42800..c0d49f60 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java @@ -85,7 +85,7 @@ public class ScreenCapture extends SurfaceCapture { Ln.v("ScreenCapture: requestReset(): " + getSessionDisplaySize() + " -> (unknown)"); } setSessionDisplaySize(null); - requestReset(); + invalidate(); } else { Size size = di.getSize(); @@ -102,7 +102,7 @@ public class ScreenCapture extends SurfaceCapture { // Set the new size immediately, so that a future onDisplayChanged() event called before the asynchronous prepare() // considers that the current size is the requested size (to avoid a duplicate requestReset()) setSessionDisplaySize(size); - requestReset(); + invalidate(); } else if (Ln.isEnabled(Ln.Level.VERBOSE)) { Ln.v("ScreenCapture: Size not changed (" + size + "): do not requestReset()"); } @@ -246,7 +246,7 @@ public class ScreenCapture extends SurfaceCapture { if (Ln.isEnabled(Ln.Level.VERBOSE)) { Ln.v("ScreenCapture: onRotationChanged(" + rotation + ")"); } - requestReset(); + invalidate(); } }; ServiceManager.getWindowManager().registerRotationWatcher(rotationWatcher, displayId); @@ -272,7 +272,7 @@ public class ScreenCapture extends SurfaceCapture { // Ignore events related to other display ids return; } - requestReset(); + invalidate(); } }; ServiceManager.getWindowManager().registerDisplayFoldListener(displayFoldListener); diff --git a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java index 0ee93c92..172bd78f 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceCapture.java @@ -6,36 +6,37 @@ import com.genymobile.scrcpy.device.Size; import android.view.Surface; import java.io.IOException; -import java.util.concurrent.atomic.AtomicBoolean; /** * A video source which can be rendered on a Surface for encoding. */ public abstract class SurfaceCapture { - private final AtomicBoolean resetCapture = new AtomicBoolean(); - - /** - * Request the encoding session to be restarted, for example if the capture implementation detects that the video source size has changed (on - * device rotation for example). - */ - protected void requestReset() { - resetCapture.set(true); + public interface CaptureListener { + void onInvalidated(); } + private CaptureListener listener; + /** - * Consume the reset request (intended to be called by the encoder). - * - * @return {@code true} if a reset request was pending, {@code false} otherwise. + * Notify the listener that the capture has been invalidated (for example, because its size changed). */ - public boolean consumeReset() { - return resetCapture.getAndSet(false); + protected void invalidate() { + listener.onInvalidated(); } /** * Called once before the first capture starts. */ - public abstract void init() throws ConfigurationException, IOException; + public final void init(CaptureListener listener) throws ConfigurationException, IOException { + this.listener = listener; + init(); + } + + /** + * Called once before the first capture starts. + */ + protected abstract void init() throws ConfigurationException, IOException; /** * Called after the last capture ends (if and only if {@link #init()} has been called). diff --git a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java index 6a58d791..3a1c481e 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -49,6 +49,8 @@ public class SurfaceEncoder implements AsyncProcessor { private Thread thread; private final AtomicBoolean stopped = new AtomicBoolean(); + private final CaptureReset reset = new CaptureReset(); + public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, int videoBitRate, float maxFps, List codecOptions, String encoderName, boolean downsizeOnError) { this.capture = capture; @@ -65,14 +67,14 @@ public class SurfaceEncoder implements AsyncProcessor { MediaCodec mediaCodec = createMediaCodec(codec, encoderName); MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions); - capture.init(); + capture.init(reset); try { boolean alive; boolean headerWritten = false; do { - capture.consumeReset(); // If a capture reset was requested, it is implicitly fulfilled + reset.consumeReset(); // If a capture reset was requested, it is implicitly fulfilled capture.prepare(); Size size = capture.getSize(); if (!headerWritten) { @@ -168,14 +170,14 @@ public class SurfaceEncoder implements AsyncProcessor { boolean alive = true; MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); - while (!capture.consumeReset() && !eof) { + while (!reset.consumeReset() && !eof) { if (stopped.get()) { alive = false; break; } int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1); try { - if (capture.consumeReset()) { + if (reset.consumeReset()) { // must restart encoding with new size break; }