Add separate video and audio playback options

Add --no-video-playback and --no-audio-playback. The option
--no-playback is now an alias for both.

PR #4033 <https://github.com/Genymobile/scrcpy/pull/4033>
This commit is contained in:
Romain Vimont 2023-05-24 21:22:31 +02:00
parent 751c09f47a
commit 1efbfe1175
8 changed files with 94 additions and 43 deletions

View file

@ -29,6 +29,7 @@ _scrcpy() {
-M --hid-mouse -M --hid-mouse
-m --max-size= -m --max-size=
--no-audio --no-audio
--no-audio-playback
--no-cleanup --no-cleanup
--no-clipboard-autosync --no-clipboard-autosync
--no-downsize-on-error --no-downsize-on-error
@ -38,6 +39,7 @@ _scrcpy() {
--no-mipmaps --no-mipmaps
--no-power-on --no-power-on
--no-video --no-video
--no-video-playback
--otg --otg
-p --port= -p --port=
--power-off-on-close --power-off-on-close

View file

@ -35,6 +35,7 @@ arguments=(
{-M,--hid-mouse}'[Simulate a physical mouse by using HID over AOAv2]' {-M,--hid-mouse}'[Simulate a physical mouse by using HID over AOAv2]'
{-m,--max-size=}'[Limit both the width and height of the video to value]' {-m,--max-size=}'[Limit both the width and height of the video to value]'
'--no-audio[Disable audio forwarding]' '--no-audio[Disable audio forwarding]'
'--no-audio-playback[Disable audio playback]'
'--no-cleanup[Disable device cleanup actions on exit]' '--no-cleanup[Disable device cleanup actions on exit]'
'--no-clipboard-autosync[Disable automatic clipboard synchronization]' '--no-clipboard-autosync[Disable automatic clipboard synchronization]'
'--no-downsize-on-error[Disable lowering definition on MediaCodec error]' '--no-downsize-on-error[Disable lowering definition on MediaCodec error]'
@ -44,6 +45,7 @@ arguments=(
'--no-mipmaps[Disable the generation of mipmaps]' '--no-mipmaps[Disable the generation of mipmaps]'
'--no-power-on[Do not power on the device on start]' '--no-power-on[Do not power on the device on start]'
'--no-video[Disable video forwarding]' '--no-video[Disable video forwarding]'
'--no-video-playback[Disable video playback]'
'--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]' '--otg[Run in OTG mode \(simulating physical keyboard and mouse\)]'
{-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]' {-p,--port=}'[\[port\[\:port\]\] Set the TCP port \(range\) used by the client to listen]'
'--power-off-on-close[Turn the device screen off when closing scrcpy]' '--power-off-on-close[Turn the device screen off when closing scrcpy]'

View file

@ -187,6 +187,10 @@ Also see \fB\-\-hid\-keyboard\fR.
.B \-\-no\-audio .B \-\-no\-audio
Disable audio forwarding. Disable audio forwarding.
.TP
.B \-\-no\-audio\-playback
Disable audio playback on the computer.
.TP .TP
.B \-\-no\-cleanup .B \-\-no\-cleanup
By default, scrcpy removes the server binary from the device and restores the device state (show touches, stay awake and power mode) on exit. By default, scrcpy removes the server binary from the device and restores the device state (show touches, stay awake and power mode) on exit.
@ -211,7 +215,7 @@ Disable device control (mirror the device in read\-only).
.TP .TP
.B \-N, \-\-no\-playback .B \-N, \-\-no\-playback
Disable video and audio playback on the computer. Disable video and audio playback on the computer (equivalent to --no-video-playback --no-audio-playback).
.TP .TP
.B \-\-no\-key\-repeat .B \-\-no\-key\-repeat
@ -229,6 +233,10 @@ Do not power on the device on start.
.B \-\-no\-video .B \-\-no\-video
Disable video forwarding. Disable video forwarding.
.TP
.B \-\-no\-video\-playback
Disable video playback on the computer.
.TP .TP
.B \-\-otg .B \-\-otg
Run in OTG mode: simulate physical keyboard and mouse, as if the computer keyboard and mouse were plugged directly to the device via an OTG cable. Run in OTG mode: simulate physical keyboard and mouse, as if the computer keyboard and mouse were plugged directly to the device via an OTG cable.

View file

@ -74,6 +74,8 @@ enum {
OPT_AUDIO_OUTPUT_BUFFER, OPT_AUDIO_OUTPUT_BUFFER,
OPT_NO_DISPLAY, OPT_NO_DISPLAY,
OPT_NO_VIDEO, OPT_NO_VIDEO,
OPT_NO_AUDIO_PLAYBACK,
OPT_NO_VIDEO_PLAYBACK,
}; };
struct sc_option { struct sc_option {
@ -351,6 +353,11 @@ static const struct sc_option options[] = {
.longopt = "no-audio", .longopt = "no-audio",
.text = "Disable audio forwarding.", .text = "Disable audio forwarding.",
}, },
{
.longopt_id = OPT_NO_AUDIO_PLAYBACK,
.longopt = "no-audio-playback",
.text = "Disable audio playback on the computer.",
},
{ {
.longopt_id = OPT_NO_CLEANUP, .longopt_id = OPT_NO_CLEANUP,
.longopt = "no-cleanup", .longopt = "no-cleanup",
@ -383,7 +390,8 @@ static const struct sc_option options[] = {
{ {
.shortopt = 'N', .shortopt = 'N',
.longopt = "no-playback", .longopt = "no-playback",
.text = "Disable video and audio playback on the computer.", .text = "Disable video and audio playback on the computer (equivalent "
"to --no-video-playback --no-audio-playback).",
}, },
{ {
// deprecated // deprecated
@ -412,6 +420,11 @@ static const struct sc_option options[] = {
.longopt = "no-video", .longopt = "no-video",
.text = "Disable video forwarding.", .text = "Disable video forwarding.",
}, },
{
.longopt_id = OPT_NO_VIDEO_PLAYBACK,
.longopt = "no-video-playback",
.text = "Disable video playback on the computer.",
},
{ {
.longopt_id = OPT_OTG, .longopt_id = OPT_OTG,
.longopt = "otg", .longopt = "otg",
@ -1673,7 +1686,14 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
LOGW("--no-display is deprecated, use --no-playback instead."); LOGW("--no-display is deprecated, use --no-playback instead.");
// fall through // fall through
case 'N': case 'N':
opts->playback = false; opts->video_playback = false;
opts->audio_playback = false;
break;
case OPT_NO_VIDEO_PLAYBACK:
opts->video_playback = false;
break;
case OPT_NO_AUDIO_PLAYBACK:
opts->audio_playback = false;
break; break;
case 'p': case 'p':
if (!parse_port_range(optarg, &opts->port_range)) { if (!parse_port_range(optarg, &opts->port_range)) {
@ -1932,23 +1952,41 @@ 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->playback && opts->video) && !otg) { if (!opts->video) {
opts->video_playback = false;
}
if (!opts->audio) {
opts->audio_playback = false;
}
if (!opts->video_playback && !otg) {
// If video playback is disabled and OTG are disabled, then there is // If video playback is disabled and OTG are disabled, then there is
// no way to control the device. // no way to control the device.
opts->control = false; opts->control = false;
} }
if (!opts->video) { if (opts->video && !opts->video_playback && !opts->record_filename
// If video is disabled, then scrcpy must exit on audio failure. && !v4l2) {
opts->require_audio = true; LOGI("No video playback, no recording, no V4L2 sink: video disabled");
opts->video = false;
} }
if (!opts->playback && !opts->record_filename && !v4l2) { if (opts->audio && !opts->audio_playback && !opts->record_filename) {
LOGE("-N/--no-playback requires either screen recording (-r/--record)" LOGI("No audio playback, no recording: audio disabled");
" or sink to v4l2loopback device (--v4l2-sink)"); opts->audio = false;
}
if (!opts->video && !opts->audio && !otg) {
LOGE("No video, no audio, no OTG: nothing to do");
return false; return false;
} }
if (!opts->video && !otg) {
// If video is disabled, then scrcpy must exit on audio failure.
opts->require_audio = true;
}
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
if (v4l2) { if (v4l2) {
if (opts->lock_video_orientation == if (opts->lock_video_orientation ==
@ -1970,11 +2008,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
} }
#endif #endif
if (opts->audio && !opts->playback && !opts->record_filename) {
LOGI("No playback and no recording: audio disabled");
opts->audio = false;
}
if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) { if ((opts->tunnel_host || opts->tunnel_port) && !opts->force_adb_forward) {
LOGI("Tunnel host/port is set, " LOGI("Tunnel host/port is set, "
"--force-adb-forward automatically enabled."); "--force-adb-forward automatically enabled.");

View file

@ -52,7 +52,8 @@ const struct scrcpy_options scrcpy_options_default = {
.fullscreen = false, .fullscreen = false,
.always_on_top = false, .always_on_top = false,
.control = true, .control = true,
.playback = true, .video_playback = true,
.audio_playback = true,
.turn_screen_off = false, .turn_screen_off = false,
.key_inject_mode = SC_KEY_INJECT_MODE_MIXED, .key_inject_mode = SC_KEY_INJECT_MODE_MIXED,
.window_borderless = false, .window_borderless = false,

View file

@ -147,7 +147,8 @@ struct scrcpy_options {
bool fullscreen; bool fullscreen;
bool always_on_top; bool always_on_top;
bool control; bool control;
bool playback; bool video_playback;
bool audio_playback;
bool turn_screen_off; bool turn_screen_off;
enum sc_key_inject_mode key_inject_mode; enum sc_key_inject_mode key_inject_mode;
bool window_borderless; bool window_borderless;

View file

@ -137,7 +137,7 @@ sdl_set_hints(const char *render_driver) {
} }
static void static void
sdl_configure(bool playback, bool disable_screensaver) { sdl_configure(bool video_playback, bool disable_screensaver) {
#ifdef _WIN32 #ifdef _WIN32
// Clean up properly on Ctrl+C on Windows // Clean up properly on Ctrl+C on Windows
bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE); bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE);
@ -146,7 +146,7 @@ sdl_configure(bool playback, bool disable_screensaver) {
} }
#endif // _WIN32 #endif // _WIN32
if (!playback) { if (!video_playback) {
return; return;
} }
@ -386,22 +386,26 @@ scrcpy(struct scrcpy_options *options) {
goto end; goto end;
} }
if (options->playback) { // playback implies capture
sdl_set_hints(options->render_driver); assert(!options->video_playback || options->video);
assert(!options->audio_playback || options->audio);
// Initialize SDL video and audio in addition if playback is enabled if (options->video_playback) {
if (options->video && SDL_Init(SDL_INIT_VIDEO)) { sdl_set_hints(options->render_driver);
if (SDL_Init(SDL_INIT_VIDEO)) {
LOGE("Could not initialize SDL video: %s", SDL_GetError()); LOGE("Could not initialize SDL video: %s", SDL_GetError());
goto end; goto end;
} }
}
if (options->audio && SDL_Init(SDL_INIT_AUDIO)) { if (options->audio_playback) {
if (SDL_Init(SDL_INIT_AUDIO)) {
LOGE("Could not initialize SDL audio: %s", SDL_GetError()); LOGE("Could not initialize SDL audio: %s", SDL_GetError());
goto end; goto end;
} }
} }
sdl_configure(options->playback, options->disable_screensaver); sdl_configure(options->video_playback, options->disable_screensaver);
// Await for server without blocking Ctrl+C handling // Await for server without blocking Ctrl+C handling
bool connected; bool connected;
@ -427,7 +431,8 @@ scrcpy(struct scrcpy_options *options) {
struct sc_file_pusher *fp = NULL; struct sc_file_pusher *fp = NULL;
assert(!options->control || options->playback); // control implies playback // control implies video playback
assert(!options->control || options->video_playback);
if (options->control) { if (options->control) {
if (!sc_file_pusher_init(&s->file_pusher, serial, if (!sc_file_pusher_init(&s->file_pusher, serial,
options->push_target)) { options->push_target)) {
@ -453,8 +458,8 @@ scrcpy(struct scrcpy_options *options) {
&audio_demuxer_cbs, options); &audio_demuxer_cbs, options);
} }
bool needs_video_decoder = options->playback && options->video; bool needs_video_decoder = options->video_playback;
bool needs_audio_decoder = options->playback && options->audio; bool needs_audio_decoder = options->audio_playback;
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
needs_video_decoder |= !!options->v4l2_device; needs_video_decoder |= !!options->v4l2_device;
#endif #endif
@ -650,7 +655,7 @@ aoa_hid_end:
// 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->playback) { if (options->video_playback) {
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;
@ -684,21 +689,19 @@ aoa_hid_end:
src = &s->display_buffer.frame_source; src = &s->display_buffer.frame_source;
} }
if (options->video) { if (!sc_screen_init(&s->screen, &screen_params)) {
if (!sc_screen_init(&s->screen, &screen_params)) { goto end;
goto end;
}
screen_initialized = true;
sc_frame_source_add_sink(src, &s->screen.frame_sink);
} }
screen_initialized = true;
if (options->audio) { sc_frame_source_add_sink(src, &s->screen.frame_sink);
sc_audio_player_init(&s->audio_player, options->audio_buffer, }
options->audio_output_buffer);
sc_frame_source_add_sink(&s->audio_decoder.frame_source, if (options->audio_playback) {
&s->audio_player.frame_sink); sc_audio_player_init(&s->audio_player, options->audio_buffer,
} options->audio_output_buffer);
sc_frame_source_add_sink(&s->audio_decoder.frame_source,
&s->audio_player.frame_sink);
} }
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2

View file

@ -117,7 +117,8 @@ static void test_options2(void) {
const struct scrcpy_options *opts = &args.opts; const struct scrcpy_options *opts = &args.opts;
assert(!opts->control); assert(!opts->control);
assert(!opts->playback); assert(!opts->video_playback);
assert(!opts->audio_playback);
assert(!strcmp(opts->record_filename, "file.mp4")); assert(!strcmp(opts->record_filename, "file.mp4"));
assert(opts->record_format == SC_RECORD_FORMAT_MP4); assert(opts->record_format == SC_RECORD_FORMAT_MP4);
} }