diff --git a/app/data/bash-completion/scrcpy b/app/data/bash-completion/scrcpy index fa95ce6e..74c3ee57 100644 --- a/app/data/bash-completion/scrcpy +++ b/app/data/bash-completion/scrcpy @@ -45,6 +45,7 @@ _scrcpy() { -r --record= --record-format= --render-driver= + --require-audio --rotation= -s --serial= --shortcut-mod= diff --git a/app/data/zsh-completion/_scrcpy b/app/data/zsh-completion/_scrcpy index 231405ce..b28201a4 100644 --- a/app/data/zsh-completion/_scrcpy +++ b/app/data/zsh-completion/_scrcpy @@ -51,6 +51,7 @@ arguments=( {-r,--record=}'[Record screen to file]:record file:_files' '--record-format=[Force recording format]:format:(mp4 mkv)' '--render-driver=[Request SDL to use the given render driver]:driver name:(direct3d opengl opengles2 opengles metal software)' + '--require-audio=[Make scrcpy fail if audio is enabled but does not work]' '--rotation=[Set the initial display rotation]:rotation values:(0 1 2 3)' {-s,--serial=}'[The device serial number \(mandatory for multiple devices only\)]:serial:($("${ADB-adb}" devices | awk '\''$2 == "device" {print $1}'\''))' '--shortcut-mod=[\[key1,key2+key3,...\] Specify the modifiers to use for scrcpy shortcuts]:shortcut mod:(lctrl rctrl lalt ralt lsuper rsuper)' diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 40b8158c..91258414 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -272,6 +272,10 @@ Supported names are currently "direct3d", "opengl", "opengles2", "opengles", "me .UR https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER .UE +.TP +.B \-\-require\-audio +By default, scrcpy mirrors only the video if audio capture fails on the device. This option makes scrcpy fail if audio is enabled but does not work. + .TP .BI "\-\-rotation " value Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each increment adds a 90 degrees rotation counterclockwise. diff --git a/app/src/cli.c b/app/src/cli.c index 8dfcdc79..18f3b83b 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -70,6 +70,7 @@ enum { OPT_AUDIO_ENCODER, OPT_LIST_ENCODERS, OPT_LIST_DISPLAYS, + OPT_REQUIRE_AUDIO, }; struct sc_option { @@ -465,6 +466,13 @@ static const struct sc_option options[] = { .longopt_id = OPT_RENDER_EXPIRED_FRAMES, .longopt = "render-expired-frames", }, + { + .longopt_id = OPT_REQUIRE_AUDIO, + .longopt = "require-audio", + .text = "By default, scrcpy mirrors only the video when audio capture " + "fails on the device. This option makes scrcpy fail if audio " + "is enabled but does not work." + }, { .longopt_id = OPT_ROTATION, .longopt = "rotation", @@ -1811,6 +1819,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], case OPT_LIST_DISPLAYS: opts->list_displays = true; break; + case OPT_REQUIRE_AUDIO: + opts->require_audio = true; + break; default: // getopt prints the error message on stderr return false; diff --git a/app/src/demuxer.c b/app/src/demuxer.c index d80a5dda..5977a28a 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -176,14 +176,13 @@ run_demuxer(void *data) { struct sc_demuxer *demuxer = data; // Flag to report end-of-stream (i.e. device disconnected) - bool eos = false; + enum sc_demuxer_status status = SC_DEMUXER_STATUS_ERROR; uint32_t raw_codec_id; bool ok = sc_demuxer_recv_codec_id(demuxer, &raw_codec_id); if (!ok) { LOGE("Demuxer '%s': stream disabled due to connection error", demuxer->name); - eos = true; goto end; } @@ -191,7 +190,7 @@ run_demuxer(void *data) { LOGW("Demuxer '%s': stream explicitly disabled by the device", demuxer->name); sc_demuxer_disable_sinks(demuxer); - eos = true; + status = SC_DEMUXER_STATUS_DISABLED; goto end; } @@ -241,7 +240,7 @@ run_demuxer(void *data) { bool ok = sc_demuxer_recv_packet(demuxer, packet); if (!ok) { // end of stream - eos = true; + status = SC_DEMUXER_STATUS_EOS; break; } @@ -272,7 +271,7 @@ run_demuxer(void *data) { finally_close_sinks: sc_demuxer_close_sinks(demuxer); end: - demuxer->cbs->on_ended(demuxer, eos, demuxer->cbs_userdata); + demuxer->cbs->on_ended(demuxer, status, demuxer->cbs_userdata); return 0; } diff --git a/app/src/demuxer.h b/app/src/demuxer.h index 73166b41..d0e41add 100644 --- a/app/src/demuxer.h +++ b/app/src/demuxer.h @@ -27,8 +27,15 @@ struct sc_demuxer { void *cbs_userdata; }; +enum sc_demuxer_status { + SC_DEMUXER_STATUS_EOS, + SC_DEMUXER_STATUS_DISABLED, + SC_DEMUXER_STATUS_ERROR, +}; + struct sc_demuxer_callbacks { - void (*on_ended)(struct sc_demuxer *demuxer, bool eos, void *userdata); + void (*on_ended)(struct sc_demuxer *demuxer, enum sc_demuxer_status, + void *userdata); }; // The name must be statically allocated (e.g. a string literal) diff --git a/app/src/options.c b/app/src/options.c index 8560b37b..5dd655ce 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -72,6 +72,7 @@ const struct scrcpy_options scrcpy_options_default = { .start_fps_counter = false, .power_on = true, .audio = true, + .require_audio = false, .list_encoders = false, .list_displays = false, }; diff --git a/app/src/options.h b/app/src/options.h index a15d51f8..5fcaf016 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -154,6 +154,7 @@ struct scrcpy_options { bool start_fps_counter; bool power_on; bool audio; + bool require_audio; bool list_encoders; bool list_displays; }; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index eb70749a..4355d71b 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -216,12 +216,15 @@ sc_recorder_on_ended(struct sc_recorder *recorder, bool success, } static void -sc_video_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos, - void *userdata) { +sc_video_demuxer_on_ended(struct sc_demuxer *demuxer, + enum sc_demuxer_status status, void *userdata) { (void) demuxer; (void) userdata; - if (eos) { + // The device may not decide to disable the video + assert(status != SC_DEMUXER_STATUS_DISABLED); + + if (status == SC_DEMUXER_STATUS_EOS) { PUSH_EVENT(SC_EVENT_DEVICE_DISCONNECTED); } else { PUSH_EVENT(SC_EVENT_DEMUXER_ERROR); @@ -229,20 +232,17 @@ sc_video_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos, } static void -sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos, - void *userdata) { +sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer, + enum sc_demuxer_status status, void *userdata) { (void) demuxer; - (void) userdata; - // Contrary to the video demuxer, keep mirroring if only the audio fails. - // 'eos' is true on end-of-stream, including when audio capture is not - // possible on the device (so that scrcpy continue to mirror video without - // failing). - // However, if an audio configuration failure occurs (for example the user - // explicitly selected an unknown audio encoder), 'eos' is false and scrcpy - // must exit. + const struct scrcpy_options *options = userdata; - if (!eos) { + // Contrary to the video demuxer, keep mirroring if only the audio fails + // (unless --require-audio is set). + if (status == SC_DEMUXER_STATUS_ERROR + || (status == SC_DEMUXER_STATUS_DISABLED + && options->require_audio)) { PUSH_EVENT(SC_EVENT_DEMUXER_ERROR); } } @@ -434,7 +434,7 @@ scrcpy(struct scrcpy_options *options) { .on_ended = sc_audio_demuxer_on_ended, }; sc_demuxer_init(&s->audio_demuxer, "audio", s->server.audio_socket, - &audio_demuxer_cbs, NULL); + &audio_demuxer_cbs, options); } bool needs_video_decoder = options->display;