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 <https://github.com/Genymobile/scrcpy/pull/3757>
This commit is contained in:
Romain Vimont 2023-02-28 21:19:43 +01:00
parent de40cac6ad
commit c1528cdca9
9 changed files with 46 additions and 21 deletions

View file

@ -45,6 +45,7 @@ _scrcpy() {
-r --record= -r --record=
--record-format= --record-format=
--render-driver= --render-driver=
--require-audio
--rotation= --rotation=
-s --serial= -s --serial=
--shortcut-mod= --shortcut-mod=

View file

@ -51,6 +51,7 @@ arguments=(
{-r,--record=}'[Record screen to file]:record file:_files' {-r,--record=}'[Record screen to file]:record file:_files'
'--record-format=[Force recording format]:format:(mp4 mkv)' '--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)' '--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)' '--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}'\''))' {-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)' '--shortcut-mod=[\[key1,key2+key3,...\] Specify the modifiers to use for scrcpy shortcuts]:shortcut mod:(lctrl rctrl lalt ralt lsuper rsuper)'

View file

@ -272,6 +272,10 @@ Supported names are currently "direct3d", "opengl", "opengles2", "opengles", "me
.UR https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER .UR https://wiki.libsdl.org/SDL_HINT_RENDER_DRIVER
.UE .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 .TP
.BI "\-\-rotation " value .BI "\-\-rotation " value
Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each increment adds a 90 degrees rotation counterclockwise. Set the initial display rotation. Possibles values are 0, 1, 2 and 3. Each increment adds a 90 degrees rotation counterclockwise.

View file

@ -70,6 +70,7 @@ enum {
OPT_AUDIO_ENCODER, OPT_AUDIO_ENCODER,
OPT_LIST_ENCODERS, OPT_LIST_ENCODERS,
OPT_LIST_DISPLAYS, OPT_LIST_DISPLAYS,
OPT_REQUIRE_AUDIO,
}; };
struct sc_option { struct sc_option {
@ -465,6 +466,13 @@ static const struct sc_option options[] = {
.longopt_id = OPT_RENDER_EXPIRED_FRAMES, .longopt_id = OPT_RENDER_EXPIRED_FRAMES,
.longopt = "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_id = OPT_ROTATION,
.longopt = "rotation", .longopt = "rotation",
@ -1811,6 +1819,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_LIST_DISPLAYS: case OPT_LIST_DISPLAYS:
opts->list_displays = true; opts->list_displays = true;
break; break;
case OPT_REQUIRE_AUDIO:
opts->require_audio = true;
break;
default: default:
// getopt prints the error message on stderr // getopt prints the error message on stderr
return false; return false;

View file

@ -176,14 +176,13 @@ run_demuxer(void *data) {
struct sc_demuxer *demuxer = data; struct sc_demuxer *demuxer = data;
// Flag to report end-of-stream (i.e. device disconnected) // 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; uint32_t raw_codec_id;
bool ok = sc_demuxer_recv_codec_id(demuxer, &raw_codec_id); bool ok = sc_demuxer_recv_codec_id(demuxer, &raw_codec_id);
if (!ok) { if (!ok) {
LOGE("Demuxer '%s': stream disabled due to connection error", LOGE("Demuxer '%s': stream disabled due to connection error",
demuxer->name); demuxer->name);
eos = true;
goto end; goto end;
} }
@ -191,7 +190,7 @@ run_demuxer(void *data) {
LOGW("Demuxer '%s': stream explicitly disabled by the device", LOGW("Demuxer '%s': stream explicitly disabled by the device",
demuxer->name); demuxer->name);
sc_demuxer_disable_sinks(demuxer); sc_demuxer_disable_sinks(demuxer);
eos = true; status = SC_DEMUXER_STATUS_DISABLED;
goto end; goto end;
} }
@ -241,7 +240,7 @@ run_demuxer(void *data) {
bool ok = sc_demuxer_recv_packet(demuxer, packet); bool ok = sc_demuxer_recv_packet(demuxer, packet);
if (!ok) { if (!ok) {
// end of stream // end of stream
eos = true; status = SC_DEMUXER_STATUS_EOS;
break; break;
} }
@ -272,7 +271,7 @@ run_demuxer(void *data) {
finally_close_sinks: finally_close_sinks:
sc_demuxer_close_sinks(demuxer); sc_demuxer_close_sinks(demuxer);
end: end:
demuxer->cbs->on_ended(demuxer, eos, demuxer->cbs_userdata); demuxer->cbs->on_ended(demuxer, status, demuxer->cbs_userdata);
return 0; return 0;
} }

View file

@ -27,8 +27,15 @@ struct sc_demuxer {
void *cbs_userdata; void *cbs_userdata;
}; };
enum sc_demuxer_status {
SC_DEMUXER_STATUS_EOS,
SC_DEMUXER_STATUS_DISABLED,
SC_DEMUXER_STATUS_ERROR,
};
struct sc_demuxer_callbacks { 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) // The name must be statically allocated (e.g. a string literal)

View file

@ -72,6 +72,7 @@ const struct scrcpy_options scrcpy_options_default = {
.start_fps_counter = false, .start_fps_counter = false,
.power_on = true, .power_on = true,
.audio = true, .audio = true,
.require_audio = false,
.list_encoders = false, .list_encoders = false,
.list_displays = false, .list_displays = false,
}; };

View file

@ -154,6 +154,7 @@ struct scrcpy_options {
bool start_fps_counter; bool start_fps_counter;
bool power_on; bool power_on;
bool audio; bool audio;
bool require_audio;
bool list_encoders; bool list_encoders;
bool list_displays; bool list_displays;
}; };

View file

@ -216,12 +216,15 @@ sc_recorder_on_ended(struct sc_recorder *recorder, bool success,
} }
static void static void
sc_video_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos, sc_video_demuxer_on_ended(struct sc_demuxer *demuxer,
void *userdata) { enum sc_demuxer_status status, void *userdata) {
(void) demuxer; (void) demuxer;
(void) userdata; (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); PUSH_EVENT(SC_EVENT_DEVICE_DISCONNECTED);
} else { } else {
PUSH_EVENT(SC_EVENT_DEMUXER_ERROR); PUSH_EVENT(SC_EVENT_DEMUXER_ERROR);
@ -229,20 +232,17 @@ sc_video_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos,
} }
static void static void
sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos, sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer,
void *userdata) { enum sc_demuxer_status status, void *userdata) {
(void) demuxer; (void) demuxer;
(void) userdata;
// Contrary to the video demuxer, keep mirroring if only the audio fails. const struct scrcpy_options *options = userdata;
// '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.
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); PUSH_EVENT(SC_EVENT_DEMUXER_ERROR);
} }
} }
@ -434,7 +434,7 @@ scrcpy(struct scrcpy_options *options) {
.on_ended = sc_audio_demuxer_on_ended, .on_ended = sc_audio_demuxer_on_ended,
}; };
sc_demuxer_init(&s->audio_demuxer, "audio", s->server.audio_socket, 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; bool needs_video_decoder = options->display;