mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-10 01:59:04 +00:00
torch-wip
There is no input manager in camera mode (so the control msg is never sent), so it does not work. We need a specific access to the controller in camera mode.
This commit is contained in:
parent
5a9e8ec7ba
commit
e68e865d65
8 changed files with 99 additions and 6 deletions
|
@ -157,6 +157,9 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
|
|||
sc_write16be(&buf[3], msg->uhid_input.size);
|
||||
memcpy(&buf[5], msg->uhid_input.data, msg->uhid_input.size);
|
||||
return 5 + msg->uhid_input.size;
|
||||
case SC_CONTROL_MSG_TYPE_SET_CAMERA_TORCH:
|
||||
buf[1] = msg->set_camera_torch.enabled ? 1 : 0;
|
||||
return 2;
|
||||
case SC_CONTROL_MSG_TYPE_EXPAND_NOTIFICATION_PANEL:
|
||||
case SC_CONTROL_MSG_TYPE_EXPAND_SETTINGS_PANEL:
|
||||
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||
|
@ -274,6 +277,10 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
|
|||
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||
LOG_CMSG("open hard keyboard settings");
|
||||
break;
|
||||
case SC_CONTROL_MSG_TYPE_SET_CAMERA_TORCH:
|
||||
LOG_CMSG("%s camera torch",
|
||||
msg->set_camera_torch.enabled ? "enable" : "disable");
|
||||
break;
|
||||
default:
|
||||
LOG_CMSG("unknown type: %u", (unsigned) msg->type);
|
||||
break;
|
||||
|
|
|
@ -41,6 +41,7 @@ enum sc_control_msg_type {
|
|||
SC_CONTROL_MSG_TYPE_UHID_CREATE,
|
||||
SC_CONTROL_MSG_TYPE_UHID_INPUT,
|
||||
SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS,
|
||||
SC_CONTROL_MSG_TYPE_SET_CAMERA_TORCH,
|
||||
};
|
||||
|
||||
enum sc_screen_power_mode {
|
||||
|
@ -106,6 +107,9 @@ struct sc_control_msg {
|
|||
uint16_t size;
|
||||
uint8_t data[SC_HID_MAX_SIZE];
|
||||
} uhid_input;
|
||||
struct {
|
||||
bool enabled;
|
||||
} set_camera_torch;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -330,6 +330,19 @@ open_hard_keyboard_settings(struct sc_input_manager *im) {
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_camera_torch(struct sc_input_manager *im, bool enabled) {
|
||||
assert(im->controller);
|
||||
|
||||
struct sc_control_msg msg;
|
||||
msg.type = SC_CONTROL_MSG_TYPE_SET_CAMERA_TORCH;
|
||||
msg.set_camera_torch.enabled = enabled;
|
||||
|
||||
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||
LOGW("Could not request setting camera torch");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
apply_orientation_transform(struct sc_input_manager *im,
|
||||
enum sc_orientation transform) {
|
||||
|
@ -569,6 +582,11 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||
open_hard_keyboard_settings(im);
|
||||
}
|
||||
return;
|
||||
case SDLK_l:
|
||||
if (control && !repeat && down) {
|
||||
set_camera_torch(im, !shift);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
|
@ -53,6 +53,8 @@ public class CameraCapture extends SurfaceCapture {
|
|||
|
||||
private final AtomicBoolean disconnected = new AtomicBoolean();
|
||||
|
||||
private CameraCaptureSession currentSession;
|
||||
|
||||
public CameraCapture(String explicitCameraId, CameraFacing cameraFacing, Size explicitSize, int maxSize, CameraAspectRatio aspectRatio, int fps,
|
||||
boolean highSpeed) {
|
||||
this.explicitCameraId = explicitCameraId;
|
||||
|
@ -196,7 +198,22 @@ public class CameraCapture extends SurfaceCapture {
|
|||
public void start(Surface surface) throws IOException {
|
||||
try {
|
||||
CameraCaptureSession session = createCaptureSession(cameraDevice, surface);
|
||||
CaptureRequest request = createCaptureRequest(surface);
|
||||
CaptureRequest request = createCaptureRequest(surface, false);
|
||||
setRepeatingRequest(session, request);
|
||||
setCurrentSession(session);
|
||||
} catch (CameraAccessException | InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public void setTorchEnabled(boolean enabled) throws IOException {
|
||||
CameraCaptureSession session = getCurrentSession();
|
||||
if (session == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
CaptureRequest request = createCaptureRequest(session.getInputSurface(), enabled);
|
||||
setRepeatingRequest(session, request);
|
||||
} catch (CameraAccessException | InterruptedException e) {
|
||||
throw new IOException(e);
|
||||
|
@ -310,9 +327,12 @@ public class CameraCapture extends SurfaceCapture {
|
|||
}
|
||||
}
|
||||
|
||||
private CaptureRequest createCaptureRequest(Surface surface) throws CameraAccessException {
|
||||
private CaptureRequest createCaptureRequest(Surface surface, boolean torch) throws CameraAccessException {
|
||||
CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||
requestBuilder.addTarget(surface);
|
||||
if (torch) {
|
||||
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
|
||||
}
|
||||
|
||||
if (fps > 0) {
|
||||
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(fps, fps));
|
||||
|
@ -348,4 +368,12 @@ public class CameraCapture extends SurfaceCapture {
|
|||
public boolean isClosed() {
|
||||
return disconnected.get();
|
||||
}
|
||||
|
||||
private synchronized void setCurrentSession(CameraCaptureSession session) {
|
||||
currentSession = session;
|
||||
}
|
||||
|
||||
private synchronized CameraCaptureSession getCurrentSession() {
|
||||
return currentSession;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ public final class ControlMessage {
|
|||
public static final int TYPE_UHID_CREATE = 12;
|
||||
public static final int TYPE_UHID_INPUT = 13;
|
||||
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 14;
|
||||
public static final int TYPE_SET_CAMERA_TORCH = 15;
|
||||
|
||||
public static final long SEQUENCE_INVALID = 0;
|
||||
|
||||
|
@ -45,6 +46,7 @@ public final class ControlMessage {
|
|||
private long sequence;
|
||||
private int id;
|
||||
private byte[] data;
|
||||
private boolean enabled;
|
||||
|
||||
private ControlMessage() {
|
||||
}
|
||||
|
@ -144,6 +146,13 @@ public final class ControlMessage {
|
|||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createSetCameraTorch(boolean enabled) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_SET_CAMERA_TORCH;
|
||||
msg.enabled = enabled;
|
||||
return msg;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
@ -215,4 +224,8 @@ public final class ControlMessage {
|
|||
public byte[] getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ public class ControlMessageReader {
|
|||
static final int SET_CLIPBOARD_FIXED_PAYLOAD_LENGTH = 9;
|
||||
static final int UHID_CREATE_FIXED_PAYLOAD_LENGTH = 4;
|
||||
static final int UHID_INPUT_FIXED_PAYLOAD_LENGTH = 4;
|
||||
static final int CAMERA_TORCH_PAYLOAD_LENGTH = 1;
|
||||
|
||||
private static final int MESSAGE_MAX_SIZE = 1 << 18; // 256k
|
||||
|
||||
|
@ -95,6 +96,9 @@ public class ControlMessageReader {
|
|||
case ControlMessage.TYPE_UHID_INPUT:
|
||||
msg = parseUhidInput();
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_CAMERA_TORCH:
|
||||
msg = parseCameraTorch();
|
||||
break;
|
||||
default:
|
||||
Ln.w("Unknown event type: " + type);
|
||||
msg = null;
|
||||
|
@ -245,6 +249,14 @@ public class ControlMessageReader {
|
|||
return ControlMessage.createUhidInput(id, data);
|
||||
}
|
||||
|
||||
private ControlMessage parseCameraTorch() {
|
||||
if (buffer.remaining() < CAMERA_TORCH_PAYLOAD_LENGTH) {
|
||||
return null;
|
||||
}
|
||||
boolean enabled = buffer.get() != 0;
|
||||
return ControlMessage.createSetCameraTorch(enabled);
|
||||
}
|
||||
|
||||
private static Position readPosition(ByteBuffer buffer) {
|
||||
int x = buffer.getInt();
|
||||
int y = buffer.getInt();
|
||||
|
|
|
@ -32,6 +32,7 @@ public class Controller implements AsyncProcessor {
|
|||
|
||||
private final Device device;
|
||||
private final ControlChannel controlChannel;
|
||||
private final CameraCapture cameraCapture;
|
||||
private final CleanUp cleanUp;
|
||||
private final DeviceMessageSender sender;
|
||||
private final boolean clipboardAutosync;
|
||||
|
@ -46,9 +47,11 @@ public class Controller implements AsyncProcessor {
|
|||
|
||||
private boolean keepPowerModeOff;
|
||||
|
||||
public Controller(Device device, ControlChannel controlChannel, CleanUp cleanUp, boolean clipboardAutosync, boolean powerOn) {
|
||||
public Controller(Device device, ControlChannel controlChannel, CameraCapture cameraCapture, CleanUp cleanUp, boolean clipboardAutosync,
|
||||
boolean powerOn) {
|
||||
this.device = device;
|
||||
this.controlChannel = controlChannel;
|
||||
this.cameraCapture = cameraCapture;
|
||||
this.cleanUp = cleanUp;
|
||||
this.clipboardAutosync = clipboardAutosync;
|
||||
this.powerOn = powerOn;
|
||||
|
@ -145,7 +148,7 @@ public class Controller implements AsyncProcessor {
|
|||
// this is expected on close
|
||||
return false;
|
||||
}
|
||||
|
||||
Ln.i("==== msg = " + msg.getType());
|
||||
switch (msg.getType()) {
|
||||
case ControlMessage.TYPE_INJECT_KEYCODE:
|
||||
if (device.supportsInputEvents()) {
|
||||
|
@ -213,6 +216,10 @@ public class Controller implements AsyncProcessor {
|
|||
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||
openHardKeyboardSettings();
|
||||
break;
|
||||
case ControlMessage.TYPE_SET_CAMERA_TORCH:
|
||||
Ln.i("===");
|
||||
cameraCapture.setTorchEnabled(msg.getEnabled());
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.genymobile.scrcpy;
|
|||
|
||||
import android.os.BatteryManager;
|
||||
import android.os.Build;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
@ -144,6 +145,7 @@ public final class Server {
|
|||
asyncProcessors.add(audioRecorder);
|
||||
}
|
||||
|
||||
CameraCapture cameraCapture = null;
|
||||
if (video) {
|
||||
Streamer videoStreamer = new Streamer(connection.getVideoFd(), options.getVideoCodec(), options.getSendCodecMeta(),
|
||||
options.getSendFrameMeta());
|
||||
|
@ -151,8 +153,9 @@ public final class Server {
|
|||
if (options.getVideoSource() == VideoSource.DISPLAY) {
|
||||
surfaceCapture = new ScreenCapture(device);
|
||||
} else {
|
||||
surfaceCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(),
|
||||
cameraCapture = new CameraCapture(options.getCameraId(), options.getCameraFacing(), options.getCameraSize(),
|
||||
options.getMaxSize(), options.getCameraAspectRatio(), options.getCameraFps(), options.getCameraHighSpeed());
|
||||
surfaceCapture = cameraCapture;
|
||||
}
|
||||
SurfaceEncoder surfaceEncoder = new SurfaceEncoder(surfaceCapture, videoStreamer, options.getVideoBitRate(), options.getMaxFps(),
|
||||
options.getVideoCodecOptions(), options.getVideoEncoder(), options.getDownsizeOnError());
|
||||
|
@ -161,7 +164,8 @@ public final class Server {
|
|||
|
||||
if (control) {
|
||||
ControlChannel controlChannel = connection.getControlChannel();
|
||||
Controller controller = new Controller(device, controlChannel, cleanUp, options.getClipboardAutosync(), options.getPowerOn());
|
||||
Controller controller = new Controller(
|
||||
device, controlChannel, cameraCapture, cleanUp, options.getClipboardAutosync(), options.getPowerOn());
|
||||
device.setClipboardListener(text -> {
|
||||
DeviceMessage msg = DeviceMessage.createClipboard(text);
|
||||
controller.getSender().send(msg);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue