Compare commits
10 Commits
rtmp
...
icon_decod
Author | SHA1 | Date | |
---|---|---|---|
77ebafd96c | |||
9ea4446369 | |||
5d1d5bdc16 | |||
fd9498e07c | |||
09e8c20168 | |||
da484b7ab9 | |||
063a8339ed | |||
b5c8de08e0 | |||
45fe6b602b | |||
cca2c9ffb7 |
@ -316,6 +316,10 @@ Disable video forwarding.
|
|||||||
.B \-\-no\-video\-playback
|
.B \-\-no\-video\-playback
|
||||||
Disable video playback on the computer.
|
Disable video playback on the computer.
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.B \-\-no\-window
|
||||||
|
Disable scrcpy window. Implies --no-video-playback and --no-control.
|
||||||
|
|
||||||
.TP
|
.TP
|
||||||
.BI "\-\-orientation " value
|
.BI "\-\-orientation " value
|
||||||
Same as --display-orientation=value --record-orientation=value.
|
Same as --display-orientation=value --record-orientation=value.
|
||||||
|
@ -194,7 +194,11 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
|||||||
// Still insufficient, drop old samples to make space
|
// Still insufficient, drop old samples to make space
|
||||||
skipped_samples = sc_audiobuf_read(&ap->buf, NULL, remaining);
|
skipped_samples = sc_audiobuf_read(&ap->buf, NULL, remaining);
|
||||||
assert(skipped_samples == remaining);
|
assert(skipped_samples == remaining);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UnlockAudioDevice(ap->device);
|
||||||
|
|
||||||
|
if (written < samples) {
|
||||||
// Now there is enough space
|
// Now there is enough space
|
||||||
uint32_t w = sc_audiobuf_write(&ap->buf,
|
uint32_t w = sc_audiobuf_write(&ap->buf,
|
||||||
swr_buf + TO_BYTES(written),
|
swr_buf + TO_BYTES(written),
|
||||||
@ -202,8 +206,6 @@ sc_audio_player_frame_sink_push(struct sc_frame_sink *sink,
|
|||||||
assert(w == remaining);
|
assert(w == remaining);
|
||||||
(void) w;
|
(void) w;
|
||||||
}
|
}
|
||||||
|
|
||||||
SDL_UnlockAudioDevice(ap->device);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t underflow = 0;
|
uint32_t underflow = 0;
|
||||||
|
@ -97,6 +97,7 @@ enum {
|
|||||||
OPT_MOUSE,
|
OPT_MOUSE,
|
||||||
OPT_HID_KEYBOARD_DEPRECATED,
|
OPT_HID_KEYBOARD_DEPRECATED,
|
||||||
OPT_HID_MOUSE_DEPRECATED,
|
OPT_HID_MOUSE_DEPRECATED,
|
||||||
|
OPT_NO_WINDOW,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
@ -566,6 +567,12 @@ static const struct sc_option options[] = {
|
|||||||
.longopt = "no-video-playback",
|
.longopt = "no-video-playback",
|
||||||
.text = "Disable video playback on the computer.",
|
.text = "Disable video playback on the computer.",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_NO_WINDOW,
|
||||||
|
.longopt = "no-window",
|
||||||
|
.text = "Disable scrcpy window. Implies --no-video-playback and "
|
||||||
|
"--no-control.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.longopt_id = OPT_ORIENTATION,
|
.longopt_id = OPT_ORIENTATION,
|
||||||
.longopt = "orientation",
|
.longopt = "orientation",
|
||||||
@ -2486,6 +2493,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
case OPT_CAMERA_HIGH_SPEED:
|
case OPT_CAMERA_HIGH_SPEED:
|
||||||
opts->camera_high_speed = true;
|
opts->camera_high_speed = true;
|
||||||
break;
|
break;
|
||||||
|
case OPT_NO_WINDOW:
|
||||||
|
opts->window = false;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
// getopt prints the error message on stderr
|
// getopt prints the error message on stderr
|
||||||
return false;
|
return false;
|
||||||
@ -2523,6 +2533,12 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
v4l2 = !!opts->v4l2_device;
|
v4l2 = !!opts->v4l2_device;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!opts->window) {
|
||||||
|
// Without window, there cannot be any video playback or control
|
||||||
|
opts->video_playback = false;
|
||||||
|
opts->control = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!opts->video) {
|
if (!opts->video) {
|
||||||
opts->video_playback = false;
|
opts->video_playback = false;
|
||||||
// Do not power on the device on start if video capture is disabled
|
// Do not power on the device on start if video capture is disabled
|
||||||
@ -2544,8 +2560,8 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
opts->audio = false;
|
opts->audio = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opts->video && !opts->audio && !otg) {
|
if (!opts->video && !opts->audio && !opts->control && !otg) {
|
||||||
LOGE("No video, no audio, no OTG: nothing to do");
|
LOGE("No video, no audio, no control, no OTG: nothing to do");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2569,6 +2585,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
|
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
if (v4l2) {
|
if (v4l2) {
|
||||||
|
if (!opts->video) {
|
||||||
|
LOGE("V4L2 sink requires video capture, but --no-video was set.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->lock_video_orientation ==
|
if (opts->lock_video_orientation ==
|
||||||
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
SC_LOCK_VIDEO_ORIENTATION_UNLOCKED) {
|
||||||
LOGI("Video orientation is locked for v4l2 sink. "
|
LOGI("Video orientation is locked for v4l2 sink. "
|
||||||
@ -2588,13 +2609,25 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (opts->control) {
|
||||||
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
|
if (opts->keyboard_input_mode == SC_KEYBOARD_INPUT_MODE_AUTO) {
|
||||||
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
|
opts->keyboard_input_mode = otg ? SC_KEYBOARD_INPUT_MODE_AOA
|
||||||
: SC_KEYBOARD_INPUT_MODE_SDK;
|
: SC_KEYBOARD_INPUT_MODE_SDK;
|
||||||
}
|
}
|
||||||
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
|
if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_AUTO) {
|
||||||
opts->mouse_input_mode = otg ? SC_MOUSE_INPUT_MODE_AOA
|
if (otg) {
|
||||||
: SC_MOUSE_INPUT_MODE_SDK;
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_AOA;
|
||||||
|
} else if (!opts->video_playback) {
|
||||||
|
LOGI("No video mirroring, mouse mode switched to UHID");
|
||||||
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_UHID;
|
||||||
|
} else {
|
||||||
|
opts->mouse_input_mode = SC_MOUSE_INPUT_MODE_SDK;
|
||||||
|
}
|
||||||
|
} else if (opts->mouse_input_mode == SC_MOUSE_INPUT_MODE_SDK
|
||||||
|
&& !opts->video_playback) {
|
||||||
|
LOGE("SDK mouse mode requires video playback. Try --mouse=uhid.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otg) {
|
if (otg) {
|
||||||
@ -2705,6 +2738,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (opts->record_filename) {
|
if (opts->record_filename) {
|
||||||
|
if (!opts->video && !opts->audio) {
|
||||||
|
LOGE("Video and audio disabled, nothing to record");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!opts->record_format) {
|
if (!opts->record_format) {
|
||||||
opts->record_format = guess_record_format(opts->record_filename);
|
opts->record_format = guess_record_format(opts->record_filename);
|
||||||
if (!opts->record_format) {
|
if (!opts->record_format) {
|
||||||
@ -2811,6 +2849,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
|||||||
}
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
|
if (opts->start_fps_counter && !opts->video_playback) {
|
||||||
|
LOGW("--print-fps has no effect without video playback");
|
||||||
|
opts->start_fps_counter = false;
|
||||||
|
}
|
||||||
|
|
||||||
if (otg) {
|
if (otg) {
|
||||||
// OTG mode is compatible with only very few options.
|
// OTG mode is compatible with only very few options.
|
||||||
// Only report obvious errors.
|
// Only report obvious errors.
|
||||||
|
@ -6,8 +6,19 @@
|
|||||||
|
|
||||||
#define SC_CONTROL_MSG_QUEUE_MAX 64
|
#define SC_CONTROL_MSG_QUEUE_MAX 64
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_controller_receiver_on_error(struct sc_receiver *receiver, void *userdata) {
|
||||||
|
(void) receiver;
|
||||||
|
|
||||||
|
struct sc_controller *controller = userdata;
|
||||||
|
// Forward the event to the controller listener
|
||||||
|
controller->cbs->on_error(controller, controller->cbs_userdata);
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket) {
|
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||||
|
const struct sc_controller_callbacks *cbs,
|
||||||
|
void *cbs_userdata) {
|
||||||
sc_vecdeque_init(&controller->queue);
|
sc_vecdeque_init(&controller->queue);
|
||||||
|
|
||||||
bool ok = sc_vecdeque_reserve(&controller->queue, SC_CONTROL_MSG_QUEUE_MAX);
|
bool ok = sc_vecdeque_reserve(&controller->queue, SC_CONTROL_MSG_QUEUE_MAX);
|
||||||
@ -15,7 +26,12 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_receiver_init(&controller->receiver, control_socket);
|
static const struct sc_receiver_callbacks receiver_cbs = {
|
||||||
|
.on_error = sc_controller_receiver_on_error,
|
||||||
|
};
|
||||||
|
|
||||||
|
ok = sc_receiver_init(&controller->receiver, control_socket, &receiver_cbs,
|
||||||
|
controller);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
sc_vecdeque_destroy(&controller->queue);
|
sc_vecdeque_destroy(&controller->queue);
|
||||||
return false;
|
return false;
|
||||||
@ -39,6 +55,10 @@ sc_controller_init(struct sc_controller *controller, sc_socket control_socket) {
|
|||||||
controller->control_socket = control_socket;
|
controller->control_socket = control_socket;
|
||||||
controller->stopped = false;
|
controller->stopped = false;
|
||||||
|
|
||||||
|
assert(cbs && cbs->on_error);
|
||||||
|
controller->cbs = cbs;
|
||||||
|
controller->cbs_userdata = cbs_userdata;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,10 +145,16 @@ run_controller(void *data) {
|
|||||||
sc_control_msg_destroy(&msg);
|
sc_control_msg_destroy(&msg);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGD("Could not write msg to socket");
|
LOGD("Could not write msg to socket");
|
||||||
break;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
error:
|
||||||
|
controller->cbs->on_error(controller, controller->cbs_userdata);
|
||||||
|
|
||||||
|
return 1; // ignored
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -22,10 +22,19 @@ struct sc_controller {
|
|||||||
bool stopped;
|
bool stopped;
|
||||||
struct sc_control_msg_queue queue;
|
struct sc_control_msg_queue queue;
|
||||||
struct sc_receiver receiver;
|
struct sc_receiver receiver;
|
||||||
|
|
||||||
|
const struct sc_controller_callbacks *cbs;
|
||||||
|
void *cbs_userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_controller_callbacks {
|
||||||
|
void (*on_error)(struct sc_controller *controller, void *userdata);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_controller_init(struct sc_controller *controller, sc_socket control_socket);
|
sc_controller_init(struct sc_controller *controller, sc_socket control_socket,
|
||||||
|
const struct sc_controller_callbacks *cbs,
|
||||||
|
void *cbs_userdata);
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_controller_configure(struct sc_controller *controller,
|
sc_controller_configure(struct sc_controller *controller,
|
||||||
|
@ -5,8 +5,30 @@
|
|||||||
|
|
||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
|
|
||||||
|
static bool
|
||||||
|
sc_display_init_novideo_icon(struct sc_display *display,
|
||||||
|
SDL_Surface *icon_novideo) {
|
||||||
|
assert(icon_novideo);
|
||||||
|
|
||||||
|
if (SDL_RenderSetLogicalSize(display->renderer,
|
||||||
|
icon_novideo->w, icon_novideo->h)) {
|
||||||
|
LOGW("Could not set renderer logical size: %s", SDL_GetError());
|
||||||
|
// don't fail
|
||||||
|
}
|
||||||
|
|
||||||
|
display->texture = SDL_CreateTextureFromSurface(display->renderer,
|
||||||
|
icon_novideo);
|
||||||
|
if (!display->texture) {
|
||||||
|
LOGE("Could not create texture: %s", SDL_GetError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
|
sc_display_init(struct sc_display *display, SDL_Window *window,
|
||||||
|
SDL_Surface *icon_novideo, bool mipmaps) {
|
||||||
display->renderer =
|
display->renderer =
|
||||||
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||||
if (!display->renderer) {
|
if (!display->renderer) {
|
||||||
@ -68,6 +90,18 @@ sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps) {
|
|||||||
display->pending.frame = NULL;
|
display->pending.frame = NULL;
|
||||||
display->has_frame = false;
|
display->has_frame = false;
|
||||||
|
|
||||||
|
if (icon_novideo) {
|
||||||
|
// Without video, set a static scrcpy icon as window content
|
||||||
|
bool ok = sc_display_init_novideo_icon(display, icon_novideo);
|
||||||
|
if (!ok) {
|
||||||
|
#ifdef SC_DISPLAY_FORCE_OPENGL_CORE_PROFILE
|
||||||
|
SDL_GL_DeleteContext(display->gl_context);
|
||||||
|
#endif
|
||||||
|
SDL_DestroyRenderer(display->renderer);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +44,8 @@ enum sc_display_result {
|
|||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_display_init(struct sc_display *display, SDL_Window *window, bool mipmaps);
|
sc_display_init(struct sc_display *display, SDL_Window *window,
|
||||||
|
SDL_Surface *icon_novideo, bool mipmaps);
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_display_destroy(struct sc_display *display);
|
sc_display_destroy(struct sc_display *display);
|
||||||
|
@ -7,3 +7,4 @@
|
|||||||
#define SC_EVENT_RECORDER_ERROR (SDL_USEREVENT + 6)
|
#define SC_EVENT_RECORDER_ERROR (SDL_USEREVENT + 6)
|
||||||
#define SC_EVENT_SCREEN_INIT_SIZE (SDL_USEREVENT + 7)
|
#define SC_EVENT_SCREEN_INIT_SIZE (SDL_USEREVENT + 7)
|
||||||
#define SC_EVENT_TIME_LIMIT_REACHED (SDL_USEREVENT + 8)
|
#define SC_EVENT_TIME_LIMIT_REACHED (SDL_USEREVENT + 8)
|
||||||
|
#define SC_EVENT_CONTROLLER_ERROR (SDL_USEREVENT + 9)
|
||||||
|
@ -78,7 +78,10 @@ decode_image(const char *path) {
|
|||||||
goto close_input;
|
goto close_input;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stream = av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
|
const AVCodec *codec;
|
||||||
|
|
||||||
|
int stream =
|
||||||
|
av_find_best_stream(ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
|
||||||
if (stream < 0 ) {
|
if (stream < 0 ) {
|
||||||
LOGE("Could not find best image stream");
|
LOGE("Could not find best image stream");
|
||||||
goto close_input;
|
goto close_input;
|
||||||
@ -86,12 +89,6 @@ decode_image(const char *path) {
|
|||||||
|
|
||||||
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
AVCodecParameters *params = ctx->streams[stream]->codecpar;
|
||||||
|
|
||||||
const AVCodec *codec = avcodec_find_decoder(params->codec_id);
|
|
||||||
if (!codec) {
|
|
||||||
LOGE("Could not find image decoder");
|
|
||||||
goto close_input;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
|
||||||
if (!codec_ctx) {
|
if (!codec_ctx) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
|
@ -403,6 +403,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
// controller is NULL if --no-control is requested
|
// controller is NULL if --no-control is requested
|
||||||
bool control = im->controller;
|
bool control = im->controller;
|
||||||
bool paused = im->screen->paused;
|
bool paused = im->screen->paused;
|
||||||
|
bool video = im->screen->video;
|
||||||
|
|
||||||
SDL_Keycode keycode = event->keysym.sym;
|
SDL_Keycode keycode = event->keysym.sym;
|
||||||
uint16_t mod = event->keysym.mod;
|
uint16_t mod = event->keysym.mod;
|
||||||
@ -462,13 +463,13 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_z:
|
case SDLK_z:
|
||||||
if (down && !repeat) {
|
if (video && down && !repeat) {
|
||||||
sc_screen_set_paused(im->screen, !shift);
|
sc_screen_set_paused(im->screen, !shift);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_DOWN:
|
case SDLK_DOWN:
|
||||||
if (shift) {
|
if (shift) {
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_180);
|
SC_ORIENTATION_FLIP_180);
|
||||||
}
|
}
|
||||||
@ -479,7 +480,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
return;
|
return;
|
||||||
case SDLK_UP:
|
case SDLK_UP:
|
||||||
if (shift) {
|
if (shift) {
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_180);
|
SC_ORIENTATION_FLIP_180);
|
||||||
}
|
}
|
||||||
@ -489,7 +490,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_LEFT:
|
case SDLK_LEFT:
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_0);
|
SC_ORIENTATION_FLIP_0);
|
||||||
@ -500,7 +501,7 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_RIGHT:
|
case SDLK_RIGHT:
|
||||||
if (!repeat && down) {
|
if (video && !repeat && down) {
|
||||||
if (shift) {
|
if (shift) {
|
||||||
apply_orientation_transform(im,
|
apply_orientation_transform(im,
|
||||||
SC_ORIENTATION_FLIP_0);
|
SC_ORIENTATION_FLIP_0);
|
||||||
@ -533,22 +534,22 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_f:
|
case SDLK_f:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
sc_screen_switch_fullscreen(im->screen);
|
sc_screen_switch_fullscreen(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_w:
|
case SDLK_w:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
sc_screen_resize_to_fit(im->screen);
|
sc_screen_resize_to_fit(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_g:
|
case SDLK_g:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
sc_screen_resize_to_pixel_perfect(im->screen);
|
sc_screen_resize_to_pixel_perfect(im->screen);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
case SDLK_i:
|
case SDLK_i:
|
||||||
if (!shift && !repeat && down) {
|
if (video && !shift && !repeat && down) {
|
||||||
switch_fps_counter_state(im);
|
switch_fps_counter_state(im);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -625,6 +626,23 @@ sc_input_manager_process_key(struct sc_input_manager *im,
|
|||||||
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
|
im->kp->ops->process_key(im->kp, &evt, ack_to_wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct sc_position
|
||||||
|
sc_input_manager_get_position(struct sc_input_manager *im, int32_t x,
|
||||||
|
int32_t y) {
|
||||||
|
if (im->mp->relative_mode) {
|
||||||
|
// No absolute position
|
||||||
|
return (struct sc_position) {
|
||||||
|
.screen_size = {0, 0},
|
||||||
|
.point = {0, 0},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return (struct sc_position) {
|
||||||
|
.screen_size = im->screen->frame_size,
|
||||||
|
.point = sc_screen_convert_window_to_frame_coords(im->screen, x, y),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
||||||
const SDL_MouseMotionEvent *event) {
|
const SDL_MouseMotionEvent *event) {
|
||||||
@ -634,12 +652,7 @@ sc_input_manager_process_mouse_motion(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct sc_mouse_motion_event evt = {
|
struct sc_mouse_motion_event evt = {
|
||||||
.position = {
|
.position = sc_input_manager_get_position(im, event->x, event->y),
|
||||||
.screen_size = im->screen->frame_size,
|
|
||||||
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
||||||
event->x,
|
|
||||||
event->y),
|
|
||||||
},
|
|
||||||
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
||||||
: POINTER_ID_GENERIC_FINGER,
|
: POINTER_ID_GENERIC_FINGER,
|
||||||
.xrel = event->xrel,
|
.xrel = event->xrel,
|
||||||
@ -735,7 +748,8 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// double-click on black borders resize to fit the device screen
|
// double-click on black borders resize to fit the device screen
|
||||||
if (event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
bool video = im->screen->video;
|
||||||
|
if (video && event->button == SDL_BUTTON_LEFT && event->clicks == 2) {
|
||||||
int32_t x = event->x;
|
int32_t x = event->x;
|
||||||
int32_t y = event->y;
|
int32_t y = event->y;
|
||||||
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
|
sc_screen_hidpi_scale_coords(im->screen, &x, &y);
|
||||||
@ -759,12 +773,7 @@ sc_input_manager_process_mouse_button(struct sc_input_manager *im,
|
|||||||
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
uint32_t sdl_buttons_state = SDL_GetMouseState(NULL, NULL);
|
||||||
|
|
||||||
struct sc_mouse_click_event evt = {
|
struct sc_mouse_click_event evt = {
|
||||||
.position = {
|
.position = sc_input_manager_get_position(im, event->x, event->y),
|
||||||
.screen_size = im->screen->frame_size,
|
|
||||||
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
||||||
event->x,
|
|
||||||
event->y),
|
|
||||||
},
|
|
||||||
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
.action = sc_action_from_sdl_mousebutton_type(event->type),
|
||||||
.button = sc_mouse_button_from_sdl(event->button),
|
.button = sc_mouse_button_from_sdl(event->button),
|
||||||
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
.pointer_id = im->forward_all_clicks ? POINTER_ID_MOUSE
|
||||||
@ -839,11 +848,7 @@ sc_input_manager_process_mouse_wheel(struct sc_input_manager *im,
|
|||||||
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
uint32_t buttons = SDL_GetMouseState(&mouse_x, &mouse_y);
|
||||||
|
|
||||||
struct sc_mouse_scroll_event evt = {
|
struct sc_mouse_scroll_event evt = {
|
||||||
.position = {
|
.position = sc_input_manager_get_position(im, mouse_x, mouse_y),
|
||||||
.screen_size = im->screen->frame_size,
|
|
||||||
.point = sc_screen_convert_window_to_frame_coords(im->screen,
|
|
||||||
mouse_x, mouse_y),
|
|
||||||
},
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||||
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
|
.hscroll = CLAMP(event->preciseX, -1.0f, 1.0f),
|
||||||
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
|
.vscroll = CLAMP(event->preciseY, -1.0f, 1.0f),
|
||||||
|
@ -89,6 +89,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
|||||||
.kill_adb_on_close = false,
|
.kill_adb_on_close = false,
|
||||||
.camera_high_speed = false,
|
.camera_high_speed = false,
|
||||||
.list = 0,
|
.list = 0,
|
||||||
|
.window = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sc_orientation
|
enum sc_orientation
|
||||||
|
@ -279,6 +279,7 @@ struct scrcpy_options {
|
|||||||
#define SC_OPTION_LIST_CAMERAS 0x4
|
#define SC_OPTION_LIST_CAMERAS 0x4
|
||||||
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
|
#define SC_OPTION_LIST_CAMERA_SIZES 0x8
|
||||||
uint8_t list;
|
uint8_t list;
|
||||||
|
bool window;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct scrcpy_options scrcpy_options_default;
|
extern const struct scrcpy_options scrcpy_options_default;
|
||||||
|
@ -10,7 +10,8 @@
|
|||||||
#include "util/str.h"
|
#include "util/str.h"
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket) {
|
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
|
||||||
|
const struct sc_receiver_callbacks *cbs, void *cbs_userdata) {
|
||||||
bool ok = sc_mutex_init(&receiver->mutex);
|
bool ok = sc_mutex_init(&receiver->mutex);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
return false;
|
return false;
|
||||||
@ -20,6 +21,10 @@ sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket) {
|
|||||||
receiver->acksync = NULL;
|
receiver->acksync = NULL;
|
||||||
receiver->uhid_devices = NULL;
|
receiver->uhid_devices = NULL;
|
||||||
|
|
||||||
|
assert(cbs && cbs->on_error);
|
||||||
|
receiver->cbs = cbs;
|
||||||
|
receiver->cbs_userdata = cbs_userdata;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,6 +157,8 @@ run_receiver(void *data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
receiver->cbs->on_error(receiver, receiver->cbs_userdata);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,10 +19,18 @@ struct sc_receiver {
|
|||||||
|
|
||||||
struct sc_acksync *acksync;
|
struct sc_acksync *acksync;
|
||||||
struct sc_uhid_devices *uhid_devices;
|
struct sc_uhid_devices *uhid_devices;
|
||||||
|
|
||||||
|
const struct sc_receiver_callbacks *cbs;
|
||||||
|
void *cbs_userdata;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sc_receiver_callbacks {
|
||||||
|
void (*on_error)(struct sc_receiver *receiver, void *userdata);
|
||||||
};
|
};
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket);
|
sc_receiver_init(struct sc_receiver *receiver, sc_socket control_socket,
|
||||||
|
const struct sc_receiver_callbacks *cbs, void *cbs_userdata);
|
||||||
|
|
||||||
void
|
void
|
||||||
sc_receiver_destroy(struct sc_receiver *receiver);
|
sc_receiver_destroy(struct sc_receiver *receiver);
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
#include "util/log.h"
|
#include "util/log.h"
|
||||||
#include "util/str.h"
|
#include "util/str.h"
|
||||||
|
|
||||||
#define RTMP_URL "rtmp://localhost/live/stream"
|
|
||||||
|
|
||||||
/** Downcast packet sinks to recorder */
|
/** Downcast packet sinks to recorder */
|
||||||
#define DOWNCAST_VIDEO(SINK) \
|
#define DOWNCAST_VIDEO(SINK) \
|
||||||
container_of(SINK, struct sc_recorder, video_packet_sink)
|
container_of(SINK, struct sc_recorder, video_packet_sink)
|
||||||
@ -133,7 +131,7 @@ static bool
|
|||||||
sc_recorder_open_output_file(struct sc_recorder *recorder) {
|
sc_recorder_open_output_file(struct sc_recorder *recorder) {
|
||||||
const char *format_name = sc_recorder_get_format_name(recorder->format);
|
const char *format_name = sc_recorder_get_format_name(recorder->format);
|
||||||
assert(format_name);
|
assert(format_name);
|
||||||
const AVOutputFormat *format = find_muxer("flv");
|
const AVOutputFormat *format = find_muxer(format_name);
|
||||||
if (!format) {
|
if (!format) {
|
||||||
LOGE("Could not find muxer");
|
LOGE("Could not find muxer");
|
||||||
return false;
|
return false;
|
||||||
@ -145,11 +143,8 @@ sc_recorder_open_output_file(struct sc_recorder *recorder) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
AVDictionary *options = NULL;
|
int ret = avio_open(&recorder->ctx->pb, recorder->filename,
|
||||||
av_dict_set(&options, "listen", "1", 0); // Enable listening
|
AVIO_FLAG_WRITE);
|
||||||
|
|
||||||
int ret = avio_open2(&recorder->ctx->pb, RTMP_URL,
|
|
||||||
AVIO_FLAG_WRITE, NULL, &options);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
LOGE("Failed to open output file: %s", recorder->filename);
|
LOGE("Failed to open output file: %s", recorder->filename);
|
||||||
avformat_free_context(recorder->ctx);
|
avformat_free_context(recorder->ctx);
|
||||||
|
@ -174,6 +174,9 @@ event_loop(struct scrcpy *s) {
|
|||||||
case SC_EVENT_DEMUXER_ERROR:
|
case SC_EVENT_DEMUXER_ERROR:
|
||||||
LOGE("Demuxer error");
|
LOGE("Demuxer error");
|
||||||
return SCRCPY_EXIT_FAILURE;
|
return SCRCPY_EXIT_FAILURE;
|
||||||
|
case SC_EVENT_CONTROLLER_ERROR:
|
||||||
|
LOGE("Controller error");
|
||||||
|
return SCRCPY_EXIT_FAILURE;
|
||||||
case SC_EVENT_RECORDER_ERROR:
|
case SC_EVENT_RECORDER_ERROR:
|
||||||
LOGE("Recorder error");
|
LOGE("Recorder error");
|
||||||
return SCRCPY_EXIT_FAILURE;
|
return SCRCPY_EXIT_FAILURE;
|
||||||
@ -265,6 +268,16 @@ sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_controller_on_error(struct sc_controller *controller, void *userdata) {
|
||||||
|
// Note: this function may be called twice, once from the controller thread
|
||||||
|
// and once from the receiver thread
|
||||||
|
(void) controller;
|
||||||
|
(void) userdata;
|
||||||
|
|
||||||
|
PUSH_EVENT(SC_EVENT_CONTROLLER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sc_server_on_connection_failed(struct sc_server *server, void *userdata) {
|
sc_server_on_connection_failed(struct sc_server *server, void *userdata) {
|
||||||
(void) server;
|
(void) server;
|
||||||
@ -408,7 +421,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
return SCRCPY_EXIT_FAILURE;
|
return SCRCPY_EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options->video_playback) {
|
if (options->window) {
|
||||||
// Set hints before starting the server thread to avoid race conditions
|
// Set hints before starting the server thread to avoid race conditions
|
||||||
// in SDL
|
// in SDL
|
||||||
sdl_set_hints(options->render_driver);
|
sdl_set_hints(options->render_driver);
|
||||||
@ -430,7 +443,7 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
assert(!options->video_playback || options->video);
|
assert(!options->video_playback || options->video);
|
||||||
assert(!options->audio_playback || options->audio);
|
assert(!options->audio_playback || options->audio);
|
||||||
|
|
||||||
if (options->video_playback ||
|
if (options->window ||
|
||||||
(options->control && options->clipboard_autosync)) {
|
(options->control && options->clipboard_autosync)) {
|
||||||
// Initialize the video subsystem even if --no-video or
|
// Initialize the video subsystem even if --no-video or
|
||||||
// --no-video-playback is passed so that clipboard synchronization
|
// --no-video-playback is passed so that clipboard synchronization
|
||||||
@ -553,7 +566,12 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
struct sc_mouse_processor *mp = NULL;
|
struct sc_mouse_processor *mp = NULL;
|
||||||
|
|
||||||
if (options->control) {
|
if (options->control) {
|
||||||
if (!sc_controller_init(&s->controller, s->server.control_socket)) {
|
static const struct sc_controller_callbacks controller_cbs = {
|
||||||
|
.on_error = sc_controller_on_error,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!sc_controller_init(&s->controller, s->server.control_socket,
|
||||||
|
&controller_cbs, NULL)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
controller_initialized = true;
|
controller_initialized = true;
|
||||||
@ -684,11 +702,12 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
// There is a controller if and only if control is enabled
|
// There is a controller if and only if control is enabled
|
||||||
assert(options->control == !!controller);
|
assert(options->control == !!controller);
|
||||||
|
|
||||||
if (options->video_playback) {
|
if (options->window) {
|
||||||
const char *window_title =
|
const char *window_title =
|
||||||
options->window_title ? options->window_title : info->device_name;
|
options->window_title ? options->window_title : info->device_name;
|
||||||
|
|
||||||
struct sc_screen_params screen_params = {
|
struct sc_screen_params screen_params = {
|
||||||
|
.video = options->video_playback,
|
||||||
.controller = controller,
|
.controller = controller,
|
||||||
.fp = fp,
|
.fp = fp,
|
||||||
.kp = kp,
|
.kp = kp,
|
||||||
@ -710,21 +729,26 @@ scrcpy(struct scrcpy_options *options) {
|
|||||||
.start_fps_counter = options->start_fps_counter,
|
.start_fps_counter = options->start_fps_counter,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_frame_source *src = &s->video_decoder.frame_source;
|
struct sc_frame_source *src;
|
||||||
|
if (options->video_playback) {
|
||||||
|
src = &s->video_decoder.frame_source;
|
||||||
if (options->display_buffer) {
|
if (options->display_buffer) {
|
||||||
sc_delay_buffer_init(&s->display_buffer, options->display_buffer,
|
sc_delay_buffer_init(&s->display_buffer,
|
||||||
true);
|
options->display_buffer, true);
|
||||||
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
|
sc_frame_source_add_sink(src, &s->display_buffer.frame_sink);
|
||||||
src = &s->display_buffer.frame_source;
|
src = &s->display_buffer.frame_source;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!sc_screen_init(&s->screen, &screen_params)) {
|
if (!sc_screen_init(&s->screen, &screen_params)) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
screen_initialized = true;
|
screen_initialized = true;
|
||||||
|
|
||||||
|
if (options->video_playback) {
|
||||||
sc_frame_source_add_sink(src, &s->screen.frame_sink);
|
sc_frame_source_add_sink(src, &s->screen.frame_sink);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options->audio_playback) {
|
if (options->audio_playback) {
|
||||||
sc_audio_player_init(&s->audio_player, options->audio_buffer,
|
sc_audio_player_init(&s->audio_player, options->audio_buffer,
|
||||||
|
113
app/src/screen.c
113
app/src/screen.c
@ -205,6 +205,8 @@ sc_screen_toggle_mouse_capture(struct sc_screen *screen) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
sc_screen_update_content_rect(struct sc_screen *screen) {
|
sc_screen_update_content_rect(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
int dw;
|
int dw;
|
||||||
int dh;
|
int dh;
|
||||||
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
SDL_GL_GetDrawableSize(screen->window, &dw, &dh);
|
||||||
@ -246,6 +248,8 @@ sc_screen_update_content_rect(struct sc_screen *screen) {
|
|||||||
// changed, so that the content rectangle is recomputed
|
// changed, so that the content rectangle is recomputed
|
||||||
static void
|
static void
|
||||||
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (update_content_rect) {
|
if (update_content_rect) {
|
||||||
sc_screen_update_content_rect(screen);
|
sc_screen_update_content_rect(screen);
|
||||||
}
|
}
|
||||||
@ -255,6 +259,13 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
|||||||
(void) res; // any error already logged
|
(void) res; // any error already logged
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sc_screen_render_novideo(struct sc_screen *screen) {
|
||||||
|
enum sc_display_result res =
|
||||||
|
sc_display_render(&screen->display, NULL, SC_ORIENTATION_0);
|
||||||
|
(void) res; // any error already logged
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__WINDOWS__)
|
#if defined(__APPLE__) || defined(__WINDOWS__)
|
||||||
# define CONTINUOUS_RESIZING_WORKAROUND
|
# define CONTINUOUS_RESIZING_WORKAROUND
|
||||||
#endif
|
#endif
|
||||||
@ -268,6 +279,8 @@ sc_screen_render(struct sc_screen *screen, bool update_content_rect) {
|
|||||||
static int
|
static int
|
||||||
event_watcher(void *data, SDL_Event *event) {
|
event_watcher(void *data, SDL_Event *event) {
|
||||||
struct sc_screen *screen = data;
|
struct sc_screen *screen = data;
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (event->type == SDL_WINDOWEVENT
|
if (event->type == SDL_WINDOWEVENT
|
||||||
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
&& event->window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
// In practice, it seems to always be called from the same thread in
|
// In practice, it seems to always be called from the same thread in
|
||||||
@ -326,6 +339,7 @@ sc_screen_frame_sink_close(struct sc_frame_sink *sink) {
|
|||||||
static bool
|
static bool
|
||||||
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
sc_screen_frame_sink_push(struct sc_frame_sink *sink, const AVFrame *frame) {
|
||||||
struct sc_screen *screen = DOWNCAST(sink);
|
struct sc_screen *screen = DOWNCAST(sink);
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
bool previous_skipped;
|
bool previous_skipped;
|
||||||
bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped);
|
bool ok = sc_frame_buffer_push(&screen->fb, frame, &previous_skipped);
|
||||||
@ -364,6 +378,9 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
screen->mouse_capture_key_pressed = 0;
|
screen->mouse_capture_key_pressed = 0;
|
||||||
screen->paused = false;
|
screen->paused = false;
|
||||||
screen->resume_frame = NULL;
|
screen->resume_frame = NULL;
|
||||||
|
screen->orientation = SC_ORIENTATION_0;
|
||||||
|
|
||||||
|
screen->video = params->video;
|
||||||
|
|
||||||
screen->req.x = params->window_x;
|
screen->req.x = params->window_x;
|
||||||
screen->req.y = params->window_y;
|
screen->req.y = params->window_y;
|
||||||
@ -381,41 +398,75 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
goto error_destroy_frame_buffer;
|
goto error_destroy_frame_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (screen->video) {
|
||||||
screen->orientation = params->orientation;
|
screen->orientation = params->orientation;
|
||||||
if (screen->orientation != SC_ORIENTATION_0) {
|
if (screen->orientation != SC_ORIENTATION_0) {
|
||||||
LOGI("Initial display orientation set to %s",
|
LOGI("Initial display orientation set to %s",
|
||||||
sc_orientation_get_name(screen->orientation));
|
sc_orientation_get_name(screen->orientation));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t window_flags = SDL_WINDOW_HIDDEN
|
uint32_t window_flags = SDL_WINDOW_ALLOW_HIGHDPI;
|
||||||
| SDL_WINDOW_RESIZABLE
|
|
||||||
| SDL_WINDOW_ALLOW_HIGHDPI;
|
|
||||||
if (params->always_on_top) {
|
if (params->always_on_top) {
|
||||||
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
window_flags |= SDL_WINDOW_ALWAYS_ON_TOP;
|
||||||
}
|
}
|
||||||
if (params->window_borderless) {
|
if (params->window_borderless) {
|
||||||
window_flags |= SDL_WINDOW_BORDERLESS;
|
window_flags |= SDL_WINDOW_BORDERLESS;
|
||||||
}
|
}
|
||||||
|
if (params->video) {
|
||||||
|
// The window will be shown on first frame
|
||||||
|
window_flags |= SDL_WINDOW_HIDDEN
|
||||||
|
| SDL_WINDOW_RESIZABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *title = params->window_title;
|
||||||
|
assert(title);
|
||||||
|
|
||||||
|
int x = SDL_WINDOWPOS_UNDEFINED;
|
||||||
|
int y = SDL_WINDOWPOS_UNDEFINED;
|
||||||
|
int width = 256;
|
||||||
|
int height = 256;
|
||||||
|
if (params->window_x != SC_WINDOW_POSITION_UNDEFINED) {
|
||||||
|
x = params->window_x;
|
||||||
|
}
|
||||||
|
if (params->window_y != SC_WINDOW_POSITION_UNDEFINED) {
|
||||||
|
y = params->window_y;
|
||||||
|
}
|
||||||
|
if (params->window_width) {
|
||||||
|
width = params->window_width;
|
||||||
|
}
|
||||||
|
if (params->window_height) {
|
||||||
|
height = params->window_height;
|
||||||
|
}
|
||||||
|
|
||||||
// The window will be positioned and sized on first video frame
|
// The window will be positioned and sized on first video frame
|
||||||
screen->window =
|
screen->window = SDL_CreateWindow(title, x, y, width, height, window_flags);
|
||||||
SDL_CreateWindow(params->window_title, 0, 0, 0, 0, window_flags);
|
|
||||||
if (!screen->window) {
|
if (!screen->window) {
|
||||||
LOGE("Could not create window: %s", SDL_GetError());
|
LOGE("Could not create window: %s", SDL_GetError());
|
||||||
goto error_destroy_fps_counter;
|
goto error_destroy_fps_counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_display_init(&screen->display, screen->window, params->mipmaps);
|
|
||||||
if (!ok) {
|
|
||||||
goto error_destroy_window;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Surface *icon = scrcpy_icon_load();
|
SDL_Surface *icon = scrcpy_icon_load();
|
||||||
if (icon) {
|
if (icon) {
|
||||||
SDL_SetWindowIcon(screen->window, icon);
|
SDL_SetWindowIcon(screen->window, icon);
|
||||||
scrcpy_icon_destroy(icon);
|
} else if (params->video) {
|
||||||
} else {
|
// just a warning
|
||||||
LOGW("Could not load icon");
|
LOGW("Could not load icon");
|
||||||
|
} else {
|
||||||
|
// without video, the icon is used as window content, it must be present
|
||||||
|
LOGE("Could not load icon");
|
||||||
|
goto error_destroy_fps_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Surface *icon_novideo = params->video ? NULL : icon;
|
||||||
|
bool mipmaps = params->video && params->mipmaps;
|
||||||
|
ok = sc_display_init(&screen->display, screen->window, icon_novideo,
|
||||||
|
mipmaps);
|
||||||
|
if (icon) {
|
||||||
|
scrcpy_icon_destroy(icon);
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
goto error_destroy_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
screen->frame = av_frame_alloc();
|
screen->frame = av_frame_alloc();
|
||||||
@ -439,7 +490,9 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
sc_input_manager_init(&screen->im, &im_params);
|
sc_input_manager_init(&screen->im, &im_params);
|
||||||
|
|
||||||
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
#ifdef CONTINUOUS_RESIZING_WORKAROUND
|
||||||
|
if (screen->video) {
|
||||||
SDL_AddEventWatch(event_watcher, screen);
|
SDL_AddEventWatch(event_watcher, screen);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct sc_frame_sink_ops ops = {
|
static const struct sc_frame_sink_ops ops = {
|
||||||
@ -454,6 +507,11 @@ sc_screen_init(struct sc_screen *screen,
|
|||||||
screen->open = false;
|
screen->open = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (!screen->video && sc_screen_is_relative_mode(screen)) {
|
||||||
|
// Capture mouse immediately if video mirroring is disabled
|
||||||
|
sc_screen_set_mouse_capture(screen, true);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
error_destroy_display:
|
error_destroy_display:
|
||||||
@ -524,6 +582,8 @@ sc_screen_destroy(struct sc_screen *screen) {
|
|||||||
static void
|
static void
|
||||||
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
||||||
struct sc_size new_content_size) {
|
struct sc_size new_content_size) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
struct sc_size window_size = get_window_size(screen);
|
struct sc_size window_size = get_window_size(screen);
|
||||||
struct sc_size target_size = {
|
struct sc_size target_size = {
|
||||||
.width = (uint32_t) window_size.width * new_content_size.width
|
.width = (uint32_t) window_size.width * new_content_size.width
|
||||||
@ -537,6 +597,8 @@ resize_for_content(struct sc_screen *screen, struct sc_size old_content_size,
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
|
if (!screen->fullscreen && !screen->maximized && !screen->minimized) {
|
||||||
resize_for_content(screen, screen->content_size, new_content_size);
|
resize_for_content(screen, screen->content_size, new_content_size);
|
||||||
} else if (!screen->resize_pending) {
|
} else if (!screen->resize_pending) {
|
||||||
@ -551,6 +613,8 @@ set_content_size(struct sc_screen *screen, struct sc_size new_content_size) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
apply_pending_resize(struct sc_screen *screen) {
|
apply_pending_resize(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
assert(!screen->fullscreen);
|
assert(!screen->fullscreen);
|
||||||
assert(!screen->maximized);
|
assert(!screen->maximized);
|
||||||
assert(!screen->minimized);
|
assert(!screen->minimized);
|
||||||
@ -564,6 +628,8 @@ apply_pending_resize(struct sc_screen *screen) {
|
|||||||
void
|
void
|
||||||
sc_screen_set_orientation(struct sc_screen *screen,
|
sc_screen_set_orientation(struct sc_screen *screen,
|
||||||
enum sc_orientation orientation) {
|
enum sc_orientation orientation) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (orientation == screen->orientation) {
|
if (orientation == screen->orientation) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -598,6 +664,8 @@ sc_screen_init_size(struct sc_screen *screen) {
|
|||||||
// recreate the texture and resize the window if the frame size has changed
|
// recreate the texture and resize the window if the frame size has changed
|
||||||
static enum sc_display_result
|
static enum sc_display_result
|
||||||
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->frame_size.width == new_frame_size.width
|
if (screen->frame_size.width == new_frame_size.width
|
||||||
&& screen->frame_size.height == new_frame_size.height) {
|
&& screen->frame_size.height == new_frame_size.height) {
|
||||||
return SC_DISPLAY_RESULT_OK;
|
return SC_DISPLAY_RESULT_OK;
|
||||||
@ -617,6 +685,8 @@ prepare_for_frame(struct sc_screen *screen, struct sc_size new_frame_size) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
sc_screen_apply_frame(struct sc_screen *screen) {
|
sc_screen_apply_frame(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
|
sc_fps_counter_add_rendered_frame(&screen->fps_counter);
|
||||||
|
|
||||||
AVFrame *frame = screen->frame;
|
AVFrame *frame = screen->frame;
|
||||||
@ -656,6 +726,8 @@ sc_screen_apply_frame(struct sc_screen *screen) {
|
|||||||
|
|
||||||
static bool
|
static bool
|
||||||
sc_screen_update_frame(struct sc_screen *screen) {
|
sc_screen_update_frame(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->paused) {
|
if (screen->paused) {
|
||||||
if (!screen->resume_frame) {
|
if (!screen->resume_frame) {
|
||||||
screen->resume_frame = av_frame_alloc();
|
screen->resume_frame = av_frame_alloc();
|
||||||
@ -677,6 +749,8 @@ sc_screen_update_frame(struct sc_screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_set_paused(struct sc_screen *screen, bool paused) {
|
sc_screen_set_paused(struct sc_screen *screen, bool paused) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (!paused && !screen->paused) {
|
if (!paused && !screen->paused) {
|
||||||
// nothing to do
|
// nothing to do
|
||||||
return;
|
return;
|
||||||
@ -704,6 +778,8 @@ sc_screen_set_paused(struct sc_screen *screen, bool paused) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
uint32_t new_mode = screen->fullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP;
|
||||||
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
if (SDL_SetWindowFullscreen(screen->window, new_mode)) {
|
||||||
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
LOGW("Could not switch fullscreen mode: %s", SDL_GetError());
|
||||||
@ -721,6 +797,8 @@ sc_screen_switch_fullscreen(struct sc_screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_resize_to_fit(struct sc_screen *screen) {
|
sc_screen_resize_to_fit(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->fullscreen || screen->maximized || screen->minimized) {
|
if (screen->fullscreen || screen->maximized || screen->minimized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -745,6 +823,8 @@ sc_screen_resize_to_fit(struct sc_screen *screen) {
|
|||||||
|
|
||||||
void
|
void
|
||||||
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
sc_screen_resize_to_pixel_perfect(struct sc_screen *screen) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
if (screen->fullscreen || screen->minimized) {
|
if (screen->fullscreen || screen->minimized) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -788,6 +868,13 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_WINDOWEVENT:
|
case SDL_WINDOWEVENT:
|
||||||
|
if (!screen->video
|
||||||
|
&& event->window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||||
|
sc_screen_render_novideo(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// !video implies !has_frame
|
||||||
|
assert(screen->video || !screen->has_frame);
|
||||||
if (!screen->has_frame) {
|
if (!screen->has_frame) {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
return true;
|
return true;
|
||||||
@ -891,6 +978,8 @@ sc_screen_handle_event(struct sc_screen *screen, const SDL_Event *event) {
|
|||||||
struct sc_point
|
struct sc_point
|
||||||
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
sc_screen_convert_drawable_to_frame_coords(struct sc_screen *screen,
|
||||||
int32_t x, int32_t y) {
|
int32_t x, int32_t y) {
|
||||||
|
assert(screen->video);
|
||||||
|
|
||||||
enum sc_orientation orientation = screen->orientation;
|
enum sc_orientation orientation = screen->orientation;
|
||||||
|
|
||||||
int32_t w = screen->content_size.width;
|
int32_t w = screen->content_size.width;
|
||||||
|
@ -26,6 +26,8 @@ struct sc_screen {
|
|||||||
bool open; // track the open/close state to assert correct behavior
|
bool open; // track the open/close state to assert correct behavior
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
bool video;
|
||||||
|
|
||||||
struct sc_display display;
|
struct sc_display display;
|
||||||
struct sc_input_manager im;
|
struct sc_input_manager im;
|
||||||
struct sc_frame_buffer fb;
|
struct sc_frame_buffer fb;
|
||||||
@ -70,6 +72,8 @@ struct sc_screen {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct sc_screen_params {
|
struct sc_screen_params {
|
||||||
|
bool video;
|
||||||
|
|
||||||
struct sc_controller *controller;
|
struct sc_controller *controller;
|
||||||
struct sc_file_pusher *fp;
|
struct sc_file_pusher *fp;
|
||||||
struct sc_key_processor *kp;
|
struct sc_key_processor *kp;
|
||||||
|
@ -176,6 +176,8 @@ sc_process_execute_p(const char *const argv[], HANDLE *handle, unsigned flags,
|
|||||||
free(lpAttributeList);
|
free(lpAttributeList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
|
||||||
// These handles are used by the child process, close them for this process
|
// These handles are used by the child process, close them for this process
|
||||||
if (pin) {
|
if (pin) {
|
||||||
CloseHandle(stdin_read_handle);
|
CloseHandle(stdin_read_handle);
|
||||||
|
@ -46,6 +46,9 @@ sc_audiobuf_read(struct sc_audiobuf *buf, void *to_, uint32_t samples_count) {
|
|||||||
uint32_t head = atomic_load_explicit(&buf->head, memory_order_acquire);
|
uint32_t head = atomic_load_explicit(&buf->head, memory_order_acquire);
|
||||||
|
|
||||||
uint32_t can_read = (buf->alloc_size + head - tail) % buf->alloc_size;
|
uint32_t can_read = (buf->alloc_size + head - tail) % buf->alloc_size;
|
||||||
|
if (!can_read) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (samples_count > can_read) {
|
if (samples_count > can_read) {
|
||||||
samples_count = can_read;
|
samples_count = can_read;
|
||||||
}
|
}
|
||||||
@ -86,6 +89,9 @@ sc_audiobuf_write(struct sc_audiobuf *buf, const void *from_,
|
|||||||
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
uint32_t tail = atomic_load_explicit(&buf->tail, memory_order_acquire);
|
||||||
|
|
||||||
uint32_t can_write = (buf->alloc_size + tail - head - 1) % buf->alloc_size;
|
uint32_t can_write = (buf->alloc_size + tail - head - 1) % buf->alloc_size;
|
||||||
|
if (!can_write) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (samples_count > can_write) {
|
if (samples_count > can_write) {
|
||||||
samples_count = can_write;
|
samples_count = can_write;
|
||||||
}
|
}
|
||||||
|
11
doc/audio.md
11
doc/audio.md
@ -28,10 +28,17 @@ To disable only the audio playback, see [no playback](video.md#no-playback).
|
|||||||
|
|
||||||
## Audio only
|
## Audio only
|
||||||
|
|
||||||
To play audio only, disable the video:
|
To play audio only, disable video and control:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --no-video
|
scrcpy --no-video --no-control
|
||||||
|
```
|
||||||
|
|
||||||
|
To play audio without a window:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# --no-video and --no-control are implied by --no-window
|
||||||
|
scrcpy --no-window
|
||||||
# interrupt with Ctrl+C
|
# interrupt with Ctrl+C
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -15,6 +15,31 @@ scrcpy -n # short version
|
|||||||
Read [keyboard](keyboard.md) and [mouse](mouse.md).
|
Read [keyboard](keyboard.md) and [mouse](mouse.md).
|
||||||
|
|
||||||
|
|
||||||
|
## Control only
|
||||||
|
|
||||||
|
To control the device without mirroring:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-video --no-audio
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, mouse mode is switched to UHID if video mirroring is disabled (a
|
||||||
|
relative mouse mode is required).
|
||||||
|
|
||||||
|
To also use a UHID keyboard, set it explicitly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-video --no-audio --keyboard=uhid
|
||||||
|
scrcpy --no-video --no-audio -K # short version
|
||||||
|
```
|
||||||
|
|
||||||
|
To use AOA instead (over USB only):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoa
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## Copy-paste
|
## Copy-paste
|
||||||
|
|
||||||
Any time the Android clipboard changes, it is automatically synchronized to the
|
Any time the Android clipboard changes, it is automatically synchronized to the
|
||||||
|
43
doc/otg.md
43
doc/otg.md
@ -1,19 +1,21 @@
|
|||||||
# OTG
|
# OTG
|
||||||
|
|
||||||
By default, _scrcpy_ injects input events at the Android API level. As an
|
By default, _scrcpy_ injects input events at the Android API level. As an
|
||||||
alternative, when connected over USB, it is possible to send HID events, so that
|
alternative, it is possible to send HID events, so that scrcpy behaves as if it
|
||||||
scrcpy behaves as if it was a physical keyboard and/or mouse connected to the
|
was a [physical keyboard] and/or a [physical mouse] connected to the Android
|
||||||
Android device.
|
device (see [keyboard](keyboard.md) and [mouse](mouse.md)).
|
||||||
|
|
||||||
A special mode allows to control the device without mirroring, using AOA
|
[physical keyboard]: keyboard.md#physical-keyboard-simulation
|
||||||
[keyboard](keyboard.md#aoa) and [mouse](mouse.md#aoa). Therefore, it is possible
|
[physical mouse]: physical-keyboard-simulation
|
||||||
to run _scrcpy_ with only physical keyboard and mouse simulation (HID), as if
|
|
||||||
the computer keyboard and mouse were plugged directly to the device via an OTG
|
|
||||||
cable.
|
|
||||||
|
|
||||||
In this mode, `adb` (USB debugging) is not necessary, and mirroring is disabled.
|
A special mode (OTG) allows to control the device using AOA
|
||||||
|
[keyboard](keyboard.md#aoa) and [mouse](mouse.md#aoa), without using _adb_ at
|
||||||
|
all (so USB debugging is not necessary). In this mode, video and audio are
|
||||||
|
disabled, and `--keyboard=aoa and `--mouse=aoa` are implicitly set.
|
||||||
|
|
||||||
This is similar to `--keyboard=aoa --mouse=aoa`, but without mirroring.
|
Therefore, it is possible to run _scrcpy_ with only physical keyboard and mouse
|
||||||
|
simulation, as if the computer keyboard and mouse were plugged directly to the
|
||||||
|
device via an OTG cable.
|
||||||
|
|
||||||
To enable OTG mode:
|
To enable OTG mode:
|
||||||
|
|
||||||
@ -23,7 +25,7 @@ scrcpy --otg
|
|||||||
scrcpy --otg -s 0123456789abcdef
|
scrcpy --otg -s 0123456789abcdef
|
||||||
```
|
```
|
||||||
|
|
||||||
It is possible to disable HID keyboard or HID mouse:
|
It is possible to disable keyboard or mouse:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --otg --keyboard=disabled
|
scrcpy --otg --keyboard=disabled
|
||||||
@ -35,3 +37,22 @@ It only works if the device is connected over USB.
|
|||||||
## OTG issues on Windows
|
## OTG issues on Windows
|
||||||
|
|
||||||
See [FAQ](/FAQ.md#otg-issues-on-windows).
|
See [FAQ](/FAQ.md#otg-issues-on-windows).
|
||||||
|
|
||||||
|
|
||||||
|
## Control only
|
||||||
|
|
||||||
|
Note that the purpose of OTG is to control the device without USB debugging
|
||||||
|
(adb).
|
||||||
|
|
||||||
|
If you want to solely control the device without mirroring while USB debugging
|
||||||
|
is enabled, then OTG mode is not necessary.
|
||||||
|
|
||||||
|
Instead, disable video and audio, and select UHID (or AOA):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-video --no-audio --keyboard=uhid --mouse=uhid
|
||||||
|
scrcpy --no-video --no-audio -KM # short version
|
||||||
|
scrcpy --no-video --no-audio --keyboard=aoa --mouse=aoa
|
||||||
|
```
|
||||||
|
|
||||||
|
One benefit of UHID is that it also works wirelessly.
|
||||||
|
@ -58,12 +58,10 @@ orientation](video.md#orientation).
|
|||||||
|
|
||||||
## No playback
|
## No playback
|
||||||
|
|
||||||
To disable playback while recording:
|
To disable playback and control while recording:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
scrcpy --no-playback --record=file.mp4
|
scrcpy --no-playback --no-control --record=file.mp4
|
||||||
scrcpy -Nr file.mkv
|
|
||||||
# interrupt recording with Ctrl+C
|
|
||||||
```
|
```
|
||||||
|
|
||||||
It is also possible to disable video and audio playback separately:
|
It is also possible to disable video and audio playback separately:
|
||||||
@ -73,6 +71,13 @@ It is also possible to disable video and audio playback separately:
|
|||||||
scrcpy --record=file.mkv --no-audio-playback
|
scrcpy --record=file.mkv --no-audio-playback
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To also disable the window:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-playback --no-window --record=file.mp4
|
||||||
|
# interrupt recording with Ctrl+C
|
||||||
|
```
|
||||||
|
|
||||||
## Time limit
|
## Time limit
|
||||||
|
|
||||||
To limit the recording time:
|
To limit the recording time:
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
# Window
|
# Window
|
||||||
|
|
||||||
|
## Disable window
|
||||||
|
|
||||||
|
To disable window (may be useful for recording or for playing audio only):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
scrcpy --no-window --record=file.mp4
|
||||||
|
# Ctrl+C to interrupt
|
||||||
|
```
|
||||||
|
|
||||||
## Title
|
## Title
|
||||||
|
|
||||||
By default, the window title is the device model. It can be changed:
|
By default, the window title is the device model. It can be changed:
|
||||||
|
@ -48,7 +48,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
this.downsizeOnError = downsizeOnError;
|
this.downsizeOnError = downsizeOnError;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void streamScreen() throws IOException, ConfigurationException {
|
private void streamCapture() throws IOException, ConfigurationException {
|
||||||
Codec codec = streamer.getCodec();
|
Codec codec = streamer.getCodec();
|
||||||
MediaCodec mediaCodec = createMediaCodec(codec, encoderName);
|
MediaCodec mediaCodec = createMediaCodec(codec, encoderName);
|
||||||
MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions);
|
MediaFormat format = createFormat(codec.getMimeType(), videoBitRate, maxFps, codecOptions);
|
||||||
@ -254,7 +254,7 @@ public class SurfaceEncoder implements AsyncProcessor {
|
|||||||
Looper.prepare();
|
Looper.prepare();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
streamScreen();
|
streamCapture();
|
||||||
} catch (ConfigurationException e) {
|
} catch (ConfigurationException e) {
|
||||||
// Do not print stack trace, a user-friendly error-message has already been logged
|
// Do not print stack trace, a user-friendly error-message has already been logged
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
Reference in New Issue
Block a user