|
|
|
@ -51,6 +51,7 @@ public final class AudioEncoder {
|
|
|
|
|
private final Streamer streamer;
|
|
|
|
|
private final int bitRate;
|
|
|
|
|
private final List<CodecOption> codecOptions;
|
|
|
|
|
private final String encoderName;
|
|
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
@ -65,10 +66,11 @@ public final class AudioEncoder {
|
|
|
|
|
|
|
|
|
|
private boolean ended;
|
|
|
|
|
|
|
|
|
|
public AudioEncoder(Streamer streamer, int bitRate, List<CodecOption> codecOptions) {
|
|
|
|
|
public AudioEncoder(Streamer streamer, int bitRate, List<CodecOption> codecOptions, String encoderName) {
|
|
|
|
|
this.streamer = streamer;
|
|
|
|
|
this.bitRate = bitRate;
|
|
|
|
|
this.codecOptions = codecOptions;
|
|
|
|
|
this.encoderName = encoderName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static AudioFormat createAudioFormat() {
|
|
|
|
@ -177,6 +179,8 @@ public final class AudioEncoder {
|
|
|
|
|
thread = new Thread(() -> {
|
|
|
|
|
try {
|
|
|
|
|
encode();
|
|
|
|
|
} catch (ConfigurationException e) {
|
|
|
|
|
// Do not print stack trace, a user-friendly error-message has already been logged
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
Ln.e("Audio encoding error", e);
|
|
|
|
|
} finally {
|
|
|
|
@ -215,7 +219,7 @@ public final class AudioEncoder {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@TargetApi(Build.VERSION_CODES.M)
|
|
|
|
|
public void encode() throws IOException {
|
|
|
|
|
public void encode() throws IOException, ConfigurationException {
|
|
|
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
|
|
|
|
Ln.w("Audio disabled: it is not supported before Android 11");
|
|
|
|
|
streamer.writeDisableStream();
|
|
|
|
@ -228,13 +232,13 @@ public final class AudioEncoder {
|
|
|
|
|
boolean mediaCodecStarted = false;
|
|
|
|
|
boolean recorderStarted = false;
|
|
|
|
|
try {
|
|
|
|
|
String mimeType = streamer.getCodec().getMimeType();
|
|
|
|
|
mediaCodec = MediaCodec.createEncoderByType(mimeType); // may throw IOException
|
|
|
|
|
Codec codec = streamer.getCodec();
|
|
|
|
|
mediaCodec = createMediaCodec(codec, encoderName);
|
|
|
|
|
|
|
|
|
|
mediaCodecThread = new HandlerThread("AudioEncoder");
|
|
|
|
|
mediaCodecThread.start();
|
|
|
|
|
|
|
|
|
|
MediaFormat format = createFormat(mimeType, bitRate, codecOptions);
|
|
|
|
|
MediaFormat format = createFormat(codec.getMimeType(), bitRate, codecOptions);
|
|
|
|
|
mediaCodec.setCallback(new EncoderCallback(), new Handler(mediaCodecThread.getLooper()));
|
|
|
|
|
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
|
|
|
|
|
|
|
|
|
@ -324,6 +328,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 class EncoderCallback extends MediaCodec.Callback {
|
|
|
|
|
@TargetApi(Build.VERSION_CODES.N)
|
|
|
|
|
@Override
|
|
|
|
|