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:
Romain Vimont 2024-02-27 21:22:39 +01:00
commit e68e865d65
8 changed files with 99 additions and 6 deletions

View file

@ -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;

View file

@ -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;
};
};

View file

@ -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;

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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
}

View file

@ -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);