Add --audio-codec
Introduce the selection mechanism. Alternative codecs will be added later. PR #3757 <https://github.com/Genymobile/scrcpy/pull/3757>
This commit is contained in:
parent
0870b8c8be
commit
839b842aa7
12 changed files with 69 additions and 6 deletions
|
@ -3,6 +3,7 @@ _scrcpy() {
|
||||||
local opts="
|
local opts="
|
||||||
--always-on-top
|
--always-on-top
|
||||||
--audio-bit-rate=
|
--audio-bit-rate=
|
||||||
|
--audio-codec=
|
||||||
-b --video-bit-rate=
|
-b --video-bit-rate=
|
||||||
--crop=
|
--crop=
|
||||||
-d --select-usb
|
-d --select-usb
|
||||||
|
@ -71,6 +72,10 @@ _scrcpy() {
|
||||||
COMPREPLY=($(compgen -W 'h264 h265 av1' -- "$cur"))
|
COMPREPLY=($(compgen -W 'h264 h265 av1' -- "$cur"))
|
||||||
return
|
return
|
||||||
;;
|
;;
|
||||||
|
--audio-codec)
|
||||||
|
COMPREPLY=($(compgen -W 'opus' -- "$cur"))
|
||||||
|
return
|
||||||
|
;;
|
||||||
--lock-video-orientation)
|
--lock-video-orientation)
|
||||||
COMPREPLY=($(compgen -W 'unlocked initial 0 1 2 3' -- "$cur"))
|
COMPREPLY=($(compgen -W 'unlocked initial 0 1 2 3' -- "$cur"))
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,6 +10,7 @@ local arguments
|
||||||
arguments=(
|
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)'
|
||||||
{-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]'
|
||||||
|
|
|
@ -25,6 +25,12 @@ Encode the audio at the given bit\-rate, expressed in bits/s. Unit suffixes are
|
||||||
|
|
||||||
Default is 128K (128000).
|
Default is 128K (128000).
|
||||||
|
|
||||||
|
.TP
|
||||||
|
.BI "\-\-audio\-codec " name
|
||||||
|
Select an audio codec (opus).
|
||||||
|
|
||||||
|
Default is opus.
|
||||||
|
|
||||||
.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).
|
||||||
|
|
|
@ -65,6 +65,7 @@ enum {
|
||||||
OPT_VIDEO_CODEC,
|
OPT_VIDEO_CODEC,
|
||||||
OPT_NO_AUDIO,
|
OPT_NO_AUDIO,
|
||||||
OPT_AUDIO_BIT_RATE,
|
OPT_AUDIO_BIT_RATE,
|
||||||
|
OPT_AUDIO_CODEC,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
|
@ -114,6 +115,13 @@ static const struct sc_option options[] = {
|
||||||
"Unit suffixes are supported: 'K' (x1000) and 'M' (x1000000).\n"
|
"Unit suffixes are supported: 'K' (x1000) and 'M' (x1000000).\n"
|
||||||
"Default is 128K (128000).",
|
"Default is 128K (128000).",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.longopt_id = OPT_AUDIO_CODEC,
|
||||||
|
.longopt = "audio-codec",
|
||||||
|
.argdesc = "name",
|
||||||
|
.text = "Select an audio codec (opus).\n"
|
||||||
|
"Default is opus.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 'b',
|
.shortopt = 'b',
|
||||||
.longopt = "video-bit-rate",
|
.longopt = "video-bit-rate",
|
||||||
|
@ -1452,6 +1460,16 @@ parse_video_codec(const char *optarg, enum sc_codec *codec) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_audio_codec(const char *optarg, enum sc_codec *codec) {
|
||||||
|
if (!strcmp(optarg, "opus")) {
|
||||||
|
*codec = SC_CODEC_OPUS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
LOGE("Unsupported audio codec: %s (expected opus)", optarg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
const char *optstring, const struct option *longopts) {
|
const char *optstring, const struct option *longopts) {
|
||||||
|
@ -1711,6 +1729,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case OPT_AUDIO_CODEC:
|
||||||
|
if (!parse_audio_codec(optarg, &opts->audio_codec)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case OPT_OTG:
|
case OPT_OTG:
|
||||||
#ifdef HAVE_USB
|
#ifdef HAVE_USB
|
||||||
opts->otg = true;
|
opts->otg = true;
|
||||||
|
|
|
@ -14,6 +14,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||||
#endif
|
#endif
|
||||||
.log_level = SC_LOG_LEVEL_INFO,
|
.log_level = SC_LOG_LEVEL_INFO,
|
||||||
.video_codec = SC_CODEC_H264,
|
.video_codec = SC_CODEC_H264,
|
||||||
|
.audio_codec = SC_CODEC_OPUS,
|
||||||
.record_format = SC_RECORD_FORMAT_AUTO,
|
.record_format = SC_RECORD_FORMAT_AUTO,
|
||||||
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT,
|
.keyboard_input_mode = SC_KEYBOARD_INPUT_MODE_INJECT,
|
||||||
.mouse_input_mode = SC_MOUSE_INPUT_MODE_INJECT,
|
.mouse_input_mode = SC_MOUSE_INPUT_MODE_INJECT,
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum sc_codec {
|
||||||
SC_CODEC_H264,
|
SC_CODEC_H264,
|
||||||
SC_CODEC_H265,
|
SC_CODEC_H265,
|
||||||
SC_CODEC_AV1,
|
SC_CODEC_AV1,
|
||||||
|
SC_CODEC_OPUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum sc_lock_video_orientation {
|
enum sc_lock_video_orientation {
|
||||||
|
@ -100,6 +101,7 @@ struct scrcpy_options {
|
||||||
#endif
|
#endif
|
||||||
enum sc_log_level log_level;
|
enum sc_log_level log_level;
|
||||||
enum sc_codec video_codec;
|
enum sc_codec video_codec;
|
||||||
|
enum sc_codec audio_codec;
|
||||||
enum sc_record_format record_format;
|
enum sc_record_format record_format;
|
||||||
enum sc_keyboard_input_mode keyboard_input_mode;
|
enum sc_keyboard_input_mode keyboard_input_mode;
|
||||||
enum sc_mouse_input_mode mouse_input_mode;
|
enum sc_mouse_input_mode mouse_input_mode;
|
||||||
|
|
|
@ -315,6 +315,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||||
.select_tcpip = options->select_tcpip,
|
.select_tcpip = options->select_tcpip,
|
||||||
.log_level = options->log_level,
|
.log_level = options->log_level,
|
||||||
.video_codec = options->video_codec,
|
.video_codec = options->video_codec,
|
||||||
|
.audio_codec = options->audio_codec,
|
||||||
.crop = options->crop,
|
.crop = options->crop,
|
||||||
.port_range = options->port_range,
|
.port_range = options->port_range,
|
||||||
.tunnel_host = options->tunnel_host,
|
.tunnel_host = options->tunnel_host,
|
||||||
|
|
|
@ -165,6 +165,8 @@ sc_server_get_codec_name(enum sc_codec codec) {
|
||||||
return "h265";
|
return "h265";
|
||||||
case SC_CODEC_AV1:
|
case SC_CODEC_AV1:
|
||||||
return "av1";
|
return "av1";
|
||||||
|
case SC_CODEC_OPUS:
|
||||||
|
return "opus";
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -228,6 +230,10 @@ execute_server(struct sc_server *server,
|
||||||
ADD_PARAM("video_codec=%s",
|
ADD_PARAM("video_codec=%s",
|
||||||
sc_server_get_codec_name(params->video_codec));
|
sc_server_get_codec_name(params->video_codec));
|
||||||
}
|
}
|
||||||
|
if (params->audio_codec != SC_CODEC_OPUS) {
|
||||||
|
ADD_PARAM("audio_codec=%s",
|
||||||
|
sc_server_get_codec_name(params->audio_codec));
|
||||||
|
}
|
||||||
if (params->max_size) {
|
if (params->max_size) {
|
||||||
ADD_PARAM("max_size=%" PRIu16, params->max_size);
|
ADD_PARAM("max_size=%" PRIu16, params->max_size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ struct sc_server_params {
|
||||||
const char *req_serial;
|
const char *req_serial;
|
||||||
enum sc_log_level log_level;
|
enum sc_log_level log_level;
|
||||||
enum sc_codec video_codec;
|
enum sc_codec video_codec;
|
||||||
|
enum sc_codec audio_codec;
|
||||||
const char *crop;
|
const char *crop;
|
||||||
const char *video_codec_options;
|
const char *video_codec_options;
|
||||||
const char *video_encoder;
|
const char *video_encoder;
|
||||||
|
|
|
@ -38,7 +38,6 @@ public final class AudioEncoder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String MIMETYPE = MediaFormat.MIMETYPE_AUDIO_OPUS;
|
|
||||||
private static final int SAMPLE_RATE = 48000;
|
private static final int SAMPLE_RATE = 48000;
|
||||||
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
|
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_STEREO;
|
||||||
private static final int CHANNELS = 2;
|
private static final int CHANNELS = 2;
|
||||||
|
@ -93,9 +92,9 @@ public final class AudioEncoder {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaFormat createFormat(int bitRate) {
|
private static MediaFormat createFormat(String mimeType, int bitRate) {
|
||||||
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);
|
||||||
|
@ -216,12 +215,13 @@ public final class AudioEncoder {
|
||||||
boolean mediaCodecStarted = false;
|
boolean mediaCodecStarted = false;
|
||||||
boolean recorderStarted = false;
|
boolean recorderStarted = false;
|
||||||
try {
|
try {
|
||||||
mediaCodec = MediaCodec.createEncoderByType(MIMETYPE); // may throw IOException
|
String mimeType = streamer.getCodec().getMimeType();
|
||||||
|
mediaCodec = MediaCodec.createEncoderByType(mimeType); // may throw IOException
|
||||||
|
|
||||||
mediaCodecThread = new HandlerThread("AudioEncoder");
|
mediaCodecThread = new HandlerThread("AudioEncoder");
|
||||||
mediaCodecThread.start();
|
mediaCodecThread.start();
|
||||||
|
|
||||||
MediaFormat format = createFormat(bitRate);
|
MediaFormat format = createFormat(mimeType, 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);
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@ public class Options {
|
||||||
private boolean audio = true;
|
private boolean audio = true;
|
||||||
private int maxSize;
|
private int maxSize;
|
||||||
private VideoCodec videoCodec = VideoCodec.H264;
|
private VideoCodec videoCodec = VideoCodec.H264;
|
||||||
|
private AudioCodec audioCodec = AudioCodec.OPUS;
|
||||||
private int videoBitRate = 8000000;
|
private int videoBitRate = 8000000;
|
||||||
private int audioBitRate = 128000;
|
private int audioBitRate = 128000;
|
||||||
private int maxFps;
|
private int maxFps;
|
||||||
|
@ -75,6 +76,14 @@ public class Options {
|
||||||
this.videoCodec = videoCodec;
|
this.videoCodec = videoCodec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AudioCodec getAudioCodec() {
|
||||||
|
return audioCodec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioCodec(AudioCodec audioCodec) {
|
||||||
|
this.audioCodec = audioCodec;
|
||||||
|
}
|
||||||
|
|
||||||
public int getVideoBitRate() {
|
public int getVideoBitRate() {
|
||||||
return videoBitRate;
|
return videoBitRate;
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,7 +109,8 @@ public final class Server {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio) {
|
if (audio) {
|
||||||
Streamer audioStreamer = new Streamer(connection.getAudioFd(), AudioCodec.OPUS, options.getSendCodecId(), options.getSendFrameMeta());
|
Streamer audioStreamer = new Streamer(connection.getAudioFd(), options.getAudioCodec(), options.getSendCodecId(),
|
||||||
|
options.getSendFrameMeta());
|
||||||
audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate());
|
audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate());
|
||||||
audioEncoder.start();
|
audioEncoder.start();
|
||||||
}
|
}
|
||||||
|
@ -202,6 +203,13 @@ public final class Server {
|
||||||
}
|
}
|
||||||
options.setVideoCodec(videoCodec);
|
options.setVideoCodec(videoCodec);
|
||||||
break;
|
break;
|
||||||
|
case "audio_codec":
|
||||||
|
AudioCodec audioCodec = AudioCodec.findByName(value);
|
||||||
|
if (audioCodec == null) {
|
||||||
|
throw new IllegalArgumentException("Audio codec " + value + " not supported");
|
||||||
|
}
|
||||||
|
options.setAudioCodec(audioCodec);
|
||||||
|
break;
|
||||||
case "max_size":
|
case "max_size":
|
||||||
int maxSize = Integer.parseInt(value) & ~7; // multiple of 8
|
int maxSize = Integer.parseInt(value) & ~7; // multiple of 8
|
||||||
options.setMaxSize(maxSize);
|
options.setMaxSize(maxSize);
|
||||||
|
|
Loading…
Reference in a new issue