Add --audio-codec-options

Similar to --video-codec-options, but for audio.

PR #3757 <https://github.com/Genymobile/scrcpy/pull/3757>
This commit is contained in:
Romain Vimont 2023-02-22 22:48:23 +01:00
parent 58cf8e5401
commit b03c864c70
12 changed files with 68 additions and 4 deletions

View file

@ -4,6 +4,7 @@ _scrcpy() {
--always-on-top --always-on-top
--audio-bit-rate= --audio-bit-rate=
--audio-codec= --audio-codec=
--audio-codec-options=
-b --video-bit-rate= -b --video-bit-rate=
--crop= --crop=
-d --select-usb -d --select-usb

View file

@ -11,6 +11,7 @@ arguments=(
'--always-on-top[Make scrcpy window always on top \(above other windows\)]' '--always-on-top[Make scrcpy window always on top \(above other windows\)]'
'--audio-bit-rate=[Encode the audio at the given bit-rate]' '--audio-bit-rate=[Encode the audio at the given bit-rate]'
'--audio-codec=[Select the audio codec]:codec:(opus aac)' '--audio-codec=[Select the audio codec]:codec:(opus aac)'
'--audio-codec-options=[Set a list of comma-separated key\:type=value options for the device audio encoder]'
{-b,--video-bit-rate=}'[Encode the video at the given bit-rate]' {-b,--video-bit-rate=}'[Encode the video at the given bit-rate]'
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]' '--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
{-d,--select-usb}'[Use USB device]' {-d,--select-usb}'[Use USB device]'

View file

@ -31,6 +31,16 @@ Select an audio codec (opus or aac).
Default is opus. Default is opus.
.TP
.BI "\-\-audio\-codec\-options " key\fR[:\fItype\fR]=\fIvalue\fR[,...]
Set a list of comma-separated key:type=value options for the device audio encoder.
The possible values for 'type' are 'int' (default), 'long', 'float' and 'string'.
The list of possible codec options is available in the Android documentation
.UR https://d.android.com/reference/android/media/MediaFormat
.UE .
.TP .TP
.BI "\-b, \-\-video\-bit\-rate " value .BI "\-b, \-\-video\-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). Encode the video at the given bit\-rate, expressed in bits/s. Unit suffixes are supported: '\fBK\fR' (x1000) and '\fBM\fR' (x1000000).

View file

@ -66,6 +66,7 @@ enum {
OPT_NO_AUDIO, OPT_NO_AUDIO,
OPT_AUDIO_BIT_RATE, OPT_AUDIO_BIT_RATE,
OPT_AUDIO_CODEC, OPT_AUDIO_CODEC,
OPT_AUDIO_CODEC_OPTIONS,
}; };
struct sc_option { struct sc_option {
@ -122,6 +123,18 @@ static const struct sc_option options[] = {
.text = "Select an audio codec (opus or aac).\n" .text = "Select an audio codec (opus or aac).\n"
"Default is opus.", "Default is opus.",
}, },
{
.longopt_id = OPT_AUDIO_CODEC_OPTIONS,
.longopt = "audio-codec-options",
.argdesc = "key[:type]=value[,...]",
.text = "Set a list of comma-separated key:type=value options for the "
"device audio encoder.\n"
"The possible values for 'type' are 'int' (default), 'long', "
"'float' and 'string'.\n"
"The list of possible codec options is available in the "
"Android documentation: "
"<https://d.android.com/reference/android/media/MediaFormat>",
},
{ {
.shortopt = 'b', .shortopt = 'b',
.longopt = "video-bit-rate", .longopt = "video-bit-rate",
@ -1672,6 +1685,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
case OPT_VIDEO_CODEC_OPTIONS: case OPT_VIDEO_CODEC_OPTIONS:
opts->video_codec_options = optarg; opts->video_codec_options = optarg;
break; break;
case OPT_AUDIO_CODEC_OPTIONS:
opts->audio_codec_options = optarg;
break;
case OPT_ENCODER: case OPT_ENCODER:
LOGW("--encoder is deprecated, use --video-encoder instead."); LOGW("--encoder is deprecated, use --video-encoder instead.");
// fall through // fall through

View file

@ -8,6 +8,7 @@ const struct scrcpy_options scrcpy_options_default = {
.push_target = NULL, .push_target = NULL,
.render_driver = NULL, .render_driver = NULL,
.video_codec_options = NULL, .video_codec_options = NULL,
.audio_codec_options = NULL,
.video_encoder = NULL, .video_encoder = NULL,
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
.v4l2_device = NULL, .v4l2_device = NULL,

View file

@ -96,6 +96,7 @@ struct scrcpy_options {
const char *push_target; const char *push_target;
const char *render_driver; const char *render_driver;
const char *video_codec_options; const char *video_codec_options;
const char *audio_codec_options;
const char *video_encoder; const char *video_encoder;
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
const char *v4l2_device; const char *v4l2_device;

View file

@ -331,6 +331,7 @@ scrcpy(struct scrcpy_options *options) {
.show_touches = options->show_touches, .show_touches = options->show_touches,
.stay_awake = options->stay_awake, .stay_awake = options->stay_awake,
.video_codec_options = options->video_codec_options, .video_codec_options = options->video_codec_options,
.audio_codec_options = options->audio_codec_options,
.video_encoder = options->video_encoder, .video_encoder = options->video_encoder,
.force_adb_forward = options->force_adb_forward, .force_adb_forward = options->force_adb_forward,
.power_off_on_close = options->power_off_on_close, .power_off_on_close = options->power_off_on_close,

View file

@ -72,6 +72,7 @@ sc_server_params_destroy(struct sc_server_params *params) {
free((char *) params->req_serial); free((char *) params->req_serial);
free((char *) params->crop); free((char *) params->crop);
free((char *) params->video_codec_options); free((char *) params->video_codec_options);
free((char *) params->audio_codec_options);
free((char *) params->video_encoder); free((char *) params->video_encoder);
free((char *) params->tcpip_dst); free((char *) params->tcpip_dst);
} }
@ -96,6 +97,7 @@ sc_server_params_copy(struct sc_server_params *dst,
COPY(req_serial); COPY(req_serial);
COPY(crop); COPY(crop);
COPY(video_codec_options); COPY(video_codec_options);
COPY(audio_codec_options);
COPY(video_encoder); COPY(video_encoder);
COPY(tcpip_dst); COPY(tcpip_dst);
#undef COPY #undef COPY
@ -268,6 +270,9 @@ execute_server(struct sc_server *server,
if (params->video_codec_options) { if (params->video_codec_options) {
ADD_PARAM("video_codec_options=%s", params->video_codec_options); ADD_PARAM("video_codec_options=%s", params->video_codec_options);
} }
if (params->audio_codec_options) {
ADD_PARAM("audio_codec_options=%s", params->audio_codec_options);
}
if (params->video_encoder) { if (params->video_encoder) {
ADD_PARAM("video_encoder=%s", params->video_encoder); ADD_PARAM("video_encoder=%s", params->video_encoder);
} }

View file

@ -29,6 +29,7 @@ struct sc_server_params {
enum sc_codec audio_codec; enum sc_codec audio_codec;
const char *crop; const char *crop;
const char *video_codec_options; const char *video_codec_options;
const char *audio_codec_options;
const char *video_encoder; const char *video_encoder;
struct sc_port_range port_range; struct sc_port_range port_range;
uint32_t tunnel_host; uint32_t tunnel_host;

View file

@ -15,6 +15,7 @@ import android.os.Looper;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@ -49,6 +50,7 @@ public final class AudioEncoder {
private final Streamer streamer; private final Streamer streamer;
private final int bitRate; private final int bitRate;
private final List<CodecOption> codecOptions;
// Capacity of 64 is in practice "infinite" (it is limited by the number of available MediaCodec buffers, typically 4). // 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. // So many pending tasks would lead to an unacceptable delay anyway.
@ -63,9 +65,10 @@ public final class AudioEncoder {
private boolean ended; private boolean ended;
public AudioEncoder(Streamer streamer, int bitRate) { public AudioEncoder(Streamer streamer, int bitRate, List<CodecOption> codecOptions) {
this.streamer = streamer; this.streamer = streamer;
this.bitRate = bitRate; this.bitRate = bitRate;
this.codecOptions = codecOptions;
} }
private static AudioFormat createAudioFormat() { private static AudioFormat createAudioFormat() {
@ -92,12 +95,22 @@ public final class AudioEncoder {
return builder.build(); return builder.build();
} }
private static MediaFormat createFormat(String mimeType, int bitRate) { private static MediaFormat createFormat(String mimeType, int bitRate, List<CodecOption> codecOptions) {
MediaFormat format = new MediaFormat(); MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, mimeType); format.setString(MediaFormat.KEY_MIME, mimeType);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate); format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE); format.setInteger(MediaFormat.KEY_SAMPLE_RATE, SAMPLE_RATE);
if (codecOptions != null) {
for (CodecOption option : codecOptions) {
String key = option.getKey();
Object value = option.getValue();
CodecUtils.setCodecOption(format, key, value);
Ln.d("Audio codec option set: " + key + " (" + value.getClass().getSimpleName() + ") = " + value);
}
}
return format; return format;
} }
@ -221,7 +234,7 @@ public final class AudioEncoder {
mediaCodecThread = new HandlerThread("AudioEncoder"); mediaCodecThread = new HandlerThread("AudioEncoder");
mediaCodecThread.start(); mediaCodecThread.start();
MediaFormat format = createFormat(mimeType, bitRate); MediaFormat format = createFormat(mimeType, bitRate, codecOptions);
mediaCodec.setCallback(new EncoderCallback(), new Handler(mediaCodecThread.getLooper())); mediaCodec.setCallback(new EncoderCallback(), new Handler(mediaCodecThread.getLooper()));
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

View file

@ -23,6 +23,8 @@ public class Options {
private boolean showTouches; private boolean showTouches;
private boolean stayAwake; private boolean stayAwake;
private List<CodecOption> videoCodecOptions; private List<CodecOption> videoCodecOptions;
private List<CodecOption> audioCodecOptions;
private String videoEncoder; private String videoEncoder;
private boolean powerOffScreenOnClose; private boolean powerOffScreenOnClose;
private boolean clipboardAutosync = true; private boolean clipboardAutosync = true;
@ -172,6 +174,14 @@ public class Options {
this.videoCodecOptions = videoCodecOptions; this.videoCodecOptions = videoCodecOptions;
} }
public List<CodecOption> getAudioCodecOptions() {
return audioCodecOptions;
}
public void setAudioCodecOptions(List<CodecOption> audioCodecOptions) {
this.audioCodecOptions = audioCodecOptions;
}
public String getVideoEncoder() { public String getVideoEncoder() {
return videoEncoder; return videoEncoder;
} }

View file

@ -111,7 +111,7 @@ public final class Server {
if (audio) { if (audio) {
Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(), Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(),
options.getSendFrameMeta()); options.getSendFrameMeta());
audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate()); audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate(), options.getAudioCodecOptions());
audioEncoder.start(); audioEncoder.start();
} }
@ -258,6 +258,10 @@ public final class Server {
List<CodecOption> videoCodecOptions = CodecOption.parse(value); List<CodecOption> videoCodecOptions = CodecOption.parse(value);
options.setVideoCodecOptions(videoCodecOptions); options.setVideoCodecOptions(videoCodecOptions);
break; break;
case "audio_codec_options":
List<CodecOption> audioCodecOptions = CodecOption.parse(value);
options.setAudioCodecOptions(audioCodecOptions);
break;
case "video_encoder": case "video_encoder":
if (!value.isEmpty()) { if (!value.isEmpty()) {
options.setVideoEncoder(value); options.setVideoEncoder(value);