Compare commits

..

9 Commits

Author SHA1 Message Date
Simon Chan
e3942b9a78 Fail-fast camera mirroring on Android 11 and older
Co-authored-by: Romain Vimont <rom@rom1v.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2023-10-28 00:45:52 +02:00
Romain Vimont
c0e1a4152f Support camera size selection using -m/--camera-ar
In addition to --camera-size to specify an explicit size, make it
possible to select the camera size automatically, respecting the maximum
size (already used for display mirroring) and an aspect ratio.

For example, "scrcpy --video-source=camera" followed by:
 - (no additional arguments)
    : mirrors at the maximum size, any a-r
 - -m1920
    : only consider valid sizes having both dimensions not above 1920
 - --camera-ar=4:3
    : only consider valid sizes having an aspect ratio of 4:3 (+/- 10%)
 - -m2048 --camera-ar=1.6
    : only consider valid sizes having both dimensions not above 2048
      and an aspect ratio of 1.6 (+/- 10%)

Co-authored-by: Simon Chan <1330321+yume-chan@users.noreply.github.com>
2023-10-28 00:45:52 +02:00
Simon Chan
5834657dae Add --camera-facing
Add an option to select the camera by its lens facing (any, front, back
or external).

Co-authored-by: Romain Vimont <rom@rom1v.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2023-10-28 00:10:15 +02:00
Romain Vimont
5f30a68297 Make camera id optional
If no camera id is provided, use the first camera available.
2023-10-28 00:10:15 +02:00
Romain Vimont
8735b388da Handle camera disconnection
Stop mirroring on camera disconnection.
2023-10-28 00:09:51 +02:00
Romain Vimont
8e9fa773c5 Automatically select audio source
If --audio-source is not specified, select the default value
according to the video source:
 - for display mirroring, use device audio by default;
 - for camera mirroring, use microphone by default.
2023-10-28 00:08:27 +02:00
Simon Chan
75e58b3101 Add camera mirroring
Add --video-source=camera, and related options:
 - --camera-id=ID: select the camera (ids are listed by --list-cameras);
 - --camera-size=WIDTHxHEIGHT: select the capture size.

Co-authored-by: Romain Vimont <rom@rom1v.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2023-10-28 00:08:25 +02:00
Romain Vimont
548858bfdb Add --list-camera-sizes 2023-10-28 00:07:25 +02:00
Simon Chan
5ed63314a5 Add --list-cameras
Add an option to list the device cameras.

Co-authored-by: Romain Vimont <rom@rom1v.com>
Signed-off-by: Romain Vimont <rom@rom1v.com>
2023-10-28 00:06:15 +02:00
8 changed files with 39 additions and 16 deletions

View File

@@ -458,6 +458,8 @@ The available encoders can be listed by \-\-list\-encoders.
.BI "\-\-video\-source " source
Select the video source (display or camera).
Camera mirroring requires Android 12+.
Default is display.
.TP

View File

@@ -743,6 +743,7 @@ static const struct sc_option options[] = {
.longopt = "video-source",
.argdesc = "source",
.text = "Select the video source (display or camera).\n"
"Camera mirroring requires Android 12+.\n"
"Default is display.",
},
{

View File

@@ -1,6 +1,6 @@
package com.genymobile.scrcpy;
public class CameraAspectRatio {
public final class CameraAspectRatio {
private static final float SENSOR = -1;
private float ar;

View File

@@ -135,8 +135,31 @@ public class CameraCapture extends SurfaceCapture {
});
}
Optional<android.util.Size> selected = stream.min(
Comparator.comparing(android.util.Size::getWidth).thenComparing(android.util.Size::getHeight).reversed());
Optional<android.util.Size> selected = stream.max((s1, s2) -> {
// Greater width is better
int cmp = Integer.compare(s1.getWidth(), s2.getWidth());
if (cmp != 0) {
return cmp;
}
if (targetAspectRatio != null) {
// Closer to the target aspect ratio is better
float ar1 = ((float) s1.getWidth() / s1.getHeight());
float arRatio1 = ar1 / targetAspectRatio;
float distance1 = Math.abs(1 - arRatio1);
float ar2 = ((float) s2.getWidth() / s2.getHeight());
float arRatio2 = ar2 / targetAspectRatio;
float distance2 = Math.abs(1 - arRatio2);
// Reverse the order because lower distance is better
return Float.compare(distance2, distance1);
}
// Greater height is better
return Integer.compare(s1.getHeight(), s2.getHeight());
});
if (selected.isPresent()) {
android.util.Size size = selected.get();
return new Size(size.getWidth(), size.getHeight());
@@ -206,13 +229,11 @@ public class CameraCapture extends SurfaceCapture {
@SuppressLint("MissingPermission")
@TargetApi(Build.VERSION_CODES.S)
private CameraDevice openCamera(String id) throws CameraAccessException, InterruptedException {
Ln.v("Open Camera: " + id);
CompletableFuture<CameraDevice> future = new CompletableFuture<>();
ServiceManager.getCameraManager().openCamera(id, new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
Ln.v("Open Camera Success");
Ln.d("Camera opened successfully");
future.complete(camera);
}
@@ -255,17 +276,13 @@ public class CameraCapture extends SurfaceCapture {
@TargetApi(Build.VERSION_CODES.S)
private CameraCaptureSession createCaptureSession(CameraDevice camera, Surface surface) throws CameraAccessException, InterruptedException {
Ln.d("Create Capture Session");
CompletableFuture<CameraCaptureSession> future = new CompletableFuture<>();
// replace by createCaptureSession(SessionConfiguration)
OutputConfiguration outputConfig = new OutputConfiguration(surface);
List<OutputConfiguration> outputs = Arrays.asList(outputConfig);
SessionConfiguration sessionConfig = new SessionConfiguration(SessionConfiguration.SESSION_REGULAR, outputs, cameraExecutor,
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
Ln.d("Create Capture Session Success");
future.complete(session);
}
@@ -310,4 +327,4 @@ public class CameraCapture extends SurfaceCapture {
public boolean isClosed() {
return disconnected.get();
}
}
}

View File

@@ -2,7 +2,6 @@ package com.genymobile.scrcpy;
import android.os.BatteryManager;
import android.os.Build;
import android.os.Looper;
import java.io.IOException;
import java.util.ArrayList;
@@ -89,6 +88,12 @@ public final class Server {
private static void scrcpy(Options options) throws IOException, ConfigurationException {
Ln.i("Device: [" + Build.MANUFACTURER + "] " + Build.BRAND + " " + Build.MODEL + " (Android " + Build.VERSION.RELEASE + ")");
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S && options.getVideoSource() == VideoSource.CAMERA) {
Ln.e("Camera mirroring is not supported before Android 12");
throw new ConfigurationException("Camera mirroring is not supported");
}
final Device device = new Device(options);
Thread initThread = startInitThread(options);

View File

@@ -1,7 +1,5 @@
package com.genymobile.scrcpy;
import android.media.MediaRecorder;
public enum VideoSource {
DISPLAY("display"),
CAMERA("camera");

View File

@@ -32,7 +32,7 @@ public final class Workarounds {
Workarounds.prepareMainLooper();
boolean mustFillAppInfo = false;
boolean mustFillBaseContext = true;
boolean mustFillBaseContext = false;
boolean mustFillAppContext = false;
if (Build.BRAND.equalsIgnoreCase("meizu")) {
@@ -66,6 +66,7 @@ public final class Workarounds {
if (camera) {
mustFillAppInfo = true;
mustFillBaseContext = true;
}
if (mustFillAppInfo) {

View File

@@ -1,7 +1,6 @@
package com.genymobile.scrcpy.wrappers;
import com.genymobile.scrcpy.FakeContext;
import com.genymobile.scrcpy.Workarounds;
import android.annotation.SuppressLint;
import android.content.Context;