From 2788e442b29eeb7cff3b9406874a0041cf71c7c3 Mon Sep 17 00:00:00 2001 From: Romain Vimont Date: Sun, 19 Feb 2023 20:20:29 +0100 Subject: [PATCH] Add --audio-encoder option Similar to --video-encoder, but for audio. --- app/scrcpy.1 | 4 +++ app/src/cli.c | 11 ++++++++ app/src/options.h | 1 + app/src/scrcpy.c | 1 + app/src/server.c | 5 ++++ app/src/server.h | 1 + .../com/genymobile/scrcpy/AudioEncoder.java | 27 ++++++++++++++++--- .../java/com/genymobile/scrcpy/Options.java | 9 +++++++ .../java/com/genymobile/scrcpy/Server.java | 6 ++++- 9 files changed, 60 insertions(+), 5 deletions(-) diff --git a/app/scrcpy.1 b/app/scrcpy.1 index a845d128..d3ddc643 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -31,6 +31,10 @@ Select an audio codec (opus or aac). Default is opus. +.TP +.BI "\-\-audio\-encoder " name +Use a specific MediaCodec audio encoder (depending on the codec provided by \fB\-\-audio\-codec\fR). + .TP .BI "\-b, \-\-bit\-rate " value Encode the video at the given bit\-rate, expressed in bits/s. Unit suffixes are supported: '\fBK\fR' (x1000) and '\fBM\fR' (x1000000). diff --git a/app/src/cli.c b/app/src/cli.c index 7106f27f..aac0b668 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -61,6 +61,7 @@ #define OPT_NO_AUDIO 1041 #define OPT_AUDIO_BIT_RATE 1042 #define OPT_AUDIO_CODEC 1043 +#define OPT_AUDIO_ENCODER_NAME 1044 struct sc_option { char shortopt; @@ -116,6 +117,13 @@ static const struct sc_option options[] = { .text = "Select an audio codec (opus or aac).\n" "Default is opus.", }, + { + .longopt_id = OPT_AUDIO_ENCODER_NAME, + .longopt = "audio-encoder", + .argdesc = "name", + .text = "Use a specific MediaCodec audio encoder (depending on the " + "codec provided by --audio-codec).", + }, { .shortopt = 'b', .longopt = "bit-rate", @@ -1636,6 +1644,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], case OPT_ENCODER_NAME: opts->encoder_name = optarg; break; + case OPT_AUDIO_ENCODER_NAME: + opts->audio_encoder_name = optarg; + break; case OPT_FORCE_ADB_FORWARD: opts->force_adb_forward = true; break; diff --git a/app/src/options.h b/app/src/options.h index dced5f7b..1db62a01 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -97,6 +97,7 @@ struct scrcpy_options { const char *render_driver; const char *codec_options; const char *encoder_name; + const char *audio_encoder_name; #ifdef HAVE_V4L2 const char *v4l2_device; #endif diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 8d5922e2..6eae7266 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -368,6 +368,7 @@ scrcpy(struct scrcpy_options *options) { .stay_awake = options->stay_awake, .codec_options = options->codec_options, .encoder_name = options->encoder_name, + .audio_encoder_name = options->audio_encoder_name, .force_adb_forward = options->force_adb_forward, .power_off_on_close = options->power_off_on_close, .clipboard_autosync = options->clipboard_autosync, diff --git a/app/src/server.c b/app/src/server.c index cbe3dfc3..da63c7f3 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -73,6 +73,7 @@ sc_server_params_destroy(struct sc_server_params *params) { free((char *) params->crop); free((char *) params->codec_options); free((char *) params->encoder_name); + free((char *) params->audio_encoder_name); free((char *) params->tcpip_dst); } @@ -97,6 +98,7 @@ sc_server_params_copy(struct sc_server_params *dst, COPY(crop); COPY(codec_options); COPY(encoder_name); + COPY(audio_encoder_name); COPY(tcpip_dst); #undef COPY @@ -271,6 +273,9 @@ execute_server(struct sc_server *server, if (params->encoder_name) { ADD_PARAM("encoder_name=%s", params->encoder_name); } + if (params->audio_encoder_name) { + ADD_PARAM("audio_encoder_name=%s", params->audio_encoder_name); + } if (params->power_off_on_close) { ADD_PARAM("power_off_on_close=true"); } diff --git a/app/src/server.h b/app/src/server.h index ac28d00b..1f65301c 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -30,6 +30,7 @@ struct sc_server_params { const char *crop; const char *codec_options; const char *encoder_name; + const char *audio_encoder_name; struct sc_port_range port_range; uint32_t tunnel_host; uint16_t tunnel_port; diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java index 11a32695..3a20c877 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java @@ -46,6 +46,7 @@ public final class AudioEncoder { private final Streamer streamer; private final int bitRate; + private final String encoderName; // Capacity of 64 is in practice "infinite" (it is limited by the number of available MediaCodec buffers, typically 4). // So many pending tasks would lead to an unacceptable delay anyway. @@ -60,9 +61,10 @@ public final class AudioEncoder { private boolean ended; - public AudioEncoder(Streamer streamer, int bitRate) { + public AudioEncoder(Streamer streamer, int bitRate, String encoderName) { this.streamer = streamer; this.bitRate = bitRate; + this.encoderName = encoderName; } private static AudioFormat createAudioFormat() { @@ -210,14 +212,14 @@ public final class AudioEncoder { boolean mediaCodecStarted = false; boolean recorderStarted = false; try { - String mimeType = streamer.getCodec().getMimeType(); - mediaCodec = MediaCodec.createEncoderByType(mimeType); // may throw IOException + Codec codec = streamer.getCodec(); + mediaCodec = createMediaCodec(codec, encoderName); recorder = createAudioRecord(); mediaCodecThread = new HandlerThread("AudioEncoder"); mediaCodecThread.start(); - MediaFormat format = createFormat(mimeType, bitRate); + MediaFormat format = createFormat(codec.getMimeType(), bitRate); mediaCodec.setCallback(new EncoderCallback(), new Handler(mediaCodecThread.getLooper())); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); @@ -257,6 +259,8 @@ public final class AudioEncoder { outputThread.start(); waitEnded(); + } catch (ConfigurationException e) { + // Do not print stack trace, a user-friendly error-message has already been logged } finally { if (!recorderStarted) { // Notify the client that the audio could not be captured @@ -307,6 +311,21 @@ public final class AudioEncoder { } } + private static MediaCodec createMediaCodec(Codec codec, String encoderName) throws IOException, ConfigurationException { + if (encoderName != null) { + Ln.d("Creating audio encoder by name: '" + encoderName + "'"); + try { + return MediaCodec.createByCodecName(encoderName); + } catch (IllegalArgumentException e) { + Ln.e(CodecUtils.buildUnknownEncoderMessage(codec, encoderName)); + throw new ConfigurationException("Unknown encoder: " + encoderName); + } + } + MediaCodec mediaCodec = MediaCodec.createEncoderByType(codec.getMimeType()); + Ln.d("Using audio encoder: '" + mediaCodec.getName() + "'"); + return mediaCodec; + } + private class EncoderCallback extends MediaCodec.Callback { @TargetApi(Build.VERSION_CODES.N) @Override diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index cb8ec638..14fde6e1 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -24,6 +24,7 @@ public class Options { private boolean stayAwake; private List codecOptions; private String encoderName; + private String audioEncoderName; private boolean powerOffScreenOnClose; private boolean clipboardAutosync = true; private boolean downsizeOnError = true; @@ -180,6 +181,14 @@ public class Options { this.encoderName = encoderName; } + public String getAudioEncoderName() { + return audioEncoderName; + } + + public void setAudioEncoderName(String audioEncoderName) { + this.audioEncoderName = audioEncoderName; + } + public void setPowerOffScreenOnClose(boolean powerOffScreenOnClose) { this.powerOffScreenOnClose = powerOffScreenOnClose; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 7d5d1bbb..94a07411 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Server.java +++ b/server/src/main/java/com/genymobile/scrcpy/Server.java @@ -112,7 +112,7 @@ public final class Server { if (audio) { Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(), options.getSendFrameMeta()); - audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate()); + audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioEncoderName()); audioEncoder.start(); } @@ -264,6 +264,10 @@ public final class Server { options.setEncoderName(value); } break; + case "audio_encoder_name": + if (!value.isEmpty()) { + options.setAudioEncoderName(value); + } case "power_off_on_close": boolean powerOffScreenOnClose = Boolean.parseBoolean(value); options.setPowerOffScreenOnClose(powerOffScreenOnClose);