Add --audio-bit-rate
Add an option to configure the audio bit-rate. PR #3757 <https://github.com/Genymobile/scrcpy/pull/3757>
This commit is contained in:
parent
8e640dc90f
commit
0870b8c8be
12 changed files with 48 additions and 6 deletions
|
@ -2,6 +2,7 @@ _scrcpy() {
|
||||||
local cur prev words cword
|
local cur prev words cword
|
||||||
local opts="
|
local opts="
|
||||||
--always-on-top
|
--always-on-top
|
||||||
|
--audio-bit-rate=
|
||||||
-b --video-bit-rate=
|
-b --video-bit-rate=
|
||||||
--crop=
|
--crop=
|
||||||
-d --select-usb
|
-d --select-usb
|
||||||
|
|
|
@ -9,6 +9,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]'
|
||||||
{-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]'
|
||||||
|
|
|
@ -19,6 +19,12 @@ provides display and control of Android devices connected on USB (or over TCP/IP
|
||||||
.B \-\-always\-on\-top
|
.B \-\-always\-on\-top
|
||||||
Make scrcpy window always on top (above other windows).
|
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 128K (128000).
|
||||||
|
|
||||||
.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).
|
||||||
|
|
|
@ -64,6 +64,7 @@ enum {
|
||||||
OPT_CODEC,
|
OPT_CODEC,
|
||||||
OPT_VIDEO_CODEC,
|
OPT_VIDEO_CODEC,
|
||||||
OPT_NO_AUDIO,
|
OPT_NO_AUDIO,
|
||||||
|
OPT_AUDIO_BIT_RATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sc_option {
|
struct sc_option {
|
||||||
|
@ -105,6 +106,14 @@ static const struct sc_option options[] = {
|
||||||
.longopt = "always-on-top",
|
.longopt = "always-on-top",
|
||||||
.text = "Make scrcpy window always on top (above other windows).",
|
.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 128K (128000).",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
.shortopt = 'b',
|
.shortopt = 'b',
|
||||||
.longopt = "video-bit-rate",
|
.longopt = "video-bit-rate",
|
||||||
|
@ -1461,6 +1470,11 @@ parse_args_with_getopt(struct scrcpy_cli_args *args, int argc, char *argv[],
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case OPT_AUDIO_BIT_RATE:
|
||||||
|
if (!parse_bit_rate(optarg, &opts->audio_bit_rate)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case OPT_CROP:
|
case OPT_CROP:
|
||||||
opts->crop = optarg;
|
opts->crop = optarg;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -29,6 +29,7 @@ const struct scrcpy_options scrcpy_options_default = {
|
||||||
},
|
},
|
||||||
.max_size = 0,
|
.max_size = 0,
|
||||||
.video_bit_rate = 0,
|
.video_bit_rate = 0,
|
||||||
|
.audio_bit_rate = 0,
|
||||||
.max_fps = 0,
|
.max_fps = 0,
|
||||||
.lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED,
|
.lock_video_orientation = SC_LOCK_VIDEO_ORIENTATION_UNLOCKED,
|
||||||
.rotation = 0,
|
.rotation = 0,
|
||||||
|
|
|
@ -109,6 +109,7 @@ struct scrcpy_options {
|
||||||
struct sc_shortcut_mods shortcut_mods;
|
struct sc_shortcut_mods shortcut_mods;
|
||||||
uint16_t max_size;
|
uint16_t max_size;
|
||||||
uint32_t video_bit_rate;
|
uint32_t video_bit_rate;
|
||||||
|
uint32_t audio_bit_rate;
|
||||||
uint16_t max_fps;
|
uint16_t max_fps;
|
||||||
enum sc_lock_video_orientation lock_video_orientation;
|
enum sc_lock_video_orientation lock_video_orientation;
|
||||||
uint8_t rotation;
|
uint8_t rotation;
|
||||||
|
|
|
@ -321,6 +321,7 @@ scrcpy(struct scrcpy_options *options) {
|
||||||
.tunnel_port = options->tunnel_port,
|
.tunnel_port = options->tunnel_port,
|
||||||
.max_size = options->max_size,
|
.max_size = options->max_size,
|
||||||
.video_bit_rate = options->video_bit_rate,
|
.video_bit_rate = options->video_bit_rate,
|
||||||
|
.audio_bit_rate = options->audio_bit_rate,
|
||||||
.max_fps = options->max_fps,
|
.max_fps = options->max_fps,
|
||||||
.lock_video_orientation = options->lock_video_orientation,
|
.lock_video_orientation = options->lock_video_orientation,
|
||||||
.control = options->control,
|
.control = options->control,
|
||||||
|
|
|
@ -221,6 +221,8 @@ execute_server(struct sc_server *server,
|
||||||
}
|
}
|
||||||
if (!params->audio) {
|
if (!params->audio) {
|
||||||
ADD_PARAM("audio=false");
|
ADD_PARAM("audio=false");
|
||||||
|
} else if (params->audio_bit_rate) {
|
||||||
|
ADD_PARAM("audio_bit_rate=%" PRIu32, params->audio_bit_rate);
|
||||||
}
|
}
|
||||||
if (params->video_codec != SC_CODEC_H264) {
|
if (params->video_codec != SC_CODEC_H264) {
|
||||||
ADD_PARAM("video_codec=%s",
|
ADD_PARAM("video_codec=%s",
|
||||||
|
|
|
@ -34,6 +34,7 @@ struct sc_server_params {
|
||||||
uint16_t tunnel_port;
|
uint16_t tunnel_port;
|
||||||
uint16_t max_size;
|
uint16_t max_size;
|
||||||
uint32_t video_bit_rate;
|
uint32_t video_bit_rate;
|
||||||
|
uint32_t audio_bit_rate;
|
||||||
uint16_t max_fps;
|
uint16_t max_fps;
|
||||||
int8_t lock_video_orientation;
|
int8_t lock_video_orientation;
|
||||||
bool control;
|
bool control;
|
||||||
|
|
|
@ -44,12 +44,12 @@ public final class AudioEncoder {
|
||||||
private static final int CHANNELS = 2;
|
private static final int CHANNELS = 2;
|
||||||
private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;
|
private static final int FORMAT = AudioFormat.ENCODING_PCM_16BIT;
|
||||||
private static final int BYTES_PER_SAMPLE = 2;
|
private static final int BYTES_PER_SAMPLE = 2;
|
||||||
private static final int BIT_RATE = 128000;
|
|
||||||
|
|
||||||
private static final int READ_MS = 5; // milliseconds
|
private static final int READ_MS = 5; // milliseconds
|
||||||
private static final int READ_SIZE = SAMPLE_RATE * CHANNELS * BYTES_PER_SAMPLE * READ_MS / 1000;
|
private static final int READ_SIZE = SAMPLE_RATE * CHANNELS * BYTES_PER_SAMPLE * READ_MS / 1000;
|
||||||
|
|
||||||
private final Streamer streamer;
|
private final Streamer streamer;
|
||||||
|
private final int bitRate;
|
||||||
|
|
||||||
// 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.
|
||||||
|
@ -64,8 +64,9 @@ public final class AudioEncoder {
|
||||||
|
|
||||||
private boolean ended;
|
private boolean ended;
|
||||||
|
|
||||||
public AudioEncoder(Streamer streamer) {
|
public AudioEncoder(Streamer streamer, int bitRate) {
|
||||||
this.streamer = streamer;
|
this.streamer = streamer;
|
||||||
|
this.bitRate = bitRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AudioFormat createAudioFormat() {
|
private static AudioFormat createAudioFormat() {
|
||||||
|
@ -92,10 +93,10 @@ public final class AudioEncoder {
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaFormat createFormat() {
|
private static MediaFormat createFormat(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, BIT_RATE);
|
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);
|
||||||
return format;
|
return format;
|
||||||
|
@ -220,7 +221,7 @@ public final class AudioEncoder {
|
||||||
mediaCodecThread = new HandlerThread("AudioEncoder");
|
mediaCodecThread = new HandlerThread("AudioEncoder");
|
||||||
mediaCodecThread.start();
|
mediaCodecThread.start();
|
||||||
|
|
||||||
MediaFormat format = createFormat();
|
MediaFormat format = createFormat(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);
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,7 @@ public class Options {
|
||||||
private int maxSize;
|
private int maxSize;
|
||||||
private VideoCodec videoCodec = VideoCodec.H264;
|
private VideoCodec videoCodec = VideoCodec.H264;
|
||||||
private int videoBitRate = 8000000;
|
private int videoBitRate = 8000000;
|
||||||
|
private int audioBitRate = 128000;
|
||||||
private int maxFps;
|
private int maxFps;
|
||||||
private int lockVideoOrientation = -1;
|
private int lockVideoOrientation = -1;
|
||||||
private boolean tunnelForward;
|
private boolean tunnelForward;
|
||||||
|
@ -82,6 +83,14 @@ public class Options {
|
||||||
this.videoBitRate = videoBitRate;
|
this.videoBitRate = videoBitRate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getAudioBitRate() {
|
||||||
|
return audioBitRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAudioBitRate(int audioBitRate) {
|
||||||
|
this.audioBitRate = audioBitRate;
|
||||||
|
}
|
||||||
|
|
||||||
public int getMaxFps() {
|
public int getMaxFps() {
|
||||||
return maxFps;
|
return maxFps;
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ 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(), AudioCodec.OPUS, options.getSendCodecId(), options.getSendFrameMeta());
|
||||||
audioEncoder = new AudioEncoder(audioStreamer);
|
audioEncoder = new AudioEncoder(audioStreamer, options.getAudioBitRate());
|
||||||
audioEncoder.start();
|
audioEncoder.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,6 +210,10 @@ public final class Server {
|
||||||
int videoBitRate = Integer.parseInt(value);
|
int videoBitRate = Integer.parseInt(value);
|
||||||
options.setVideoBitRate(videoBitRate);
|
options.setVideoBitRate(videoBitRate);
|
||||||
break;
|
break;
|
||||||
|
case "audio_bit_rate":
|
||||||
|
int audioBitRate = Integer.parseInt(value);
|
||||||
|
options.setAudioBitRate(audioBitRate);
|
||||||
|
break;
|
||||||
case "max_fps":
|
case "max_fps":
|
||||||
int maxFps = Integer.parseInt(value);
|
int maxFps = Integer.parseInt(value);
|
||||||
options.setMaxFps(maxFps);
|
options.setMaxFps(maxFps);
|
||||||
|
|
Loading…
Reference in a new issue