mirror of
https://github.com/Genymobile/scrcpy.git
synced 2026-01-14 03:04:29 +01:00
Add shortcuts to switch the camera torch
MOD+l turns on the camera torch, MOD+Shift+l turns it off. TODO ref 6243 Co-authored-by: Tommie <teh420@gmail.com>
This commit is contained in:
@@ -25,6 +25,7 @@ public final class ControlMessage {
|
||||
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15;
|
||||
public static final int TYPE_START_APP = 16;
|
||||
public static final int TYPE_RESET_VIDEO = 17;
|
||||
public static final int TYPE_CAMERA_SET_TORCH = 18;
|
||||
|
||||
public static final long SEQUENCE_INVALID = 0;
|
||||
|
||||
@@ -166,6 +167,13 @@ public final class ControlMessage {
|
||||
return msg;
|
||||
}
|
||||
|
||||
public static ControlMessage createCameraSetTorch(boolean on) {
|
||||
ControlMessage msg = new ControlMessage();
|
||||
msg.type = TYPE_CAMERA_SET_TORCH;
|
||||
msg.on = on;
|
||||
return msg;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ public class ControlMessageReader {
|
||||
return parseUhidDestroy();
|
||||
case ControlMessage.TYPE_START_APP:
|
||||
return parseStartApp();
|
||||
case ControlMessage.TYPE_CAMERA_SET_TORCH:
|
||||
return parseCameraSetTorch();
|
||||
default:
|
||||
throw new ControlProtocolException("Unknown event type: " + type);
|
||||
}
|
||||
@@ -166,6 +168,11 @@ public class ControlMessageReader {
|
||||
return ControlMessage.createStartApp(name);
|
||||
}
|
||||
|
||||
private ControlMessage parseCameraSetTorch() throws IOException {
|
||||
boolean on = dis.readBoolean();
|
||||
return ControlMessage.createCameraSetTorch(on);
|
||||
}
|
||||
|
||||
private Position parsePosition() throws IOException {
|
||||
int x = dis.readInt();
|
||||
int y = dis.readInt();
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.genymobile.scrcpy.device.Position;
|
||||
import com.genymobile.scrcpy.device.Size;
|
||||
import com.genymobile.scrcpy.util.Ln;
|
||||
import com.genymobile.scrcpy.util.LogUtils;
|
||||
import com.genymobile.scrcpy.video.CameraCapture;
|
||||
import com.genymobile.scrcpy.video.SurfaceCapture;
|
||||
import com.genymobile.scrcpy.video.VideoSource;
|
||||
import com.genymobile.scrcpy.video.VirtualDisplayListener;
|
||||
@@ -99,7 +100,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
||||
|
||||
private boolean keepDisplayPowerOff;
|
||||
|
||||
// Used for resetting video encoding on RESET_VIDEO message
|
||||
// Used for resetting video encoding on RESET_VIDEO message or for sending camera controls
|
||||
private SurfaceCapture surfaceCapture;
|
||||
|
||||
public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) {
|
||||
@@ -365,6 +366,16 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
||||
default:
|
||||
// fall through
|
||||
}
|
||||
} else {
|
||||
switch (type) {
|
||||
case ControlMessage.TYPE_CAMERA_SET_TORCH:
|
||||
assert surfaceCapture instanceof CameraCapture;
|
||||
CameraCapture cameraCapture = (CameraCapture) surfaceCapture;
|
||||
cameraCapture.setTorchEnabled(msg.getOn());
|
||||
return true;
|
||||
default:
|
||||
// fall through
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError("Unexpected message type: " + type);
|
||||
|
||||
@@ -80,8 +80,10 @@ public class CameraCapture extends SurfaceCapture {
|
||||
|
||||
private final AtomicBoolean disconnected = new AtomicBoolean();
|
||||
|
||||
// Must be accessed only from the camera thread
|
||||
// The following fields must be accessed only from the camera thread
|
||||
private boolean started;
|
||||
private CaptureRequest.Builder requestBuilder;
|
||||
private CameraCaptureSession currentSession;
|
||||
|
||||
public CameraCapture(Options options) {
|
||||
this.explicitCameraId = options.getCameraId();
|
||||
@@ -287,7 +289,7 @@ public class CameraCapture extends SurfaceCapture {
|
||||
}
|
||||
|
||||
try {
|
||||
CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||
requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||
requestBuilder.addTarget(captureSurface);
|
||||
|
||||
if (fps > 0) {
|
||||
@@ -299,6 +301,7 @@ public class CameraCapture extends SurfaceCapture {
|
||||
|
||||
CaptureRequest request = requestBuilder.build();
|
||||
setRepeatingRequest(session, request);
|
||||
currentSession = session;
|
||||
} catch (CameraAccessException e) {
|
||||
Ln.e("Camera error", e);
|
||||
invalidate();
|
||||
@@ -324,6 +327,8 @@ public class CameraCapture extends SurfaceCapture {
|
||||
public void stop() {
|
||||
cameraHandler.post(() -> {
|
||||
assertCameraThread();
|
||||
currentSession = null;
|
||||
requestBuilder = null;
|
||||
started = false;
|
||||
});
|
||||
|
||||
@@ -434,6 +439,21 @@ public class CameraCapture extends SurfaceCapture {
|
||||
return disconnected.get();
|
||||
}
|
||||
|
||||
public void setTorchEnabled(boolean enabled) {
|
||||
cameraHandler.post(() -> {
|
||||
assertCameraThread();
|
||||
if (currentSession != null && requestBuilder != null) {
|
||||
try {
|
||||
requestBuilder.set(CaptureRequest.FLASH_MODE, enabled ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
|
||||
CaptureRequest request = requestBuilder.build();
|
||||
setRepeatingRequest(currentSession, request);
|
||||
} catch (CameraAccessException e) {
|
||||
Ln.e("Camera error", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void assertCameraThread() {
|
||||
assert Thread.currentThread() == cameraThread;
|
||||
}
|
||||
|
||||
@@ -422,6 +422,24 @@ public class ControlMessageReaderTest {
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseCameraSetTorch() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
DataOutputStream dos = new DataOutputStream(bos);
|
||||
dos.writeByte(ControlMessage.TYPE_CAMERA_SET_TORCH);
|
||||
dos.writeBoolean(true);
|
||||
byte[] packet = bos.toByteArray();
|
||||
|
||||
ByteArrayInputStream bis = new ByteArrayInputStream(packet);
|
||||
ControlMessageReader reader = new ControlMessageReader(bis);
|
||||
|
||||
ControlMessage event = reader.read();
|
||||
Assert.assertEquals(ControlMessage.TYPE_CAMERA_SET_TORCH, event.getType());
|
||||
Assert.assertTrue(event.getOn());
|
||||
|
||||
Assert.assertEquals(-1, bis.read()); // EOS
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiEvents() throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
|
||||
Reference in New Issue
Block a user