mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-12-17 05:24:19 +01:00
Simplify camera startup code
Avoid multiple back-and-forths between the caller thread and the camera thread.
This commit is contained in:
@@ -36,6 +36,7 @@ import android.view.Surface;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
@@ -78,6 +79,9 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
|
|
||||||
private final AtomicBoolean disconnected = new AtomicBoolean();
|
private final AtomicBoolean disconnected = new AtomicBoolean();
|
||||||
|
|
||||||
|
// Must be accessed only from the camera thread
|
||||||
|
private boolean started;
|
||||||
|
|
||||||
public CameraCapture(Options options) {
|
public CameraCapture(Options options) {
|
||||||
this.explicitCameraId = options.getCameraId();
|
this.explicitCameraId = options.getCameraId();
|
||||||
this.cameraFacing = options.getCameraFacing();
|
this.cameraFacing = options.getCameraFacing();
|
||||||
@@ -250,6 +254,7 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
return ratio.getAspectRatio();
|
return ratio.getAspectRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(AndroidVersions.API_28_ANDROID_9)
|
||||||
@Override
|
@Override
|
||||||
public void start(Surface surface) throws IOException {
|
public void start(Surface surface) throws IOException {
|
||||||
if (transform != null) {
|
if (transform != null) {
|
||||||
@@ -261,11 +266,50 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
surface = glRunner.start(captureSize, videoSize, surface);
|
surface = glRunner.start(captureSize, videoSize, surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cameraHandler.post(() -> {
|
||||||
|
assertCameraThread();
|
||||||
|
started = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
Surface captureSurface = surface;
|
||||||
|
OutputConfiguration outputConfig = new OutputConfiguration(captureSurface);
|
||||||
|
List<OutputConfiguration> outputs = Collections.singletonList(outputConfig);
|
||||||
|
int sessionType = highSpeed ? SessionConfiguration.SESSION_HIGH_SPEED : SessionConfiguration.SESSION_REGULAR;
|
||||||
|
SessionConfiguration sessionConfig = new SessionConfiguration(sessionType, outputs, cameraExecutor, new CameraCaptureSession.StateCallback() {
|
||||||
|
@Override
|
||||||
|
public void onConfigured(CameraCaptureSession session) {
|
||||||
|
assertCameraThread();
|
||||||
|
if (!started) {
|
||||||
|
// Stopped on the encoder thread between the call to start() and this callback
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||||
|
requestBuilder.addTarget(captureSurface);
|
||||||
|
|
||||||
|
if (fps > 0) {
|
||||||
|
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(fps, fps));
|
||||||
|
}
|
||||||
|
|
||||||
|
CaptureRequest request = requestBuilder.build();
|
||||||
|
setRepeatingRequest(session, request);
|
||||||
|
} catch (CameraAccessException e) {
|
||||||
|
Ln.e("Camera error", e);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigureFailed(CameraCaptureSession session) {
|
||||||
|
Ln.e("Camera configuration error");
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CameraCaptureSession session = createCaptureSession(cameraDevice, surface);
|
cameraDevice.createCaptureSession(sessionConfig);
|
||||||
CaptureRequest request = createCaptureRequest(surface);
|
} catch (CameraAccessException e) {
|
||||||
setRepeatingRequest(session, request);
|
|
||||||
} catch (CameraAccessException | InterruptedException e) {
|
|
||||||
stop();
|
stop();
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
@@ -273,6 +317,11 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
cameraHandler.post(() -> {
|
||||||
|
assertCameraThread();
|
||||||
|
started = false;
|
||||||
|
});
|
||||||
|
|
||||||
if (glRunner != null) {
|
if (glRunner != null) {
|
||||||
glRunner.stopAndRelease();
|
glRunner.stopAndRelease();
|
||||||
glRunner = null;
|
glRunner = null;
|
||||||
@@ -353,46 +402,7 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(AndroidVersions.API_31_ANDROID_12)
|
@TargetApi(AndroidVersions.API_31_ANDROID_12)
|
||||||
private CameraCaptureSession createCaptureSession(CameraDevice camera, Surface surface) throws CameraAccessException, InterruptedException {
|
private void setRepeatingRequest(CameraCaptureSession session, CaptureRequest request) throws CameraAccessException {
|
||||||
CompletableFuture<CameraCaptureSession> future = new CompletableFuture<>();
|
|
||||||
OutputConfiguration outputConfig = new OutputConfiguration(surface);
|
|
||||||
List<OutputConfiguration> outputs = Arrays.asList(outputConfig);
|
|
||||||
|
|
||||||
int sessionType = highSpeed ? SessionConfiguration.SESSION_HIGH_SPEED : SessionConfiguration.SESSION_REGULAR;
|
|
||||||
SessionConfiguration sessionConfig = new SessionConfiguration(sessionType, outputs, cameraExecutor, new CameraCaptureSession.StateCallback() {
|
|
||||||
@Override
|
|
||||||
public void onConfigured(CameraCaptureSession session) {
|
|
||||||
future.complete(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigureFailed(CameraCaptureSession session) {
|
|
||||||
future.completeExceptionally(new CameraAccessException(CameraAccessException.CAMERA_ERROR));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
camera.createCaptureSession(sessionConfig);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return future.get();
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw (CameraAccessException) e.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private CaptureRequest createCaptureRequest(Surface surface) throws CameraAccessException {
|
|
||||||
CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
|
||||||
requestBuilder.addTarget(surface);
|
|
||||||
|
|
||||||
if (fps > 0) {
|
|
||||||
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(fps, fps));
|
|
||||||
}
|
|
||||||
|
|
||||||
return requestBuilder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(AndroidVersions.API_31_ANDROID_12)
|
|
||||||
private void setRepeatingRequest(CameraCaptureSession session, CaptureRequest request) throws CameraAccessException, InterruptedException {
|
|
||||||
CameraCaptureSession.CaptureCallback callback = new CameraCaptureSession.CaptureCallback() {
|
CameraCaptureSession.CaptureCallback callback = new CameraCaptureSession.CaptureCallback() {
|
||||||
@Override
|
@Override
|
||||||
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {
|
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp, long frameNumber) {
|
||||||
@@ -418,4 +428,8 @@ public class CameraCapture extends SurfaceCapture {
|
|||||||
public boolean isClosed() {
|
public boolean isClosed() {
|
||||||
return disconnected.get();
|
return disconnected.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void assertCameraThread() {
|
||||||
|
assert Thread.currentThread() == cameraThread;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user