diff --git a/app/src/demuxer.c b/app/src/demuxer.c index 64bf30a3..d80a5dda 100644 --- a/app/src/demuxer.c +++ b/app/src/demuxer.c @@ -195,6 +195,12 @@ run_demuxer(void *data) { goto end; } + if (raw_codec_id == 1) { + LOGE("Demuxer '%s': stream configuration error on the device", + demuxer->name); + goto end; + } + enum AVCodecID codec_id = sc_demuxer_to_avcodec_id(raw_codec_id); if (codec_id == AV_CODEC_ID_NONE) { LOGE("Demuxer '%s': stream disabled due to unsupported codec", diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 6d0fac9e..5739c3b2 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -231,10 +231,19 @@ static void sc_audio_demuxer_on_ended(struct sc_demuxer *demuxer, bool eos, void *userdata) { (void) demuxer; - (void) eos; (void) userdata; - // Contrary to the video demuxer, keep mirroring if only the audio fails + // 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. + + if (!eos) { + PUSH_EVENT(SC_EVENT_DEMUXER_ERROR); + } } static void diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java index 540d8306..66950004 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java @@ -222,7 +222,7 @@ public final class AudioEncoder { public void encode() throws IOException, ConfigurationException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { Ln.w("Audio disabled: it is not supported before Android 11"); - streamer.writeDisableStream(); + streamer.writeDisableStream(false); return; } @@ -279,9 +279,13 @@ public final class AudioEncoder { outputThread.start(); waitEnded(); + } catch (ConfigurationException e) { + // Notify the error to make scrcpy exit + streamer.writeDisableStream(true); + throw e; } catch (Throwable e) { // Notify the client that the audio could not be captured - streamer.writeDisableStream(); + streamer.writeDisableStream(false); throw e; } finally { // Cleanup everything (either at the end or on error at any step of the initialization) diff --git a/server/src/main/java/com/genymobile/scrcpy/Streamer.java b/server/src/main/java/com/genymobile/scrcpy/Streamer.java index 7cc065eb..9bfe7e91 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Streamer.java +++ b/server/src/main/java/com/genymobile/scrcpy/Streamer.java @@ -40,10 +40,15 @@ public final class Streamer { } } - public void writeDisableStream() throws IOException { - // Writing 0 (32-bit) as codec-id means that the device disables the stream (because it could not capture) - byte[] zeros = new byte[4]; - IO.writeFully(fd, zeros, 0, zeros.length); + public void writeDisableStream(boolean error) throws IOException { + // Writing a specific code as codec-id means that the device disables the stream + // code 0: it explicitly disables the stream (because it could not capture audio), scrcpy should continue mirroring video only + // code 1: a configuration error occurred, scrcpy must be stopped + byte[] code = new byte[4]; + if (error) { + code[3] = 1; + } + IO.writeFully(fd, code, 0, code.length); } public void writePacket(ByteBuffer buffer, long pts, boolean config, boolean keyFrame) throws IOException {