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
--audio-bit-rate=
--audio-codec=
--audio-codec-options=
-b --video-bit-rate=
--crop=
-d --select-usb

View file

@ -11,6 +11,7 @@ arguments=(
'--always-on-top[Make scrcpy window always on top \(above other windows\)]'
'--audio-bit-rate=[Encode the audio at the given bit-rate]'
'--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]'
'--crop=[\[width\:height\:x\:y\] Crop the device screen on the server]'
{-d,--select-usb}'[Use USB device]'

View file

@ -31,6 +31,16 @@ Select an audio codec (opus or aac).
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
.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).

View file

@ -66,6 +66,7 @@ enum {
OPT_NO_AUDIO,
OPT_AUDIO_BIT_RATE,
OPT_AUDIO_CODEC,
OPT_AUDIO_CODEC_OPTIONS,
};
struct sc_option {
@ -122,6 +123,18 @@ static const struct sc_option options[] = {
.text = "Select an audio codec (opus or aac).\n"
"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',
.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:
opts->video_codec_options = optarg;
break;
case OPT_AUDIO_CODEC_OPTIONS:
opts->audio_codec_options = optarg;
break;
case OPT_ENCODER:
LOGW("--encoder is deprecated, use --video-encoder instead.");
// fall through

View file

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

View file

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

View file

@ -331,6 +331,7 @@ scrcpy(struct scrcpy_options *options) {
.show_touches = options->show_touches,
.stay_awake = options->stay_awake,
.video_codec_options = options->video_codec_options,
.audio_codec_options = options->audio_codec_options,
.video_encoder = options->video_encoder,
.force_adb_forward = options->force_adb_forward,
.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->crop);
free((char *) params->video_codec_options);
free((char *) params->audio_codec_options);
free((char *) params->video_encoder);
free((char *) params->tcpip_dst);
}
@ -96,6 +97,7 @@ sc_server_params_copy(struct sc_server_params *dst,
COPY(req_serial);
COPY(crop);
COPY(video_codec_options);
COPY(audio_codec_options);
COPY(video_encoder);
COPY(tcpip_dst);
#undef COPY
@ -268,6 +270,9 @@ execute_server(struct sc_server *server,
if (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) {
ADD_PARAM("video_encoder=%s", params->video_encoder);
}

View file

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

View file

@ -15,6 +15,7 @@ import android.os.Looper;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
@ -49,6 +50,7 @@ public final class AudioEncoder {
private final Streamer streamer;
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).
// So many pending tasks would lead to an unacceptable delay anyway.
@ -63,9 +65,10 @@ public final class AudioEncoder {
private boolean ended;
public AudioEncoder(Streamer streamer, int bitRate) {
public AudioEncoder(Streamer streamer, int bitRate, List<CodecOption> codecOptions) {
this.streamer = streamer;
this.bitRate = bitRate;
this.codecOptions = codecOptions;
}
private static AudioFormat createAudioFormat() {
@ -92,12 +95,22 @@ public final class AudioEncoder {
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();
format.setString(MediaFormat.KEY_MIME, mimeType);
format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, CHANNELS);
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;
}
@ -221,7 +234,7 @@ public final class AudioEncoder {
mediaCodecThread = new HandlerThread("AudioEncoder");
mediaCodecThread.start();
MediaFormat format = createFormat(mimeType, bitRate);
MediaFormat format = createFormat(mimeType, bitRate, codecOptions);
mediaCodec.setCallback(new EncoderCallback(), new Handler(mediaCodecThread.getLooper()));
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

View file

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

View file

@ -111,7 +111,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.getAudioCodecOptions());
audioEncoder.start();
}
@ -258,6 +258,10 @@ public final class Server {
List<CodecOption> videoCodecOptions = CodecOption.parse(value);
options.setVideoCodecOptions(videoCodecOptions);
break;
case "audio_codec_options":
List<CodecOption> audioCodecOptions = CodecOption.parse(value);
options.setAudioCodecOptions(audioCodecOptions);
break;
case "video_encoder":
if (!value.isEmpty()) {
options.setVideoEncoder(value);