mirror of https://github.com/Genymobile/scrcpy
Add --list-cameras
Add an option to list the device cameras. PR #4213 <https://github.com/Genymobile/scrcpy/pull/4213> Co-authored-by: Romain Vimont <rom@rom1v.com> Signed-off-by: Romain Vimont <rom@rom1v.com>
This commit is contained in:
parent
f085765e04
commit
cd63896d63
|
@ -23,6 +23,7 @@ _scrcpy() {
|
|||
--kill-adb-on-close
|
||||
-K --hid-keyboard
|
||||
--legacy-paste
|
||||
--list-cameras
|
||||
--list-displays
|
||||
--list-encoders
|
||||
--lock-video-orientation
|
||||
|
|
|
@ -30,6 +30,7 @@ arguments=(
|
|||
'--kill-adb-on-close[Kill adb when scrcpy terminates]'
|
||||
{-K,--hid-keyboard}'[Simulate a physical keyboard by using HID over AOAv2]'
|
||||
'--legacy-paste[Inject computer clipboard text as a sequence of key events on Ctrl+v]'
|
||||
'--list-cameras[List cameras available on the device]'
|
||||
'--list-displays[List displays available on the device]'
|
||||
'--list-encoders[List video and audio encoders available on the device]'
|
||||
'--lock-video-orientation=[Lock video orientation]:orientation:(unlocked initial 0 1 2 3)'
|
||||
|
|
|
@ -155,6 +155,10 @@ Inject computer clipboard text as a sequence of key events on Ctrl+v (like MOD+S
|
|||
|
||||
This is a workaround for some devices not behaving as expected when setting the device clipboard programmatically.
|
||||
|
||||
.TP
|
||||
.B \-\-list\-cameras
|
||||
List cameras available on the device.
|
||||
|
||||
.TP
|
||||
.B \-\-list\-encoders
|
||||
List video and audio encoders available on the device.
|
||||
|
|
|
@ -81,6 +81,7 @@ enum {
|
|||
OPT_KILL_ADB_ON_CLOSE,
|
||||
OPT_TIME_LIMIT,
|
||||
OPT_PAUSE_ON_EXIT,
|
||||
OPT_LIST_CAMERAS,
|
||||
};
|
||||
|
||||
struct sc_option {
|
||||
|
@ -320,6 +321,11 @@ static const struct sc_option options[] = {
|
|||
"This is a workaround for some devices not behaving as "
|
||||
"expected when setting the device clipboard programmatically.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_LIST_CAMERAS,
|
||||
.longopt = "list-cameras",
|
||||
.text = "List device cameras.",
|
||||
},
|
||||
{
|
||||
.longopt_id = OPT_LIST_DISPLAYS,
|
||||
.longopt = "list-displays",
|
||||
|
@ -1998,6 +2004,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||
case OPT_LIST_DISPLAYS:
|
||||
opts->list |= SC_OPTION_LIST_DISPLAYS;
|
||||
break;
|
||||
case OPT_LIST_CAMERAS:
|
||||
opts->list |= SC_OPTION_LIST_CAMERAS;
|
||||
break;
|
||||
case OPT_REQUIRE_AUDIO:
|
||||
opts->require_audio = true;
|
||||
break;
|
||||
|
|
|
@ -182,6 +182,7 @@ struct scrcpy_options {
|
|||
bool kill_adb_on_close;
|
||||
#define SC_OPTION_LIST_ENCODERS 0x1
|
||||
#define SC_OPTION_LIST_DISPLAYS 0x2
|
||||
#define SC_OPTION_LIST_CAMERAS 0x4
|
||||
uint8_t list;
|
||||
};
|
||||
|
||||
|
|
|
@ -317,6 +317,9 @@ execute_server(struct sc_server *server,
|
|||
if (params->list & SC_OPTION_LIST_DISPLAYS) {
|
||||
ADD_PARAM("list_displays=true");
|
||||
}
|
||||
if (params->list & SC_OPTION_LIST_CAMERAS) {
|
||||
ADD_PARAM("list_cameras=true");
|
||||
}
|
||||
|
||||
#undef ADD_PARAM
|
||||
|
||||
|
|
|
@ -3,6 +3,11 @@ package com.genymobile.scrcpy;
|
|||
import com.genymobile.scrcpy.wrappers.DisplayManager;
|
||||
import com.genymobile.scrcpy.wrappers.ServiceManager;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.camera2.CameraAccessException;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class LogUtils {
|
||||
|
@ -60,4 +65,42 @@ public final class LogUtils {
|
|||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private static String getCameraFacingName(int facing) {
|
||||
switch (facing) {
|
||||
case CameraCharacteristics.LENS_FACING_FRONT:
|
||||
return "front";
|
||||
case CameraCharacteristics.LENS_FACING_BACK:
|
||||
return "back";
|
||||
case CameraCharacteristics.LENS_FACING_EXTERNAL:
|
||||
return "external";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
public static String buildCameraListMessage() {
|
||||
StringBuilder builder = new StringBuilder("List of cameras:");
|
||||
CameraManager cameraManager = ServiceManager.getCameraManager();
|
||||
try {
|
||||
String[] cameraIds = cameraManager.getCameraIdList();
|
||||
if (cameraIds == null || cameraIds.length == 0) {
|
||||
builder.append("\n (none)");
|
||||
} else {
|
||||
for (String id : cameraIds) {
|
||||
builder.append("\n --video-source=camera --camera-id=").append(id);
|
||||
CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(id);
|
||||
|
||||
int facing = characteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
builder.append(" (").append(getCameraFacingName(facing)).append(", ");
|
||||
|
||||
Rect activeSize = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
|
||||
builder.append(activeSize.width()).append("x").append(activeSize.height()).append(')');
|
||||
}
|
||||
}
|
||||
} catch (CameraAccessException e) {
|
||||
builder.append("\n (access denied)");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public class Options {
|
|||
|
||||
private boolean listEncoders;
|
||||
private boolean listDisplays;
|
||||
private boolean listCameras;
|
||||
|
||||
// Options not used by the scrcpy client, but useful to use scrcpy-server directly
|
||||
private boolean sendDeviceMeta = true; // send device name and size
|
||||
|
@ -154,7 +155,7 @@ public class Options {
|
|||
}
|
||||
|
||||
public boolean getList() {
|
||||
return listEncoders || listDisplays;
|
||||
return listEncoders || listDisplays || listCameras;
|
||||
}
|
||||
|
||||
public boolean getListEncoders() {
|
||||
|
@ -165,6 +166,10 @@ public class Options {
|
|||
return listDisplays;
|
||||
}
|
||||
|
||||
public boolean getListCameras() {
|
||||
return listCameras;
|
||||
}
|
||||
|
||||
public boolean getSendDeviceMeta() {
|
||||
return sendDeviceMeta;
|
||||
}
|
||||
|
@ -312,6 +317,9 @@ public class Options {
|
|||
case "list_displays":
|
||||
options.listDisplays = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "list_cameras":
|
||||
options.listCameras = Boolean.parseBoolean(value);
|
||||
break;
|
||||
case "send_device_meta":
|
||||
options.sendDeviceMeta = Boolean.parseBoolean(value);
|
||||
break;
|
||||
|
|
|
@ -98,8 +98,9 @@ public final class Server {
|
|||
boolean video = options.getVideo();
|
||||
boolean audio = options.getAudio();
|
||||
boolean sendDummyByte = options.getSendDummyByte();
|
||||
boolean camera = false;
|
||||
|
||||
Workarounds.apply(audio);
|
||||
Workarounds.apply(audio, camera);
|
||||
|
||||
List<AsyncProcessor> asyncProcessors = new ArrayList<>();
|
||||
|
||||
|
@ -207,6 +208,10 @@ public final class Server {
|
|||
if (options.getListDisplays()) {
|
||||
Ln.i(LogUtils.buildDisplayListMessage());
|
||||
}
|
||||
if (options.getListCameras()) {
|
||||
Workarounds.apply(false, true);
|
||||
Ln.i(LogUtils.buildCameraListMessage());
|
||||
}
|
||||
// Just print the requested data, do not mirror
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,14 +28,13 @@ public final class Workarounds {
|
|||
// not instantiable
|
||||
}
|
||||
|
||||
public static void apply(boolean audio) {
|
||||
public static void apply(boolean audio, boolean camera) {
|
||||
Workarounds.prepareMainLooper();
|
||||
|
||||
boolean mustFillAppInfo = false;
|
||||
boolean mustFillBaseContext = false;
|
||||
boolean mustFillAppContext = false;
|
||||
|
||||
|
||||
if (Build.BRAND.equalsIgnoreCase("meizu")) {
|
||||
// Workarounds must be applied for Meizu phones:
|
||||
// - <https://github.com/Genymobile/scrcpy/issues/240>
|
||||
|
@ -65,6 +64,11 @@ public final class Workarounds {
|
|||
mustFillAppContext = true;
|
||||
}
|
||||
|
||||
if (camera) {
|
||||
mustFillAppInfo = true;
|
||||
mustFillBaseContext = true;
|
||||
}
|
||||
|
||||
if (mustFillAppInfo) {
|
||||
Workarounds.fillAppInfo();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
package com.genymobile.scrcpy.wrappers;
|
||||
|
||||
import com.genymobile.scrcpy.FakeContext;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.os.IBinder;
|
||||
import android.os.IInterface;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
|
@ -26,6 +31,7 @@ public final class ServiceManager {
|
|||
private static StatusBarManager statusBarManager;
|
||||
private static ClipboardManager clipboardManager;
|
||||
private static ActivityManager activityManager;
|
||||
private static CameraManager cameraManager;
|
||||
|
||||
private ServiceManager() {
|
||||
/* not instantiable */
|
||||
|
@ -129,4 +135,16 @@ public final class ServiceManager {
|
|||
|
||||
return activityManager;
|
||||
}
|
||||
|
||||
public static CameraManager getCameraManager() {
|
||||
if (cameraManager == null) {
|
||||
try {
|
||||
Constructor<CameraManager> ctor = CameraManager.class.getDeclaredConstructor(Context.class);
|
||||
cameraManager = ctor.newInstance(FakeContext.get());
|
||||
} catch (Exception e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
return cameraManager;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue