diff --git a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java index c5811f11..9bc3f195 100644 --- a/server/src/main/java/com/genymobile/scrcpy/control/Controller.java +++ b/server/src/main/java/com/genymobile/scrcpy/control/Controller.java @@ -13,6 +13,7 @@ import com.genymobile.scrcpy.model.Size; import com.genymobile.scrcpy.util.Ln; import com.genymobile.scrcpy.util.LogUtils; import com.genymobile.scrcpy.video.CameraCapture; +import com.genymobile.scrcpy.video.CaptureControl; import com.genymobile.scrcpy.video.SurfaceCapture; import com.genymobile.scrcpy.video.VideoSource; import com.genymobile.scrcpy.video.VirtualDisplayListener; @@ -815,7 +816,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener { private void resetVideo() { if (surfaceCapture != null) { Ln.i("Video capture reset"); - surfaceCapture.getCaptureControl().reset(); + surfaceCapture.getCaptureControl().reset(CaptureControl.RESET_REASON_CLIENT_RESET); } } } 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 0e8799f6..a3d43160 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/CameraCapture.java @@ -323,7 +323,7 @@ public class CameraCapture extends SurfaceCapture { } catch (CameraAccessException e) { Ln.e("Camera error", e); disconnected.set(true); - getCaptureControl().reset(); + getCaptureControl().reset(CaptureControl.RESET_REASON_TERMINATE); } } @@ -331,7 +331,7 @@ public class CameraCapture extends SurfaceCapture { public void onConfigureFailed(CameraCaptureSession session) { Ln.e("Camera configuration error"); disconnected.set(true); - getCaptureControl().reset(); + getCaptureControl().reset(CaptureControl.RESET_REASON_TERMINATE); } }); @@ -388,7 +388,7 @@ public class CameraCapture extends SurfaceCapture { public void onDisconnected(CameraDevice camera) { Ln.w("Camera disconnected"); disconnected.set(true); - getCaptureControl().reset(); + getCaptureControl().reset(CaptureControl.RESET_REASON_TERMINATE); } @Override diff --git a/server/src/main/java/com/genymobile/scrcpy/video/CaptureControl.java b/server/src/main/java/com/genymobile/scrcpy/video/CaptureControl.java index 001f297d..90760719 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/CaptureControl.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/CaptureControl.java @@ -2,21 +2,30 @@ package com.genymobile.scrcpy.video; import android.media.MediaCodec; -import java.util.concurrent.atomic.AtomicBoolean; - public class CaptureControl { - private final AtomicBoolean reset = new AtomicBoolean(); + public static final int RESET_REASON_TERMINATE = 1; + public static final int RESET_REASON_DISPLAY_PROPERTIES_CHANGED = 1 << 1; + public static final int RESET_REASON_CLIENT_RESET = 1 << 2; + + private int reset = 0; // Current instance of MediaCodec to "interrupt" on reset private MediaCodec runningMediaCodec; - public boolean consumeReset() { - return reset.getAndSet(false); + public synchronized boolean isResetRequested() { + return reset != 0; } - public synchronized void reset() { - reset.set(true); + public synchronized int consumeReset() { + int value = reset; + reset = 0; + return value; + } + + public synchronized void reset(int reason) { + assert reason != 0; + reset |= reason; if (runningMediaCodec != null) { try { runningMediaCodec.signalEndOfInputStream(); 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 c6ab10a7..e1fe0315 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java @@ -201,7 +201,7 @@ public class NewDisplayCapture extends SurfaceCapture { ServiceManager.getWindowManager().setDisplayImePolicy(virtualDisplayId, displayImePolicy); } - displayMonitor.start(virtualDisplayId, () -> getCaptureControl().reset()); + displayMonitor.start(virtualDisplayId, () -> getCaptureControl().reset(CaptureControl.RESET_REASON_DISPLAY_PROPERTIES_CHANGED)); } catch (Exception e) { Ln.e("Could not create display", e); throw new AssertionError("Could not create display"); 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 239402a3..5f28f868 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java @@ -61,7 +61,7 @@ public class ScreenCapture extends SurfaceCapture { @Override public void init() { - displayMonitor.start(displayId, () -> getCaptureControl().reset()); + displayMonitor.start(displayId, () -> getCaptureControl().reset(CaptureControl.RESET_REASON_DISPLAY_PROPERTIES_CHANGED)); } @Override 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 5c56ed3a..8a0c9b38 100644 --- a/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/video/SurfaceEncoder.java @@ -97,7 +97,10 @@ public class SurfaceEncoder implements AsyncProcessor { streamer.writeVideoHeader(); do { - captureControl.consumeReset(); // If a capture reset was requested, it is implicitly fulfilled + int resetReasons = captureControl.consumeReset(); + if ((resetReasons & CaptureControl.RESET_REASON_TERMINATE) != 0) { + break; + } capture.prepare(); Size size = capture.getSize(); @@ -123,12 +126,12 @@ public class SurfaceEncoder implements AsyncProcessor { if (stopped.get()) { alive = false; } else { - boolean resetRequested = captureControl.consumeReset(); - if (!resetRequested) { + if (!captureControl.isResetRequested()) { // If a reset is requested during encode(), it will interrupt the encoding by an EOS streamer.writeSessionMeta(size.getWidth(), size.getHeight()); encode(mediaCodec, streamer); } + // The capture might have been closed internally (for example if the camera is disconnected) alive = !stopped.get() && !capture.isClosed(); } @@ -275,7 +278,7 @@ public class SurfaceEncoder implements AsyncProcessor { public void stop() { if (thread != null) { stopped.set(true); - captureControl.reset(); + captureControl.reset(CaptureControl.RESET_REASON_TERMINATE); } }