From c1528cdca92d72d98f8f0d1b5cd088a1dd79467c Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Tue, 28 Feb 2023 21:19:43 +0100 Subject: [PATCH] Add --require-audio By default, scrcpy mirrors only the video when audio capture fails on the device. Add an option to force scrcpy to fail if audio is enabled but does not work. PR #3757 --- app/data/bash-completion/scrcpy | 1 + app/data/zsh-completion/_scrcpy | 1 + app/scrcpy.1 | 4 ++++ app/src/cli.c | 11 +++++++++++ app/src/demuxer.c | 9 ++++----- app/src/demuxer.h | 9 ++++++++- app/src/options.c | 1 + app/src/options.h | 1 + app/src/scrcpy.c | 30 +++++++++++++++--------------- 9 files changed, 46 insertions(+), 21 deletions(-) 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;