I want convert some audio tracks from video files from AAC 5.1 (not only but for starting) to AAC 2 (just because ac3 not supported by MediaMuxer and vorbis and opus encoders has not hardware support in Pixel 2) and use callbacks for MediaCodec. I wrote code like that:
mExtractor = new MediaExtractor();
mExtractor.setDataSource(source.getPath());
mExtractor.selectTrack(trackNumber);
MediaFormat sourceMf = mExtractor.getTrackFormat(trackNumber);
mDecoder = MediaCodec.createDecoderByType(sourceMf.getString(MediaFormat.KEY_MIME));
mDecoder.setCallback(createCallbackDecoder());
mDecoder.configure(sourceMf, null, null, 0);
MediaFormat wantedMediaFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, 44100, 2);
mEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
mEncoder.setCallback(createCallbackEncoder());
mEncoder.configure(wantedMediaFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mMuxer = new MediaMuxer(saveTo.getPath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
public void start() {
mDecoder.start();
And callbacks
Decoder:
@Override
public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
ByteBuffer byteBuffer = codec.getInputBuffer(index);
Log.i(TAG, "onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): " + byteBuffer);
if (byteBuffer != null) {
int offset = 0;
long presentationTimeUs = 0;
int flags;
int size;
if ((size = mExtractor.readSampleData(byteBuffer, offset)) > -1) {
presentationTimeUs = mExtractor.getSampleTime();
flags = mExtractor.getSampleFlags();
mExtractor.advance();
} else {
flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
}
try {
codec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
Log.i(TAG, "onInputBufferAvailable (decoder): SUCCESS");
} catch (Exception e) {
Log.e(TAG, "EXCEPTION (decoder)!\nonInputBufferAvailable (decoder): ", e);
throw e;
}
} else {
Log.e(TAG, "onInputBufferAvailable = null");
}
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
ByteBuffer byteBuffer = codec.getOutputBuffer(index);
Log.i(TAG, "onOutputBufferAvailable: byteBuffer with data (decoder): " + byteBuffer);
if (byteBuffer != null) {
ByteBuffer buffer2 = ByteBuffer.allocate(info.size);
Log.i(TAG, "onOutputBufferAvailable: allocated byteBuffer (decoder): " + buffer2);
buffer2.put(byteBuffer);
MediaCodec.BufferInfo info2 = new MediaCodec.BufferInfo();
info2.flags = info.flags;
info2.size = info.size;
info2.presentationTimeUs = info.presentationTimeUs;
info2.offset = info.offset;
if (mQueue.add(new Pair<>(buffer2, info2))) {
Log.i(TAG, String.format("onOutputBufferAvailable (decoder): added in queue: %s\n%s %s %s %s", buffer2,
info2.offset, info2.size, info2.presentationTimeUs, info2.flags));
codec.releaseOutputBuffer(index, false);
}
} else {
Log.e(TAG, "onOutputBufferAvailable = null");
}
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
Log.i(TAG, String.format("onOutputFormatChanged (decoder): OLD=%s NEW=%s", codec.getInputFormat(), format));
mEncoder.start();
}
Encoder:
@Override
public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
Log.i(TAG, "onInputBufferAvailable (encoder): index=" + index);
Pair<ByteBuffer, MediaCodec.BufferInfo> mediaChunk;
if ((mediaChunk = mQueue.poll()) != null) {
Log.i(TAG, "onInputBufferAvailable (encoder): queue poll != null");
ByteBuffer byteBuffer = codec.getInputBuffer(index);
Log.i(TAG, "onInputBufferAvailable: byteBuffer b/f queue (encoder): " + byteBuffer);
if (byteBuffer != null) {
int offset = mediaChunk.second.offset;
int flags = mediaChunk.second.flags;
long presentationTimeUs = mediaChunk.second.presentationTimeUs;
int size = mediaChunk.second.size;
byteBuffer.put(mediaChunk.first);
try {
Log.i(TAG, String.format("onInputBufferAvailable (encoder): %s\n%s %s %s %s", mediaChunk.first,
mediaChunk.second.offset,
mediaChunk.second.size,
mediaChunk.second.presentationTimeUs,
mediaChunk.second.flags));
codec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
Log.i(TAG, "queueInputBuffer (encoder): SUCCESS");
} catch (Exception e) {
Log.e(TAG, "EXCEPTION (encoder)!\nonInputBufferAvailable (encoder): ", e);
throw e;
}
}
} else {
Log.e(TAG, "onInputBufferAvailable (encoder): empty queue");
}
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
ByteBuffer byteBuffer = codec.getOutputBuffer(index);
Log.i(TAG, "onOutputBufferAvailable: byteBuffer from codec with data (encoder): " + byteBuffer);
if (byteBuffer != null) {
mMuxer.writeSampleData(mTrackNumber, byteBuffer, info);
Log.i(TAG, "onOutputBufferAvailable (encoder): muxer written");
if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
finish();
} else {
codec.releaseOutputBuffer(index, false);
}
} else {
Log.e(TAG, "onOutputBufferAvailable (encoder): buffer = null");
}
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
Log.i(TAG, String.format("onOutputFormatChanged (encoder): OLD=%s NEW=%s", codec.getInputFormat(), format));
mTrackNumber = mMuxer.addTrack(format);
mMuxer.start();
}
But on executing I got exception:
I/MediaCodec: MediaCodec will operate in async mode
I/MediaCodec: MediaCodec will operate in async mode
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onOutputFormatChanged (decoder): OLD={sample-rate=44100, mime=audio/mp4a-latm, channel-count=1, bitrate=0} NEW={sample-rate=48000, pcm-encoding=2, mime=audio/raw, channel-count=6}
I/Codec: onOutputBufferAvailable: byteBuffer with data (decoder): java.nio.DirectByteBuffer[pos=0 lim=0 cap=32768]
I/Codec: onOutputBufferAvailable: allocated byteBuffer (decoder): java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
I/Codec: onOutputBufferAvailable (decoder): added in queue: java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
0 0 0 0
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onOutputBufferAvailable: byteBuffer with data (decoder): java.nio.DirectByteBuffer[pos=0 lim=0 cap=32768]
I/Codec: onOutputBufferAvailable: allocated byteBuffer (decoder): java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
I/Codec: onOutputBufferAvailable (decoder): added in queue: java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
0 0 21333 0
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onOutputBufferAvailable: byteBuffer with data (decoder): java.nio.DirectByteBuffer[pos=0 lim=12288 cap=32768]
I/Codec: onOutputBufferAvailable: allocated byteBuffer (decoder): java.nio.HeapByteBuffer[pos=0 lim=12288 cap=12288]
I/Codec: onOutputBufferAvailable (decoder): added in queue: java.nio.HeapByteBuffer[pos=12288 lim=12288 cap=12288]
0 12288 42666 0
I/Codec: onInputBufferAvailable: byteBuffer b/f readSampleData (decoder): java.nio.DirectByteBuffer[pos=0 lim=8192 cap=8192]
I/Codec: onInputBufferAvailable (decoder): SUCCESS
I/Codec: onOutputBufferAvailable: byteBuffer with data (decoder): java.nio.DirectByteBuffer[pos=0 lim=12288 cap=32768]
I/Codec: onOutputBufferAvailable: allocated byteBuffer (decoder): java.nio.HeapByteBuffer[pos=0 lim=12288 cap=12288]
I/Codec: onOutputBufferAvailable (decoder): added in queue: java.nio.HeapByteBuffer[pos=12288 lim=12288 cap=12288]
0 12288 64000 0
I/Codec: onInputBufferAvailable (encoder): index=0
I/Codec: onInputBufferAvailable (encoder): queue poll != null
I/Codec: onInputBufferAvailable: byteBuffer b/f queue (encoder): java.nio.DirectByteBuffer[pos=0 lim=4096 cap=4096]
I/Codec: onInputBufferAvailable (encoder): java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
0 0 0 0
I/Codec: queueInputBuffer (encoder): SUCCESS
I/Codec: onInputBufferAvailable (encoder): index=1
I/Codec: onInputBufferAvailable (encoder): queue poll != null
I/Codec: onInputBufferAvailable: byteBuffer b/f queue (encoder): java.nio.DirectByteBuffer[pos=0 lim=4096 cap=4096]
I/Codec: onInputBufferAvailable (encoder): java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
0 0 21333 0
I/Codec: queueInputBuffer (encoder): SUCCESS
I/Codec: onInputBufferAvailable (encoder): index=2
I/Codec: onInputBufferAvailable (encoder): queue poll != null
I/Codec: onInputBufferAvailable: byteBuffer b/f queue (encoder): java.nio.DirectByteBuffer[pos=0 lim=4096 cap=4096]
I/Codec: onInputBufferAvailable (encoder): java.nio.HeapByteBuffer[pos=12288 lim=12288 cap=12288]
0 12288 42666 0
E/Codec: EXCEPTION (encoder)!
onInputBufferAvailable (encoder):
java.lang.IllegalArgumentException
at android.media.MediaCodec.native_queueInputBuffer(Native Method)
at android.media.MediaCodec.queueInputBuffer(MediaCodec.java:2450)
at opensource.umnik.media2media.codec.SyncCodec$2.onInputBufferAvailable(SyncCodec.java:149)
at android.media.MediaCodec$EventHandler.handleCallback(MediaCodec.java:1738)
at android.media.MediaCodec$EventHandler.handleMessage(MediaCodec.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
D/AndroidRuntime: Shutting down VM
at android.media.MediaCodec.queueInputBuffer(MediaCodec.java:2450) ← this is encoder's buffer
What I did wrong?
UPD: code updated some times. UPD2: more logs
The cause of the
IllegalArgumentExceptionis ultimately that you have specified a size of 12288 bytes for an inputByteBufferthat only has a capacity of 4096 bytes.Note that, for the first two calls to
queueInputBuffer()(encoder), you are not putting in any data; they are both 0-byte writes, therefor they are trivial, and they succeed. (Personally, I'd just skip the 0-byte writes).The 3rd iteration is the first time you actually have some data to encode. But your
byteBuffer.put(mediaChunk.first)isn't actually doing anything, becausemediaChunk.firstis already "played out". It has 0 bytes remaining. You need to "rewind" it before you do theput():mediaChunk.first.position(0)Of course, now the problem is, you have 12288 bytes to write, but the destination can only fit 4096 bytes, so the
put()will undoubtedly throw. You must write a smaller amount. This will avoid theIllegalArgumentException.As I mentioned in a comment, your decoded audio contains 6 channels of 5.1 audio. (`1024 samples x 2 bytes per sample x 6 channels == 12288). Your encoder would like 2048 samples of audio (at 2 bytes per sample; I assume you have configured the encoder for 1 channel/2 bytes per sample). This may be a good time to decide how you'd like to do the "5.1 -> mono" audio conversion; that will help reduce the amount of decoded audio that needs to be copied into the encoder.