diff --git a/app/scrcpy.1 b/app/scrcpy.1 index 7484ab7e..914aafd5 100644 --- a/app/scrcpy.1 +++ b/app/scrcpy.1 @@ -19,6 +19,12 @@ provides display and control of Android devices connected on USB (or over TCP/IP .B \-\-always\-on\-top Make scrcpy window always on top (above other windows). +.TP +.BI "\-\-audio\-bit\-rate " value +Encode the audio at the given bit\-rate, expressed in bits/s. Unit suffixes are supported: '\fBK\fR' (x1000) and '\fBM\fR' (x1000000). + +Default is 196K (196000). + .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 3df55f8e..586673ac 100644 --- a/app/src/cli.c +++ b/app/src/cli.c @@ -59,6 +59,7 @@ #define OPT_NO_POWER_ON 1039 #define OPT_CODEC 1040 #define OPT_NO_AUDIO 1041 +#define OPT_AUDIO_BIT_RATE 1042 struct sc_option { char shortopt; @@ -99,6 +100,14 @@ static const struct sc_option options[] = { .longopt = "always-on-top", .text = "Make scrcpy window always on top (above other windows).", }, + { + .longopt_id = OPT_AUDIO_BIT_RATE, + .longopt = "audio-bit-rate", + .argdesc = "value", + .text = "Encode the audio at the given bit-rate, expressed in bits/s. " + "Unit suffixes are supported: 'K' (x1000) and 'M' (x1000000).\n" + "Default is 196K (196000).", + }, { .shortopt = 'b', .longopt = "bit-rate", @@ -1424,6 +1433,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[], return false; } break; + case OPT_AUDIO_BIT_RATE: + if (!parse_bit_rate(optarg, &opts->audio_bit_rate)) { + return false; + } + break; case OPT_CROP: opts->crop = optarg; break; diff --git a/app/src/options.c b/app/src/options.c index 7329e22a..9465d154 100644 --- a/app/src/options.c +++ b/app/src/options.c @@ -28,6 +28,7 @@ const struct scrcpy_options scrcpy_options_default = { }, .max_size = 0, .bit_rate = 0, + .audio_bit_rate = 0, .max_fps = 0, .lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED, .rotation = 0, diff --git a/app/src/options.h b/app/src/options.h index 7bf30011..965c46ca 100644 --- a/app/src/options.h +++ b/app/src/options.h @@ -109,6 +109,7 @@ struct scrcpy_options { struct sc_shortcut_mods shortcut_mods; uint16_t max_size; uint32_t bit_rate; + uint32_t audio_bit_rate; uint16_t max_fps; enum sc_lock_video_orientation lock_video_orientation; uint8_t rotation; diff --git a/app/src/scrcpy.c b/app/src/scrcpy.c index 56d25622..8f72e665 100644 --- a/app/src/scrcpy.c +++ b/app/src/scrcpy.c @@ -357,6 +357,7 @@ scrcpy(struct scrcpy_options *options) { .tunnel_port = options->tunnel_port, .max_size = options->max_size, .bit_rate = options->bit_rate, + .audio_bit_rate = options->audio_bit_rate, .max_fps = options->max_fps, .lock_video_orientation = options->lock_video_orientation, .control = options->control, diff --git a/app/src/server.c b/app/src/server.c index c17a516a..f77d3312 100644 --- a/app/src/server.c +++ b/app/src/server.c @@ -221,6 +221,8 @@ execute_server(struct sc_server *server, } if (!params->audio) { ADD_PARAM("audio=false"); + } else if (params->audio_bit_rate) { + ADD_PARAM("audio_bit_rate=%" PRIu32, params->audio_bit_rate); } if (params->codec != SC_CODEC_H264) { ADD_PARAM("codec=%s", sc_server_get_codec_name(params->codec)); diff --git a/app/src/server.h b/app/src/server.h index c4d6ba65..9d8c2ec2 100644 --- a/app/src/server.h +++ b/app/src/server.h @@ -34,6 +34,7 @@ struct sc_server_params { uint16_t tunnel_port; uint16_t max_size; uint32_t bit_rate; + uint32_t audio_bit_rate; uint16_t max_fps; int8_t lock_video_orientation; bool control; diff --git a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java index 8746d324..7cda0b76 100644 --- a/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java +++ b/server/src/main/java/com/genymobile/scrcpy/AudioEncoder.java @@ -40,12 +40,12 @@ public final class AudioEncoder { private static final String MIMETYPE = MediaFormat.MIMETYPE_AUDIO_OPUS; private static final int SAMPLE_RATE = 48000; private static final int CHANNELS = 2; - private static final int BIT_RATE = 196000; private static final int BUFFER_MS = 15; // milliseconds private static final int BUFFER_SIZE = SAMPLE_RATE * CHANNELS * BUFFER_MS / 1000; private final Streamer streamer; + private final int bitRate; private AudioRecord recorder; private MediaCodec mediaCodec; @@ -63,8 +63,9 @@ public final class AudioEncoder { private boolean ended; - public AudioEncoder(Streamer streamer) { + public AudioEncoder(Streamer streamer, int bitRate) { this.streamer = streamer; + this.bitRate = bitRate; } private static AudioFormat createAudioFormat() { @@ -89,10 +90,10 @@ public final class AudioEncoder { return builder.build(); } - private static MediaFormat createFormat() { + private static MediaFormat createFormat(int bitRate) { MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, MIMETYPE); - format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE); + format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS); format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE); return format; @@ -208,7 +209,7 @@ public final class AudioEncoder { mediaCodecThread = new HandlerThread("AudioEncoder"); mediaCodecThread.start(); - MediaFormat format = createFormat(); + MediaFormat format = createFormat(bitRate); mediaCodec.setCallback(new EncoderCallback(), new Handler(mediaCodecThread.getLooper())); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); diff --git a/server/src/main/java/com/genymobile/scrcpy/Options.java b/server/src/main/java/com/genymobile/scrcpy/Options.java index 07789974..a7f2e9c7 100644 --- a/server/src/main/java/com/genymobile/scrcpy/Options.java +++ b/server/src/main/java/com/genymobile/scrcpy/Options.java @@ -12,6 +12,7 @@ public class Options { private int maxSize; private VideoCodec codec = VideoCodec.H264; private int bitRate = 8000000; + private int audioBitRate = 196000; private int maxFps; private int lockVideoOrientation = -1; private boolean tunnelForward; @@ -82,6 +83,14 @@ public class Options { this.bitRate = bitRate; } + public int getAudioBitRate() { + return audioBitRate; + } + + public void setAudioBitRate(int audioBitRate) { + this.audioBitRate = audioBitRate; + } + public int getMaxFps() { return maxFps; } diff --git a/server/src/main/java/com/genymobile/scrcpy/Server.java b/server/src/main/java/com/genymobile/scrcpy/Server.java index 48eb17e6..3aae693e 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 { AudioEncoder audioEncoder = null; if (audio) { Streamer audioStreamer = new Streamer(connection.getAudioFd(), AudioCodec.OPUS, options.getSendCodecId(), options.getSendFrameMeta()); - audioEncoder = new AudioEncoder(audioStreamer); + audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate()); audioEncoder.start(); } @@ -211,6 +211,10 @@ public final class Server { int bitRate = Integer.parseInt(value); options.setBitRate(bitRate); break; + case "audio_bit_rate": + int audioBitRate = Integer.parseInt(value); + options.setAudioBitRate(audioBitRate); + break; case "max_fps": int maxFps = Integer.parseInt(value); options.setMaxFps(maxFps);