mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-10 10:08:52 +00:00
Merge da72a3978f
into f01231dff8
This commit is contained in:
commit
1d7780a40e
23 changed files with 453 additions and 9 deletions
|
@ -18,6 +18,8 @@ _scrcpy() {
|
||||||
--camera-fps=
|
--camera-fps=
|
||||||
--camera-high-speed
|
--camera-high-speed
|
||||||
--camera-size=
|
--camera-size=
|
||||||
|
--camera-zoom-step=
|
||||||
|
--camera-torch
|
||||||
--capture-orientation=
|
--capture-orientation=
|
||||||
--crop=
|
--crop=
|
||||||
-d --select-usb
|
-d --select-usb
|
||||||
|
@ -197,6 +199,8 @@ _scrcpy() {
|
||||||
|--camera-id \
|
|--camera-id \
|
||||||
|--camera-fps \
|
|--camera-fps \
|
||||||
|--camera-size \
|
|--camera-size \
|
||||||
|
|--camera-zoom-step \
|
||||||
|
|--camera-torch \
|
||||||
|--crop \
|
|--crop \
|
||||||
|--display-id \
|
|--display-id \
|
||||||
|--max-fps \
|
|--max-fps \
|
||||||
|
|
|
@ -25,6 +25,8 @@ arguments=(
|
||||||
'--camera-facing=[Select the device camera by its facing direction]:facing:(front back external)'
|
'--camera-facing=[Select the device camera by its facing direction]:facing:(front back external)'
|
||||||
'--camera-fps=[Specify the camera capture frame rate]'
|
'--camera-fps=[Specify the camera capture frame rate]'
|
||||||
'--camera-size=[Specify an explicit camera capture size]'
|
'--camera-size=[Specify an explicit camera capture size]'
|
||||||
|
'--camera-zoom-step=[Specify the camera zoom step value]'
|
||||||
|
'--camera-torch[Turns the torch on when starting the camera]'
|
||||||
'--capture-orientation=[Set the capture video orientation]:orientation:(0 90 180 270 flip0 flip90 flip180 flip270 @0 @90 @180 @270 @flip0 @flip90 @flip180 @flip270)'
|
'--capture-orientation=[Set the capture video orientation]:orientation:(0 90 180 270 flip0 flip90 flip180 flip270 @0 @90 @180 @270 @flip0 @flip90 @flip180 @flip270)'
|
||||||
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
|
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
|
||||||
{-d,--select-usb}'[Use USB device]'
|
{-d,--select-usb}'[Use USB device]'
|
||||||
|
|
|
@ -37,6 +37,7 @@ src = [
|
||||||
'src/hid/hid_gamepad.c',
|
'src/hid/hid_gamepad.c',
|
||||||
'src/hid/hid_keyboard.c',
|
'src/hid/hid_keyboard.c',
|
||||||
'src/hid/hid_mouse.c',
|
'src/hid/hid_mouse.c',
|
||||||
|
'src/im/camera.c',
|
||||||
'src/trait/frame_source.c',
|
'src/trait/frame_source.c',
|
||||||
'src/trait/packet_source.c',
|
'src/trait/packet_source.c',
|
||||||
'src/uhid/gamepad_uhid.c',
|
'src/uhid/gamepad_uhid.c',
|
||||||
|
|
10
app/scrcpy.1
10
app/scrcpy.1
|
@ -131,6 +131,16 @@ The available camera ids can be listed by \fB\-\-list\-cameras\fR.
|
||||||
.BI "\-\-camera\-size " width\fRx\fIheight
|
.BI "\-\-camera\-size " width\fRx\fIheight
|
||||||
Specify an explicit camera capture size.
|
Specify an explicit camera capture size.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI \-\-camera\-torch
|
||||||
|
Turns the torch on when starting the camera.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-camera\-zoom\-step " step
|
||||||
|
Specify the camera zoom step value.
|
||||||
|
|
||||||
|
Default is 0.025.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-capture\-orientation " value
|
.BI "\-\-capture\-orientation " value
|
||||||
Possible values are 0, 90, 180, 270, flip0, flip90, flip180 and flip270, possibly prefixed by '@'.
|
Possible values are 0, 90, 180, 270, flip0, flip90, flip180 and flip270, possibly prefixed by '@'.
|
||||||
|
|
|
@ -93,6 +93,8 @@ enum {
|
||||||
OPT_CAMERA_AR,
|
OPT_CAMERA_AR,
|
||||||
OPT_CAMERA_FPS,
|
OPT_CAMERA_FPS,
|
||||||
OPT_CAMERA_HIGH_SPEED,
|
OPT_CAMERA_HIGH_SPEED,
|
||||||
|
OPT_CAMERA_ZOOM_STEP,
|
||||||
|
OPT_CAMERA_TORCH,
|
||||||
OPT_DISPLAY_ORIENTATION,
|
OPT_DISPLAY_ORIENTATION,
|
||||||
OPT_RECORD_ORIENTATION,
|
OPT_RECORD_ORIENTATION,
|
||||||
OPT_ORIENTATION,
|
OPT_ORIENTATION,
|
||||||
|
@ -563,6 +565,17 @@ static const struct sc_option options[] = {
|
||||||
.text = "Limit the frame rate of screen capture (officially supported "
|
.text = "Limit the frame rate of screen capture (officially supported "
|
||||||
"since Android 10, but may work on earlier versions).",
|
"since Android 10, but may work on earlier versions).",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_CAMERA_ZOOM_STEP,
|
||||||
|
.longopt = "camera-zoom-step",
|
||||||
|
.argdesc = "value",
|
||||||
|
.text = "Specify the camera zoom step value.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_CAMERA_TORCH,
|
||||||
|
.longopt = "camera-torch",
|
||||||
|
.text = "Turns the torch on when starting the camera.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.longopt_id = OPT_MOUSE,
|
.longopt_id = OPT_MOUSE,
|
||||||
.longopt = "mouse",
|
.longopt = "mouse",
|
||||||
|
@ -1142,6 +1155,18 @@ static const struct sc_shortcut shortcuts[] = {
|
||||||
.shortcuts = { "Right-click (when screen is off)" },
|
.shortcuts = { "Right-click (when screen is off)" },
|
||||||
.text = "Power on",
|
.text = "Power on",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+q" },
|
||||||
|
.text = "Camera toggle torch",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+1" },
|
||||||
|
.text = "Camera zoom in",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.shortcuts = { "MOD+2" },
|
||||||
|
.text = "Camera zoom out",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.shortcuts = { "MOD+o" },
|
.shortcuts = { "MOD+o" },
|
||||||
.text = "Turn device screen off (keep mirroring)",
|
.text = "Turn device screen off (keep mirroring)",
|
||||||
|
@ -2420,6 +2445,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
case OPT_MAX_FPS:
|
case OPT_MAX_FPS:
|
||||||
opts->max_fps = optarg;
|
opts->max_fps = optarg;
|
||||||
break;
|
break;
|
||||||
|
case OPT_CAMERA_ZOOM_STEP:
|
||||||
|
opts->camera_zoom_step = optarg;
|
||||||
|
break;
|
||||||
|
case OPT_CAMERA_TORCH:
|
||||||
|
opts->camera_torch = true;
|
||||||
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
if (!parse_max_size(optarg, &opts->max_size)) {
|
if (!parse_max_size(optarg, &opts->max_size)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -3104,11 +3135,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
LOGE("--camera-high-speed requires an explicit --camera-fps value");
|
LOGE("--camera-high-speed requires an explicit --camera-fps value");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts->control) {
|
|
||||||
LOGI("Camera video source: control disabled");
|
|
||||||
opts->control = false;
|
|
||||||
}
|
|
||||||
} else if (opts->camera_id
|
} else if (opts->camera_id
|
||||||
|| opts->camera_ar
|
|| opts->camera_ar
|
||||||
|| opts->camera_facing != SC_CAMERA_FACING_ANY
|
|| opts->camera_facing != SC_CAMERA_FACING_ANY
|
||||||
|
|
|
@ -187,6 +187,9 @@ sc_control_msg_serialize(const struct sc_control_msg *msg, uint8_t *buf) {
|
||||||
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
case SC_CONTROL_MSG_TYPE_COLLAPSE_PANELS:
|
||||||
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
case SC_CONTROL_MSG_TYPE_ROTATE_DEVICE:
|
||||||
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
case SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_TOGGLE_TORCH:
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN:
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT:
|
||||||
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
|
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
|
||||||
// no additional data
|
// no additional data
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -318,6 +321,15 @@ sc_control_msg_log(const struct sc_control_msg *msg) {
|
||||||
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
|
case SC_CONTROL_MSG_TYPE_RESET_VIDEO:
|
||||||
LOG_CMSG("reset video");
|
LOG_CMSG("reset video");
|
||||||
break;
|
break;
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_TOGGLE_TORCH:
|
||||||
|
LOG_CMSG("toggle camera torch");
|
||||||
|
break;
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN:
|
||||||
|
LOG_CMSG("camera zoom in");
|
||||||
|
break;
|
||||||
|
case SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT:
|
||||||
|
LOG_CMSG("camera zoom out");
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
LOG_CMSG("unknown type: %u", (unsigned) msg->type);
|
LOG_CMSG("unknown type: %u", (unsigned) msg->type);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -43,6 +43,9 @@ enum sc_control_msg_type {
|
||||||
SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS,
|
SC_CONTROL_MSG_TYPE_OPEN_HARD_KEYBOARD_SETTINGS,
|
||||||
SC_CONTROL_MSG_TYPE_START_APP,
|
SC_CONTROL_MSG_TYPE_START_APP,
|
||||||
SC_CONTROL_MSG_TYPE_RESET_VIDEO,
|
SC_CONTROL_MSG_TYPE_RESET_VIDEO,
|
||||||
|
SC_CONTROL_MSG_TYPE_CAMERA_TOGGLE_TORCH,
|
||||||
|
SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN,
|
||||||
|
SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sc_copy_key {
|
enum sc_copy_key {
|
||||||
|
|
193
app/src/im/camera.c
Normal file
193
app/src/im/camera.c
Normal file
|
@ -0,0 +1,193 @@
|
||||||
|
#include "input_manager.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
|
#include "android/input.h"
|
||||||
|
#include "android/keycodes.h"
|
||||||
|
#include "input_events.h"
|
||||||
|
#include "screen.h"
|
||||||
|
#include "shortcut_mod.h"
|
||||||
|
#include "util/log.h"
|
||||||
|
#include "camera.h"
|
||||||
|
|
||||||
|
static void
|
||||||
|
camera_toggle_torch(struct sc_input_manager *im) {
|
||||||
|
assert(im->controller);
|
||||||
|
|
||||||
|
struct sc_control_msg msg;
|
||||||
|
msg.type = SC_CONTROL_MSG_TYPE_CAMERA_TOGGLE_TORCH;
|
||||||
|
|
||||||
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
|
LOGW("Could not request camera toggle torch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camera_zoom_in(struct sc_input_manager *im) {
|
||||||
|
assert(im->controller);
|
||||||
|
|
||||||
|
struct sc_control_msg msg;
|
||||||
|
msg.type = SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_IN;
|
||||||
|
|
||||||
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
|
LOGW("Could not request camera zoom in");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
camera_zoom_out(struct sc_input_manager *im) {
|
||||||
|
assert(im->controller);
|
||||||
|
|
||||||
|
struct sc_control_msg msg;
|
||||||
|
msg.type = SC_CONTROL_MSG_TYPE_CAMERA_ZOOM_OUT;
|
||||||
|
|
||||||
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
|
LOGW("Could not request camera zoom out");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_video(struct sc_input_manager *im) {
|
||||||
|
assert(im->controller);
|
||||||
|
|
||||||
|
struct sc_control_msg msg;
|
||||||
|
msg.type = SC_CONTROL_MSG_TYPE_RESET_VIDEO;
|
||||||
|
|
||||||
|
if (!sc_controller_push_msg(im->controller, &msg)) {
|
||||||
|
LOGW("Could not request reset video");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
apply_orientation_transform(struct sc_input_manager *im,
|
||||||
|
enum sc_orientation transform) {
|
||||||
|
struct sc_screen *screen = im->screen;
|
||||||
|
enum sc_orientation new_orientation =
|
||||||
|
sc_orientation_apply(screen->orientation, transform);
|
||||||
|
sc_screen_set_orientation(screen, new_orientation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_input_manager_process_key_camera(struct sc_input_manager *im,
|
||||||
|
const SDL_KeyboardEvent *event) {
|
||||||
|
bool control = im->controller;
|
||||||
|
bool paused = im->screen->paused;
|
||||||
|
bool video = im->screen->video;
|
||||||
|
|
||||||
|
SDL_Keycode sdl_keycode = event->keysym.sym;
|
||||||
|
uint16_t mod = event->keysym.mod;
|
||||||
|
bool down = event->type == SDL_KEYDOWN;
|
||||||
|
bool shift = event->keysym.mod & KMOD_SHIFT;
|
||||||
|
bool repeat = event->repeat;
|
||||||
|
|
||||||
|
uint16_t mods = im->sdl_shortcut_mods;
|
||||||
|
bool is_shortcut = sc_shortcut_mods_is_shortcut_mod(mods, mod)
|
||||||
|
|| sc_shortcut_mods_is_shortcut_key(mods, sdl_keycode);
|
||||||
|
|
||||||
|
if (down && !repeat) {
|
||||||
|
if (sdl_keycode == im->last_keycode && mod == im->last_mod) {
|
||||||
|
++im->key_repeat;
|
||||||
|
} else {
|
||||||
|
im->key_repeat = 0;
|
||||||
|
im->last_keycode = sdl_keycode;
|
||||||
|
im->last_mod = mod;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_shortcut) {
|
||||||
|
switch (sdl_keycode) {
|
||||||
|
// Camera
|
||||||
|
case SDLK_1:
|
||||||
|
if (control && video && !shift && down) {
|
||||||
|
camera_zoom_in(im);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_2:
|
||||||
|
if (control && video && !shift && down) {
|
||||||
|
camera_zoom_out(im);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_q:
|
||||||
|
if (control && video && !shift && !repeat && down) {
|
||||||
|
camera_toggle_torch(im);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
// Window
|
||||||
|
case SDLK_f:
|
||||||
|
if (video && !shift && !repeat && down) {
|
||||||
|
sc_screen_toggle_fullscreen(im->screen);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_w:
|
||||||
|
if (video && !shift && !repeat && down) {
|
||||||
|
sc_screen_resize_to_fit(im->screen);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_g:
|
||||||
|
if (video && !shift && !repeat && down) {
|
||||||
|
sc_screen_resize_to_pixel_perfect(im->screen);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_DOWN:
|
||||||
|
if (shift) {
|
||||||
|
if (video && !repeat && down) {
|
||||||
|
apply_orientation_transform(im,
|
||||||
|
SC_ORIENTATION_FLIP_180);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_UP:
|
||||||
|
if (shift) {
|
||||||
|
if (video && !repeat && down) {
|
||||||
|
apply_orientation_transform(im,
|
||||||
|
SC_ORIENTATION_FLIP_180);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_LEFT:
|
||||||
|
if (video && !repeat && down) {
|
||||||
|
if (shift) {
|
||||||
|
apply_orientation_transform(im,
|
||||||
|
SC_ORIENTATION_FLIP_0);
|
||||||
|
} else {
|
||||||
|
apply_orientation_transform(im,
|
||||||
|
SC_ORIENTATION_270);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case SDLK_RIGHT:
|
||||||
|
if (video && !repeat && down) {
|
||||||
|
if (shift) {
|
||||||
|
apply_orientation_transform(im,
|
||||||
|
SC_ORIENTATION_FLIP_0);
|
||||||
|
} else {
|
||||||
|
apply_orientation_transform(im,
|
||||||
|
SC_ORIENTATION_90);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
// Video
|
||||||
|
case SDLK_r:
|
||||||
|
if (control && !repeat && down && !paused) {
|
||||||
|
if (shift) {
|
||||||
|
reset_video(im);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
sc_input_manager_handle_event_camera(struct sc_input_manager *im,
|
||||||
|
const SDL_Event *event) {
|
||||||
|
switch (event->type) {
|
||||||
|
case SDL_KEYDOWN:
|
||||||
|
case SDL_KEYUP:
|
||||||
|
sc_input_manager_process_key_camera(im, &event->key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
11
app/src/im/camera.h
Normal file
11
app/src/im/camera.h
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
#ifndef SC_CAMERA_H
|
||||||
|
#define SC_CAMERA_H
|
||||||
|
|
||||||
|
#include "input_manager.h"
|
||||||
|
|
||||||
|
#include <SDL2/SDL_events.h>
|
||||||
|
|
||||||
|
void sc_input_manager_handle_event_camera(struct sc_input_manager *im,
|
||||||
|
const SDL_Event *event);
|
||||||
|
|
||||||
|
#endif
|
|
@ -52,6 +52,8 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||||
.video_bit_rate = 0,
|
.video_bit_rate = 0,
|
||||||
.audio_bit_rate = 0,
|
.audio_bit_rate = 0,
|
||||||
.max_fps = NULL,
|
.max_fps = NULL,
|
||||||
|
.camera_zoom_step = NULL,
|
||||||
|
.camera_torch = false,
|
||||||
.capture_orientation = SC_ORIENTATION_0,
|
.capture_orientation = SC_ORIENTATION_0,
|
||||||
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
|
.capture_orientation_lock = SC_ORIENTATION_UNLOCKED,
|
||||||
.display_orientation = SC_ORIENTATION_0,
|
.display_orientation = SC_ORIENTATION_0,
|
||||||
|
|
|
@ -262,6 +262,7 @@ struct scrcpy_options {
|
||||||
uint32_t audio_bit_rate;
|
uint32_t audio_bit_rate;
|
||||||
const char *max_fps; // float to be parsed by the server
|
const char *max_fps; // float to be parsed by the server
|
||||||
const char *angle; // float to be parsed by the server
|
const char *angle; // float to be parsed by the server
|
||||||
|
const char *camera_zoom_step; // float to be parsed by the server
|
||||||
enum sc_orientation capture_orientation;
|
enum sc_orientation capture_orientation;
|
||||||
enum sc_orientation_lock capture_orientation_lock;
|
enum sc_orientation_lock capture_orientation_lock;
|
||||||
enum sc_orientation display_orientation;
|
enum sc_orientation display_orientation;
|
||||||
|
@ -314,6 +315,7 @@ struct scrcpy_options {
|
||||||
bool require_audio;
|
bool require_audio;
|
||||||
bool kill_adb_on_close;
|
bool kill_adb_on_close;
|
||||||
bool camera_high_speed;
|
bool camera_high_speed;
|
||||||
|
bool camera_torch;
|
||||||
#define SC_OPTION_LIST_ENCODERS 0x1
|
#define SC_OPTION_LIST_ENCODERS 0x1
|
||||||
#define SC_OPTION_LIST_DISPLAYS 0x2
|
#define SC_OPTION_LIST_DISPLAYS 0x2
|
||||||
#define SC_OPTION_LIST_CAMERAS 0x4
|
#define SC_OPTION_LIST_CAMERAS 0x4
|
||||||
|
|
|
@ -471,6 +471,8 @@ scrcpy(struct scrcpy_options *options) {
|
||||||
.power_on = options->power_on,
|
.power_on = options->power_on,
|
||||||
.kill_adb_on_close = options->kill_adb_on_close,
|
.kill_adb_on_close = options->kill_adb_on_close,
|
||||||
.camera_high_speed = options->camera_high_speed,
|
.camera_high_speed = options->camera_high_speed,
|
||||||
|
.camera_zoom_step = options->camera_zoom_step,
|
||||||
|
.camera_torch = options->camera_torch,
|
||||||
.vd_destroy_content = options->vd_destroy_content,
|
.vd_destroy_content = options->vd_destroy_content,
|
||||||
.vd_system_decorations = options->vd_system_decorations,
|
.vd_system_decorations = options->vd_system_decorations,
|
||||||
.list = options->list,
|
.list = options->list,
|
||||||
|
@ -804,6 +806,7 @@ aoa_complete:
|
||||||
|
|
||||||
struct sc_screen_params screen_params = {
|
struct sc_screen_params screen_params = {
|
||||||
.video = options->video_playback,
|
.video = options->video_playback,
|
||||||
|
.video_source = options->video_source,
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.fp = fp,
|
.fp = fp,
|
||||||
.kp = kp,
|
.kp = kp,
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "icon.h"
|
#include "icon.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
#include "im/camera.h"
|
||||||
|
|
||||||
#define DISPLAY_MARGINS 96
|
#define DISPLAY_MARGINS 96
|
||||||
|
|
||||||
|
@ -335,6 +336,7 @@ sc_screen_init(struct sc_screen *screen,
|
||||||
screen->orientation = SC_ORIENTATION_0;
|
screen->orientation = SC_ORIENTATION_0;
|
||||||
|
|
||||||
screen->video = params->video;
|
screen->video = params->video;
|
||||||
|
screen->video_source = params->video_source;
|
||||||
|
|
||||||
screen->req.x = params->window_x;
|
screen->req.x = params->window_x;
|
||||||
screen->req.y = params->window_y;
|
screen->req.y = params->window_y;
|
||||||
|
@ -867,7 +869,15 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_input_manager_handle_event(&screen->im, event);
|
switch (screen->video_source) {
|
||||||
|
case SC_VIDEO_SOURCE_DISPLAY:
|
||||||
|
sc_input_manager_handle_event(&screen->im, event);
|
||||||
|
break;
|
||||||
|
case SC_VIDEO_SOURCE_CAMERA:
|
||||||
|
sc_input_manager_handle_event_camera(&screen->im, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,8 @@ struct sc_screen {
|
||||||
|
|
||||||
bool paused;
|
bool paused;
|
||||||
AVFrame *resume_frame;
|
AVFrame *resume_frame;
|
||||||
|
|
||||||
|
enum sc_video_source video_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_screen_params {
|
struct sc_screen_params {
|
||||||
|
@ -100,6 +102,8 @@ struct sc_screen_params {
|
||||||
|
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
bool start_fps_counter;
|
bool start_fps_counter;
|
||||||
|
|
||||||
|
enum sc_video_source video_source;
|
||||||
};
|
};
|
||||||
|
|
||||||
// initialize screen, create window, renderer and texture (window is hidden)
|
// initialize screen, create window, renderer and texture (window is hidden)
|
||||||
|
|
|
@ -354,6 +354,13 @@ execute_server(struct sc_server *server,
|
||||||
if (params->camera_fps) {
|
if (params->camera_fps) {
|
||||||
ADD_PARAM("camera_fps=%" PRIu16, params->camera_fps);
|
ADD_PARAM("camera_fps=%" PRIu16, params->camera_fps);
|
||||||
}
|
}
|
||||||
|
if (params->camera_zoom_step) {
|
||||||
|
VALIDATE_STRING(params->camera_zoom_step);
|
||||||
|
ADD_PARAM("camera_zoom_step=%s", params->camera_zoom_step);
|
||||||
|
}
|
||||||
|
if (params->camera_torch) {
|
||||||
|
ADD_PARAM("camera_torch=true");
|
||||||
|
}
|
||||||
if (params->camera_high_speed) {
|
if (params->camera_high_speed) {
|
||||||
ADD_PARAM("camera_high_speed=true");
|
ADD_PARAM("camera_high_speed=true");
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,8 @@ struct sc_server_params {
|
||||||
uint32_t audio_bit_rate;
|
uint32_t audio_bit_rate;
|
||||||
const char *max_fps; // float to be parsed by the server
|
const char *max_fps; // float to be parsed by the server
|
||||||
const char *angle; // float to be parsed by the server
|
const char *angle; // float to be parsed by the server
|
||||||
|
const char *camera_zoom_step; // float to be parsed by the server
|
||||||
|
bool camera_torch;
|
||||||
sc_tick screen_off_timeout;
|
sc_tick screen_off_timeout;
|
||||||
enum sc_orientation capture_orientation;
|
enum sc_orientation capture_orientation;
|
||||||
enum sc_orientation_lock capture_orientation_lock;
|
enum sc_orientation_lock capture_orientation_lock;
|
||||||
|
|
|
@ -147,6 +147,28 @@ scrcpy --video-source=camera --camera-size=1920x1080 --camera-fps=240
|
||||||
[high speed]: https://developer.android.com/reference/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession
|
[high speed]: https://developer.android.com/reference/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession
|
||||||
|
|
||||||
|
|
||||||
|
## Torch
|
||||||
|
|
||||||
|
- <kbd>MOD</kbd>+<kbd>q</kbd> to toggle the torch.
|
||||||
|
|
||||||
|
To turn the torch on when starting the camera, add `--camera-torch`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --video-source=camera --camera-torch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Zoom
|
||||||
|
|
||||||
|
- <kbd>MOD</kbd>+<kbd>1</kbd> to zoom in.
|
||||||
|
- <kbd>MOD</kbd>+<kbd>2</kbd> to zoom out.
|
||||||
|
|
||||||
|
To change the camera zoom step, set `--camera-zoom-step`.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --video-source=camera --camera-zoom-step=0.025
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Brace expansion tip
|
## Brace expansion tip
|
||||||
|
|
||||||
All camera options start with `--camera-`, so if your shell supports it, you can
|
All camera options start with `--camera-`, so if your shell supports it, you can
|
||||||
|
|
|
@ -53,6 +53,9 @@ _<kbd>[Super]</kbd> is typically the <kbd>Windows</kbd> or <kbd>Cmd</kbd> key._
|
||||||
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
| Inject computer clipboard text | <kbd>MOD</kbd>+<kbd>Shift</kbd>+<kbd>v</kbd>
|
||||||
| Open keyboard settings (HID keyboard only) | <kbd>MOD</kbd>+<kbd>k</kbd>
|
| Open keyboard settings (HID keyboard only) | <kbd>MOD</kbd>+<kbd>k</kbd>
|
||||||
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
| Enable/disable FPS counter (on stdout) | <kbd>MOD</kbd>+<kbd>i</kbd>
|
||||||
|
| Camera torch toggle | <kbd>MOD</kbd>+<kbd>q</kbd>
|
||||||
|
| Camera zoom in | <kbd>MOD</kbd>+<kbd>1</kbd>
|
||||||
|
| Camera zoom out | <kbd>MOD</kbd>+<kbd>2</kbd>
|
||||||
| Pinch-to-zoom/rotate | <kbd>Ctrl</kbd>+_click-and-move_
|
| Pinch-to-zoom/rotate | <kbd>Ctrl</kbd>+_click-and-move_
|
||||||
| Tilt vertically (slide with 2 fingers) | <kbd>Shift</kbd>+_click-and-move_
|
| Tilt vertically (slide with 2 fingers) | <kbd>Shift</kbd>+_click-and-move_
|
||||||
| Tilt horizontally (slide with 2 fingers) | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+_click-and-move_
|
| Tilt horizontally (slide with 2 fingers) | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+_click-and-move_
|
||||||
|
|
|
@ -46,6 +46,8 @@ public class Options {
|
||||||
private CameraAspectRatio cameraAspectRatio;
|
private CameraAspectRatio cameraAspectRatio;
|
||||||
private int cameraFps;
|
private int cameraFps;
|
||||||
private boolean cameraHighSpeed;
|
private boolean cameraHighSpeed;
|
||||||
|
private float cameraZoomStep;
|
||||||
|
private boolean cameraTorch;
|
||||||
private boolean showTouches;
|
private boolean showTouches;
|
||||||
private boolean stayAwake;
|
private boolean stayAwake;
|
||||||
private int screenOffTimeout = -1;
|
private int screenOffTimeout = -1;
|
||||||
|
@ -176,6 +178,14 @@ public class Options {
|
||||||
return cameraHighSpeed;
|
return cameraHighSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getCameraZoomStep() {
|
||||||
|
return cameraZoomStep;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getCameraTorch() {
|
||||||
|
return cameraTorch;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean getShowTouches() {
|
public boolean getShowTouches() {
|
||||||
return showTouches;
|
return showTouches;
|
||||||
}
|
}
|
||||||
|
@ -474,6 +484,12 @@ public class Options {
|
||||||
case "camera_high_speed":
|
case "camera_high_speed":
|
||||||
options.cameraHighSpeed = Boolean.parseBoolean(value);
|
options.cameraHighSpeed = Boolean.parseBoolean(value);
|
||||||
break;
|
break;
|
||||||
|
case "camera_zoom_step":
|
||||||
|
options.cameraZoomStep = Float.parseFloat(value);
|
||||||
|
break;
|
||||||
|
case "camera_torch":
|
||||||
|
options.cameraTorch = Boolean.parseBoolean(value);
|
||||||
|
break;
|
||||||
case "new_display":
|
case "new_display":
|
||||||
options.newDisplay = parseNewDisplay(value);
|
options.newDisplay = parseNewDisplay(value);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -25,6 +25,9 @@ public final class ControlMessage {
|
||||||
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15;
|
public static final int TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15;
|
||||||
public static final int TYPE_START_APP = 16;
|
public static final int TYPE_START_APP = 16;
|
||||||
public static final int TYPE_RESET_VIDEO = 17;
|
public static final int TYPE_RESET_VIDEO = 17;
|
||||||
|
public static final int TYPE_CAMERA_TOGGLE_TORCH = 18;
|
||||||
|
public static final int TYPE_CAMERA_ZOOM_IN = 19;
|
||||||
|
public static final int TYPE_CAMERA_ZOOM_OUT = 20;
|
||||||
|
|
||||||
public static final long SEQUENCE_INVALID = 0;
|
public static final long SEQUENCE_INVALID = 0;
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,9 @@ public class ControlMessageReader {
|
||||||
case ControlMessage.TYPE_COLLAPSE_PANELS:
|
case ControlMessage.TYPE_COLLAPSE_PANELS:
|
||||||
case ControlMessage.TYPE_ROTATE_DEVICE:
|
case ControlMessage.TYPE_ROTATE_DEVICE:
|
||||||
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
case ControlMessage.TYPE_OPEN_HARD_KEYBOARD_SETTINGS:
|
||||||
|
case ControlMessage.TYPE_CAMERA_TOGGLE_TORCH:
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_IN:
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_OUT:
|
||||||
case ControlMessage.TYPE_RESET_VIDEO:
|
case ControlMessage.TYPE_RESET_VIDEO:
|
||||||
return ControlMessage.createEmpty(type);
|
return ControlMessage.createEmpty(type);
|
||||||
case ControlMessage.TYPE_UHID_CREATE:
|
case ControlMessage.TYPE_UHID_CREATE:
|
||||||
|
|
|
@ -12,6 +12,7 @@ import com.genymobile.scrcpy.device.Position;
|
||||||
import com.genymobile.scrcpy.device.Size;
|
import com.genymobile.scrcpy.device.Size;
|
||||||
import com.genymobile.scrcpy.util.Ln;
|
import com.genymobile.scrcpy.util.Ln;
|
||||||
import com.genymobile.scrcpy.util.LogUtils;
|
import com.genymobile.scrcpy.util.LogUtils;
|
||||||
|
import com.genymobile.scrcpy.video.CameraCapture;
|
||||||
import com.genymobile.scrcpy.video.SurfaceCapture;
|
import com.genymobile.scrcpy.video.SurfaceCapture;
|
||||||
import com.genymobile.scrcpy.video.VirtualDisplayListener;
|
import com.genymobile.scrcpy.video.VirtualDisplayListener;
|
||||||
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
import com.genymobile.scrcpy.wrappers.ClipboardManager;
|
||||||
|
@ -99,6 +100,7 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
||||||
|
|
||||||
// Used for resetting video encoding on RESET_VIDEO message
|
// Used for resetting video encoding on RESET_VIDEO message
|
||||||
private SurfaceCapture surfaceCapture;
|
private SurfaceCapture surfaceCapture;
|
||||||
|
private CameraCapture cameraCapture;
|
||||||
|
|
||||||
public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) {
|
public Controller(ControlChannel controlChannel, CleanUp cleanUp, Options options) {
|
||||||
this.displayId = options.getDisplayId();
|
this.displayId = options.getDisplayId();
|
||||||
|
@ -150,6 +152,9 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
||||||
|
|
||||||
public void setSurfaceCapture(SurfaceCapture surfaceCapture) {
|
public void setSurfaceCapture(SurfaceCapture surfaceCapture) {
|
||||||
this.surfaceCapture = surfaceCapture;
|
this.surfaceCapture = surfaceCapture;
|
||||||
|
if (this.surfaceCapture instanceof CameraCapture) {
|
||||||
|
this.cameraCapture = (CameraCapture) this.surfaceCapture;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private UhidManager getUhidManager() {
|
private UhidManager getUhidManager() {
|
||||||
|
@ -331,6 +336,15 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
||||||
case ControlMessage.TYPE_RESET_VIDEO:
|
case ControlMessage.TYPE_RESET_VIDEO:
|
||||||
resetVideo();
|
resetVideo();
|
||||||
break;
|
break;
|
||||||
|
case ControlMessage.TYPE_CAMERA_TOGGLE_TORCH:
|
||||||
|
cameraToggleTorch();
|
||||||
|
break;
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_IN:
|
||||||
|
cameraZoomIn();
|
||||||
|
break;
|
||||||
|
case ControlMessage.TYPE_CAMERA_ZOOM_OUT:
|
||||||
|
cameraZoomOut();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
@ -754,4 +768,22 @@ public class Controller implements AsyncProcessor, VirtualDisplayListener {
|
||||||
surfaceCapture.requestInvalidate();
|
surfaceCapture.requestInvalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cameraToggleTorch() {
|
||||||
|
if (cameraCapture != null) {
|
||||||
|
cameraCapture.toggleTorch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cameraZoomIn() {
|
||||||
|
if (cameraCapture != null) {
|
||||||
|
cameraCapture.zoomIn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cameraZoomOut() {
|
||||||
|
if (cameraCapture != null) {
|
||||||
|
cameraCapture.zoomOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,13 @@ public class CameraCapture extends SurfaceCapture {
|
||||||
private Handler cameraHandler;
|
private Handler cameraHandler;
|
||||||
private CameraDevice cameraDevice;
|
private CameraDevice cameraDevice;
|
||||||
private Executor cameraExecutor;
|
private Executor cameraExecutor;
|
||||||
|
private CameraCaptureSession session;
|
||||||
|
private CaptureRequest.Builder requestBuilder;
|
||||||
|
private boolean torchAvailable = false;
|
||||||
|
private boolean torchOn = false;
|
||||||
|
private float zoomLevel = 1f;
|
||||||
|
private float zoomStep = 0.025f;
|
||||||
|
private Range<Float> zoomRange = new Range<>(1f, 1f);
|
||||||
|
|
||||||
private final AtomicBoolean disconnected = new AtomicBoolean();
|
private final AtomicBoolean disconnected = new AtomicBoolean();
|
||||||
|
|
||||||
|
@ -90,6 +97,10 @@ public class CameraCapture extends SurfaceCapture {
|
||||||
this.captureOrientation = options.getCaptureOrientation();
|
this.captureOrientation = options.getCaptureOrientation();
|
||||||
assert captureOrientation != null;
|
assert captureOrientation != null;
|
||||||
this.angle = options.getAngle();
|
this.angle = options.getAngle();
|
||||||
|
if (options.getCameraZoomStep() > 0f) {
|
||||||
|
this.zoomStep = options.getCameraZoomStep();
|
||||||
|
}
|
||||||
|
this.torchOn = options.getCameraTorch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -106,6 +117,19 @@ public class CameraCapture extends SurfaceCapture {
|
||||||
}
|
}
|
||||||
|
|
||||||
Ln.i("Using camera '" + cameraId + "'");
|
Ln.i("Using camera '" + cameraId + "'");
|
||||||
|
|
||||||
|
CameraCharacteristics characteristics = ServiceManager.getCameraManager().getCameraCharacteristics(cameraId);
|
||||||
|
|
||||||
|
torchAvailable = Boolean.TRUE.equals(secureGet(characteristics, CameraCharacteristics.FLASH_INFO_AVAILABLE));
|
||||||
|
if (!torchAvailable && torchOn) {
|
||||||
|
Ln.w("Camera flash unit not found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
zoomRange = secureGet(characteristics, CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
|
||||||
|
if (zoomRange != null) {
|
||||||
|
Ln.d(String.format("Camera zoom range: [%f, %f]", zoomRange.getLower(), zoomRange.getUpper()));
|
||||||
|
}
|
||||||
|
|
||||||
cameraDevice = openCamera(cameraId);
|
cameraDevice = openCamera(cameraId);
|
||||||
} catch (CameraAccessException | InterruptedException e) {
|
} catch (CameraAccessException | InterruptedException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
|
@ -262,7 +286,7 @@ public class CameraCapture extends SurfaceCapture {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
CameraCaptureSession session = createCaptureSession(cameraDevice, surface);
|
session = createCaptureSession(cameraDevice, surface);
|
||||||
CaptureRequest request = createCaptureRequest(surface);
|
CaptureRequest request = createCaptureRequest(surface);
|
||||||
setRepeatingRequest(session, request);
|
setRepeatingRequest(session, request);
|
||||||
} catch (CameraAccessException | InterruptedException e) {
|
} catch (CameraAccessException | InterruptedException e) {
|
||||||
|
@ -381,13 +405,17 @@ public class CameraCapture extends SurfaceCapture {
|
||||||
}
|
}
|
||||||
|
|
||||||
private CaptureRequest createCaptureRequest(Surface surface) throws CameraAccessException {
|
private CaptureRequest createCaptureRequest(Surface surface) throws CameraAccessException {
|
||||||
CaptureRequest.Builder requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
requestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||||
requestBuilder.addTarget(surface);
|
requestBuilder.addTarget(surface);
|
||||||
|
|
||||||
if (fps > 0) {
|
if (fps > 0) {
|
||||||
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(fps, fps));
|
requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, new Range<>(fps, fps));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (torchOn && torchAvailable) {
|
||||||
|
requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
|
||||||
|
}
|
||||||
|
|
||||||
return requestBuilder.build();
|
return requestBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -421,6 +449,51 @@ public class CameraCapture extends SurfaceCapture {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void requestInvalidate() {
|
public void requestInvalidate() {
|
||||||
// do nothing (the user could not request a reset anyway for now, since there is no controller for camera mirroring)
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T secureGet(CameraCharacteristics characteristics, CameraCharacteristics.Key<T> key) {
|
||||||
|
try {
|
||||||
|
return characteristics.get(key);
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
Ln.w("Failed to get characteristic for key: " + key.getName(), e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void toggleTorch() {
|
||||||
|
if (torchAvailable) {
|
||||||
|
torchOn = !torchOn;
|
||||||
|
try {
|
||||||
|
requestBuilder.set(CaptureRequest.FLASH_MODE,
|
||||||
|
(torchOn) ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
|
||||||
|
setRepeatingRequest(session, requestBuilder.build());
|
||||||
|
} catch (CameraAccessException | InterruptedException e) {
|
||||||
|
Ln.e("Camera toggle torch error: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setZoom(float level) {
|
||||||
|
if (zoomRange == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
float zoomClamped = Math.max(zoomRange.getLower(), Math.min(level, zoomRange.getUpper()));
|
||||||
|
requestBuilder.set(CaptureRequest.CONTROL_ZOOM_RATIO, zoomClamped);
|
||||||
|
setRepeatingRequest(session, requestBuilder.build());
|
||||||
|
zoomLevel = zoomClamped;
|
||||||
|
} catch (CameraAccessException | InterruptedException e) {
|
||||||
|
Ln.e("Camera set zoom error: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void zoomIn() {
|
||||||
|
setZoom(zoomLevel += zoomStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void zoomOut() {
|
||||||
|
setZoom(zoomLevel -= zoomStep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue