diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index dccb68e5..010125fb 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -29,6 +29,7 @@ _scrcpy() { -M --hid-mouse -m --max-size= --no-audio + --no-audio-playback --no-cleanup --no-clipboard-autosync --no-downsize-on-error @@ -38,6 +39,7 @@ _scrcpy() { --no-mipmaps --no-power-on --no-video + --no-video-playback --otg -p --port= --power-off-on-close diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index afc6b1e6..4f1f16b9 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -35,6 +35,7 @@ arguments=( {-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]' '--no-audio[Disable audio forwarding]' + '--no-audio-playback[Disable audio playback]' '--no-cleanup[Disable device cleanup actions on exit]' '--no-clipboard-autosync[Disable automatic clipboard synchronization]' '--no-downsize-on-error[Disable lowering definition on MediaCodec error]' @@ -44,6 +45,7 @@ arguments=( '--no-mipmaps[Disable the generation of mipmaps]' '--no-power-on[Do not power on the device on start]' '--no-video[Disable video forwarding]' + '--no-video-playback[Disable video playback]' '--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]' '--power-off-on-close[Turn the device screen off when closing scrcpy]' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index a622d22b..d506d9e7 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -187,6 +187,10 @@ Also see \fB\-\-hid\-keyboard\fR. .B \-\-no\-audio Disable audio forwarding. +.TP +.B \-\-no\-audio\-playback +Disable audio playback on the computer. + .TP .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. @@ -211,7 +215,7 @@ Disable device control (mirror the device in read\-only). .TP .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 .B \-\-no\-key\-repeat @@ -229,6 +233,10 @@ Do not power on the device on start. .B \-\-no\-video Disable video forwarding. +.TP +.B \-\-no\-video\-playback +Disable video playback on the computer. + .TP .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. diff --git a/app/src/cli.c b/app/src/cli.c index e96a3b85..1b42f75c 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -74,6 +74,8 @@ enum { OPT_AUDIO_OUTPUT_BUFFER, OPT_NO_DISPLAY, OPT_NO_VIDEO, + OPT_NO_AUDIO_PLAYBACK, + OPT_NO_VIDEO_PLAYBACK, }; struct sc_option { @@ -351,6 +353,11 @@ static const struct sc_option options[] = { .longopt = "no-audio", .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 = "no-cleanup", @@ -383,7 +390,8 @@ static const struct sc_option options[] = { { .shortopt = 'N', .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 @@ -412,6 +420,11 @@ static const struct sc_option options[] = { .longopt = "no-video", .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 = "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."); // fall through 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; case 'p': 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; #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 // no way to control the device. opts->control = false; } - if (!opts->video) { - // If video is disabled, then scrcpy must exit on audio failure. - opts->require_audio = true; + if (opts->video && !opts->video_playback && !opts->record_filename + && !v4l2) { + LOGI("No video playback, no recording, no V4L2 sink: video disabled"); + opts->video = false; } - if (!opts->playback && !opts->record_filename && !v4l2) { - LOGE("-N/--no-playback requires either screen recording (-r/--record)" - " or sink to v4l2loopback device (--v4l2-sink)"); + if (opts->audio && !opts->audio_playback && !opts->record_filename) { + LOGI("No audio playback, no recording: audio disabled"); + opts->audio = false; + } + + if (!opts->video && !opts->audio && !otg) { + LOGE("No video, no audio, no OTG: nothing to do"); return false; } + if (!opts->video && !otg) { + // If video is disabled, then scrcpy must exit on audio failure. + opts->require_audio = true; + } + #ifdef HAVE_V4L2 if (v4l2) { if (opts->lock_video_orientation == @@ -1970,11 +2008,6 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], } #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) { LOGI("Tunnel host/port is set, " "--force-adb-forward automatically enabled."); diff --git a/app/src/options.c b/app/src/options.c index fc3b1790..49cca969 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -52,7 +52,8 @@ const struct scrcpy_options scrcpy_options_default = { .fullscreen = false, .always_on_top = false, .control = true, - .playback = true, + .video_playback = true, + .audio_playback = true, .turn_screen_off = false, .key_inject_mode = SC_KEY_INJECT_MODE_MIXED, .window_borderless = false, diff --git a/app/src/options.h b/app/src/options.h index e3cc8064..dd5d0dad 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -147,7 +147,8 @@ struct scrcpy_options { bool fullscreen; bool always_on_top; bool control; - bool playback; + bool video_playback; + bool audio_playback; bool turn_screen_off; enum sc_key_inject_mode key_inject_mode; bool window_borderless; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 3334f57d..27a1c6be 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -137,7 +137,7 @@ sdl_set_hints(const char *render_driver) { } static void -sdl_configure(bool playback, bool disable_screensaver) { +sdl_configure(bool video_playback, bool disable_screensaver) { #ifdef _WIN32 // Clean up properly on Ctrl+C on Windows bool ok = SetConsoleCtrlHandler(windows_ctrl_handler, TRUE); @@ -146,7 +146,7 @@ sdl_configure(bool playback, bool disable_screensaver) { } #endif // _WIN32 - if (!playback) { + if (!video_playback) { return; } @@ -386,22 +386,26 @@ scrcpy(struct scrcpy_options *options) { goto end; } - if (options->playback) { - sdl_set_hints(options->render_driver); + // playback implies capture + 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 && SDL_Init(SDL_INIT_VIDEO)) { + if (options->video_playback) { + sdl_set_hints(options->render_driver); + if (SDL_Init(SDL_INIT_VIDEO)) { LOGE("Could not initialize SDL video: %s", SDL_GetError()); 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()); 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 bool connected; @@ -427,7 +431,8 @@ scrcpy(struct scrcpy_options *options) { 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 (!sc_file_pusher_init(&s->file_pusher, serial, options->push_target)) { @@ -453,8 +458,8 @@ scrcpy(struct scrcpy_options *options) { &audio_demuxer_cbs, options); } - bool needs_video_decoder = options->playback && options->video; - bool needs_audio_decoder = options->playback && options->audio; + bool needs_video_decoder = options->video_playback; + bool needs_audio_decoder = options->audio_playback; #ifdef HAVE_V4L2 needs_video_decoder |= !!options->v4l2_device; #endif @@ -650,7 +655,7 @@ aoa_hid_end: // There is a controller if and only if control is enabled assert(options->control == !!controller); - if (options->playback) { + if (options->video_playback) { const char *window_title = options->window_title ? options->window_title : info->device_name; @@ -684,21 +689,19 @@ aoa_hid_end: src = &s->display_buffer.frame_source; } - if (options->video) { - if (!sc_screen_init(&s->screen, &screen_params)) { - goto end; - } - screen_initialized = true; - - sc_frame_source_add_sink(src, &s->screen.frame_sink); + if (!sc_screen_init(&s->screen, &screen_params)) { + goto end; } + screen_initialized = true; - if (options->audio) { - 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); - } + sc_frame_source_add_sink(src, &s->screen.frame_sink); + } + + if (options->audio_playback) { + 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 diff --git a/app/tests/test_cli.c b/app/tests/test_cli.c index ec1a9531..f2a17272 100644 --- a/app/tests/test_cli.c +++ b/app/tests/test_cli.c @@ -117,7 +117,8 @@ static void test_options2(void) { const struct scrcpy_options *opts = &args.opts; assert(!opts->control); - assert(!opts->playback); + assert(!opts->video_playback); + assert(!opts->audio_playback); assert(!strcmp(opts->record_filename, "file.mp4")); assert(opts->record_format == SC_RECORD_FORMAT_MP4); }