mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-04 07:09:06 +00:00
Add --audio-encoder option
Similar to --video-encoder, but for audio.
This commit is contained in:
parent
0e85424ffb
commit
d31f858204
10 changed files with 63 additions and 6 deletions
|
@ -31,6 +31,10 @@ Select an audio codec (opus or aac).
|
||||||
|
|
||||||
Default is opus.
|
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
|
.TP
|
||||||
.BI "\-b, \-\-bit\-rate " value
|
.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).
|
Encode the video at the given bit\-rate, expressed in bits/s. Unit suffixes are supported: '\fBK\fR' (x1000) and '\fBM\fR' (x1000000).
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
#define OPT_NO_AUDIO 1041
|
#define OPT_NO_AUDIO 1041
|
||||||
#define OPT_AUDIO_BIT_RATE 1042
|
#define OPT_AUDIO_BIT_RATE 1042
|
||||||
#define OPT_AUDIO_CODEC 1043
|
#define OPT_AUDIO_CODEC 1043
|
||||||
|
#define OPT_AUDIO_ENCODER_NAME 1044
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
char shortopt;
|
char shortopt;
|
||||||
|
@ -116,6 +117,13 @@ 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_ENCODER_NAME,
|
||||||
|
.longopt = "audio-encoder",
|
||||||
|
.argdesc = "name",
|
||||||
|
.text = "Use a specific MediaCodec audio encoder (depending on the "
|
||||||
|
"codec provided by --audio-codec).",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 'b',
|
.shortopt = 'b',
|
||||||
.longopt = "bit-rate",
|
.longopt = "bit-rate",
|
||||||
|
@ -1635,6 +1643,9 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
case OPT_ENCODER_NAME:
|
case OPT_ENCODER_NAME:
|
||||||
opts->encoder_name = optarg;
|
opts->encoder_name = optarg;
|
||||||
break;
|
break;
|
||||||
|
case OPT_AUDIO_ENCODER_NAME:
|
||||||
|
opts->audio_encoder_name = optarg;
|
||||||
|
break;
|
||||||
case OPT_FORCE_ADB_FORWARD:
|
case OPT_FORCE_ADB_FORWARD:
|
||||||
opts->force_adb_forward = true;
|
opts->force_adb_forward = true;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -97,6 +97,7 @@ struct scrcpy_options {
|
||||||
const char *render_driver;
|
const char *render_driver;
|
||||||
const char *codec_options;
|
const char *codec_options;
|
||||||
const char *encoder_name;
|
const char *encoder_name;
|
||||||
|
const char *audio_encoder_name;
|
||||||
#ifdef HAVE_V4L2
|
#ifdef HAVE_V4L2
|
||||||
const char *v4l2_device;
|
const char *v4l2_device;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -368,6 +368,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||||
.stay_awake = options->stay_awake,
|
.stay_awake = options->stay_awake,
|
||||||
.codec_options = options->codec_options,
|
.codec_options = options->codec_options,
|
||||||
.encoder_name = options->encoder_name,
|
.encoder_name = options->encoder_name,
|
||||||
|
.audio_encoder_name = options->audio_encoder_name,
|
||||||
.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,
|
||||||
.clipboard_autosync = options->clipboard_autosync,
|
.clipboard_autosync = options->clipboard_autosync,
|
||||||
|
|
|
@ -73,6 +73,7 @@ sc_server_params_destroy(struct sc_server_params *params) {
|
||||||
free((char *) params->crop);
|
free((char *) params->crop);
|
||||||
free((char *) params->codec_options);
|
free((char *) params->codec_options);
|
||||||
free((char *) params->encoder_name);
|
free((char *) params->encoder_name);
|
||||||
|
free((char *) params->audio_encoder_name);
|
||||||
free((char *) params->tcpip_dst);
|
free((char *) params->tcpip_dst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,6 +98,7 @@ sc_server_params_copy(struct sc_server_params *dst,
|
||||||
COPY(crop);
|
COPY(crop);
|
||||||
COPY(codec_options);
|
COPY(codec_options);
|
||||||
COPY(encoder_name);
|
COPY(encoder_name);
|
||||||
|
COPY(audio_encoder_name);
|
||||||
COPY(tcpip_dst);
|
COPY(tcpip_dst);
|
||||||
#undef COPY
|
#undef COPY
|
||||||
|
|
||||||
|
@ -270,6 +272,9 @@ execute_server(struct sc_server *server,
|
||||||
if (params->encoder_name) {
|
if (params->encoder_name) {
|
||||||
ADD_PARAM("encoder_name=%s", 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) {
|
if (params->power_off_on_close) {
|
||||||
ADD_PARAM("power_off_on_close=true");
|
ADD_PARAM("power_off_on_close=true");
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ struct sc_server_params {
|
||||||
const char *crop;
|
const char *crop;
|
||||||
const char *codec_options;
|
const char *codec_options;
|
||||||
const char *encoder_name;
|
const char *encoder_name;
|
||||||
|
const char *audio_encoder_name;
|
||||||
struct sc_port_range port_range;
|
struct sc_port_range port_range;
|
||||||
uint32_t tunnel_host;
|
uint32_t tunnel_host;
|
||||||
uint16_t tunnel_port;
|
uint16_t tunnel_port;
|
||||||
|
|
|
@ -45,6 +45,7 @@ public final class AudioEncoder {
|
||||||
|
|
||||||
private final Streamer streamer;
|
private final Streamer streamer;
|
||||||
private final int bitRate;
|
private final int bitRate;
|
||||||
|
private final String encoderName;
|
||||||
|
|
||||||
private AudioRecord recorder;
|
private AudioRecord recorder;
|
||||||
private MediaCodec mediaCodec;
|
private MediaCodec mediaCodec;
|
||||||
|
@ -62,9 +63,10 @@ public final class AudioEncoder {
|
||||||
|
|
||||||
private boolean ended;
|
private boolean ended;
|
||||||
|
|
||||||
public AudioEncoder(Streamer streamer, int bitRate) {
|
public AudioEncoder(Streamer streamer, int bitRate, String encoderName) {
|
||||||
this.streamer = streamer;
|
this.streamer = streamer;
|
||||||
this.bitRate = bitRate;
|
this.bitRate = bitRate;
|
||||||
|
this.encoderName = encoderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AudioFormat createAudioFormat() {
|
private static AudioFormat createAudioFormat() {
|
||||||
|
@ -208,15 +210,15 @@ public final class AudioEncoder {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
String mimeType = streamer.getCodec().getMimeType();
|
Codec codec = streamer.getCodec();
|
||||||
|
|
||||||
mediaCodec = MediaCodec.createEncoderByType(mimeType); // may throw IOException
|
mediaCodec = createMediaCodec(codec, encoderName);
|
||||||
recorder = createAudioRecord();
|
recorder = createAudioRecord();
|
||||||
|
|
||||||
mediaCodecThread = new HandlerThread("AudioEncoder");
|
mediaCodecThread = new HandlerThread("AudioEncoder");
|
||||||
mediaCodecThread.start();
|
mediaCodecThread.start();
|
||||||
|
|
||||||
MediaFormat format = createFormat(mimeType, bitRate);
|
MediaFormat format = createFormat(codec.getMimeType(), bitRate);
|
||||||
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);
|
||||||
|
|
||||||
|
@ -255,6 +257,8 @@ public final class AudioEncoder {
|
||||||
mediaCodec.start();
|
mediaCodec.start();
|
||||||
inputThread.start();
|
inputThread.start();
|
||||||
outputThread.start();
|
outputThread.start();
|
||||||
|
} catch (ConfigurationException e) {
|
||||||
|
// Do not print stack trace, a user-friendly error-message has already been logged
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
if (mediaCodec != null) {
|
if (mediaCodec != null) {
|
||||||
mediaCodec.release();
|
mediaCodec.release();
|
||||||
|
@ -272,6 +276,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 void cleanUp() {
|
private void cleanUp() {
|
||||||
mediaCodecThread.getLooper().quit();
|
mediaCodecThread.getLooper().quit();
|
||||||
inputThread.interrupt();
|
inputThread.interrupt();
|
||||||
|
|
|
@ -18,8 +18,10 @@ public final class CodecUtils {
|
||||||
MediaCodecInfo[] encoders = listEncoders(codec.getMimeType());
|
MediaCodecInfo[] encoders = listEncoders(codec.getMimeType());
|
||||||
if (encoders != null && encoders.length > 0) {
|
if (encoders != null && encoders.length > 0) {
|
||||||
msg.append("\nTry to use one of the available encoders:");
|
msg.append("\nTry to use one of the available encoders:");
|
||||||
|
String codecOption = codec.getType() == Codec.Type.VIDEO ? "codec" : "audio-codec";
|
||||||
for (MediaCodecInfo encoder : encoders) {
|
for (MediaCodecInfo encoder : encoders) {
|
||||||
msg.append("\n scrcpy --codec=").append(codec.getName()).append(" --encoder='").append(encoder.getName()).append("'");
|
msg.append("\n scrcpy --").append(codecOption).append("=").append(codec.getName());
|
||||||
|
msg.append(" --encoder='").append(encoder.getName()).append("'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return msg.toString();
|
return msg.toString();
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class Options {
|
||||||
private boolean stayAwake;
|
private boolean stayAwake;
|
||||||
private List<CodecOption> codecOptions;
|
private List<CodecOption> codecOptions;
|
||||||
private String encoderName;
|
private String encoderName;
|
||||||
|
private String audioEncoderName;
|
||||||
private boolean powerOffScreenOnClose;
|
private boolean powerOffScreenOnClose;
|
||||||
private boolean clipboardAutosync = true;
|
private boolean clipboardAutosync = true;
|
||||||
private boolean downsizeOnError = true;
|
private boolean downsizeOnError = true;
|
||||||
|
@ -180,6 +181,14 @@ public class Options {
|
||||||
this.encoderName = encoderName;
|
this.encoderName = encoderName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getAudioEncoderName() {
|
||||||
|
return audioEncoderName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioEncoderName(String audioEncoderName) {
|
||||||
|
this.audioEncoderName = audioEncoderName;
|
||||||
|
}
|
||||||
|
|
||||||
public void setPowerOffScreenOnClose(boolean powerOffScreenOnClose) {
|
public void setPowerOffScreenOnClose(boolean powerOffScreenOnClose) {
|
||||||
this.powerOffScreenOnClose = powerOffScreenOnClose;
|
this.powerOffScreenOnClose = powerOffScreenOnClose;
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,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.getAudioEncoderName());
|
||||||
audioEncoder.start();
|
audioEncoder.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -264,6 +264,10 @@ public final class Server {
|
||||||
options.setEncoderName(value);
|
options.setEncoderName(value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "audio_encoder_name":
|
||||||
|
if (!value.isEmpty()) {
|
||||||
|
options.setAudioEncoderName(value);
|
||||||
|
}
|
||||||
case "power_off_on_close":
|
case "power_off_on_close":
|
||||||
boolean powerOffScreenOnClose = Boolean.parseBoolean(value);
|
boolean powerOffScreenOnClose = Boolean.parseBoolean(value);
|
||||||
options.setPowerOffScreenOnClose(powerOffScreenOnClose);
|
options.setPowerOffScreenOnClose(powerOffScreenOnClose);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue