mirror of
https://github.com/Genymobile/scrcpy
synced 2025-01-20 06:30:56 +00:00
Handle capture reset via listener
When the capture source becomes "invalid" (because the display size changes for example), a reset request is performed to restart the encoder. The reset state was stored in SurfaceCapture. The capture implementation set the flag, and the encoder consumed it. However, this mechanism did not allow a reset request to _interrupt_ the encoder, which may be waiting on a blocking call (until a new frame is produced). To be able to interrupt the encoder, a reset request must not only set a flag, but run a callback provided by the encoder. For that purpose, introduce the CaptureListener interface, which is notified by the SurfaceCapture implementation whenever the capture is invalidated. For now, the listener implementation just set a flag as before, so the behavior is unchanged. It lays the groundwork for the next commits. PR #5432 <https://github.com/Genymobile/scrcpy/pull/5432>
This commit is contained in:
parent
790ea5e58c
commit
69b836930a
@ -68,7 +68,7 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() throws IOException {
|
protected void init() throws IOException {
|
||||||
cameraThread = new HandlerThread("camera");
|
cameraThread = new HandlerThread("camera");
|
||||||
cameraThread.start();
|
cameraThread.start();
|
||||||
cameraHandler = new Handler(cameraThread.getLooper());
|
cameraHandler = new Handler(cameraThread.getLooper());
|
||||||
@ -256,7 +256,7 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
public void onDisconnected(CameraDevice camera) {
|
public void onDisconnected(CameraDevice camera) {
|
||||||
Ln.w("Camera disconnected");
|
Ln.w("Camera disconnected");
|
||||||
disconnected.set(true);
|
disconnected.set(true);
|
||||||
requestReset();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -46,7 +46,7 @@ public class NewDisplayCapture extends SurfaceCapture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init() {
|
protected void init() {
|
||||||
size = newDisplay.getSize();
|
size = newDisplay.getSize();
|
||||||
dpi = newDisplay.getDpi();
|
dpi = newDisplay.getDpi();
|
||||||
if (size == null || dpi == 0) {
|
if (size == null || dpi == 0) {
|
||||||
|
@ -85,7 +85,7 @@ public class ScreenCapture extends SurfaceCapture {
|
|||||||
Ln.v("ScreenCapture: requestReset(): " + getSessionDisplaySize() + " -> (unknown)");
|
Ln.v("ScreenCapture: requestReset(): " + getSessionDisplaySize() + " -> (unknown)");
|
||||||
}
|
}
|
||||||
setSessionDisplaySize(null);
|
setSessionDisplaySize(null);
|
||||||
requestReset();
|
invalidate();
|
||||||
} else {
|
} else {
|
||||||
Size size = di.getSize();
|
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()
|
// 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())
|
// considers that the current size is the requested size (to avoid a duplicate requestReset())
|
||||||
setSessionDisplaySize(size);
|
setSessionDisplaySize(size);
|
||||||
requestReset();
|
invalidate();
|
||||||
} else if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
} else if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||||
Ln.v("ScreenCapture: Size not changed (" + size + "): do not requestReset()");
|
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)) {
|
if (Ln.isEnabled(Ln.Level.VERBOSE)) {
|
||||||
Ln.v("ScreenCapture: onRotationChanged(" + rotation + ")");
|
Ln.v("ScreenCapture: onRotationChanged(" + rotation + ")");
|
||||||
}
|
}
|
||||||
requestReset();
|
invalidate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ServiceManager.getWindowManager().registerRotationWatcher(rotationWatcher, displayId);
|
ServiceManager.getWindowManager().registerRotationWatcher(rotationWatcher, displayId);
|
||||||
@ -272,7 +272,7 @@ public class ScreenCapture extends SurfaceCapture {
|
|||||||
// Ignore events related to other display ids
|
// Ignore events related to other display ids
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
requestReset();
|
invalidate();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ServiceManager.getWindowManager().registerDisplayFoldListener(displayFoldListener);
|
ServiceManager.getWindowManager().registerDisplayFoldListener(displayFoldListener);
|
||||||
|
@ -6,36 +6,37 @@ import com.genymobile.scrcpy.device.Size;
|
|||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A video source which can be rendered on a Surface for encoding.
|
* A video source which can be rendered on a Surface for encoding.
|
||||||
*/
|
*/
|
||||||
public abstract class SurfaceCapture {
|
public abstract class SurfaceCapture {
|
||||||
|
|
||||||
private final AtomicBoolean resetCapture = new AtomicBoolean();
|
public interface CaptureListener {
|
||||||
|
void onInvalidated();
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CaptureListener listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consume the reset request (intended to be called by the encoder).
|
* Notify the listener that the capture has been invalidated (for example, because its size changed).
|
||||||
*
|
|
||||||
* @return {@code true} if a reset request was pending, {@code false} otherwise.
|
|
||||||
*/
|
*/
|
||||||
public boolean consumeReset() {
|
protected void invalidate() {
|
||||||
return resetCapture.getAndSet(false);
|
listener.onInvalidated();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called once before the first capture starts.
|
* 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).
|
* Called after the last capture ends (if and only if {@link #init()} has been called).
|
||||||
|
@ -49,6 +49,8 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
private Thread thread;
|
private Thread thread;
|
||||||
private final AtomicBoolean stopped = new AtomicBoolean();
|
private final AtomicBoolean stopped = new AtomicBoolean();
|
||||||
|
|
||||||
|
private final CaptureReset reset = new CaptureReset();
|
||||||
|
|
||||||
public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, int videoBitRate, float maxFps, List<CodecOption> codecOptions,
|
public SurfaceEncoder(SurfaceCapture capture, Streamer streamer, int videoBitRate, float maxFps, List<CodecOption> codecOptions,
|
||||||
String encoderName, boolean downsizeOnError) {
|
String encoderName, boolean downsizeOnError) {
|
||||||
this.capture = capture;
|
this.capture = capture;
|
||||||
@ -65,14 +67,14 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
MediaCodec mediaCodec = createMediaCodec(codec, encoderName);
|
MediaCodec mediaCodec = createMediaCodec(codec, encoderName);
|
||||||
MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions);
|
MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions);
|
||||||
|
|
||||||
capture.init();
|
capture.init(reset);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean alive;
|
boolean alive;
|
||||||
boolean headerWritten = false;
|
boolean headerWritten = false;
|
||||||
|
|
||||||
do {
|
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();
|
capture.prepare();
|
||||||
Size size = capture.getSize();
|
Size size = capture.getSize();
|
||||||
if (!headerWritten) {
|
if (!headerWritten) {
|
||||||
@ -168,14 +170,14 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
boolean alive = true;
|
boolean alive = true;
|
||||||
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
|
||||||
|
|
||||||
while (!capture.consumeReset() && !eof) {
|
while (!reset.consumeReset() && !eof) {
|
||||||
if (stopped.get()) {
|
if (stopped.get()) {
|
||||||
alive = false;
|
alive = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
int outputBufferId = codec.dequeueOutputBuffer(bufferInfo, -1);
|
||||||
try {
|
try {
|
||||||
if (capture.consumeReset()) {
|
if (reset.consumeReset()) {
|
||||||
// must restart encoding with new size
|
// must restart encoding with new size
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user