Конвертирование аудиодорожек средствами MediaCodec на лету

Входные данные:
1. Исходный видеофайл с несколькими дорожками. Нужная мне дорожка:

mExtractor = new MediaExtractor();
mExtractor.setDataSource(source.getPath());
mExtractor.selectTrack(trackNumber);
  1. Созданы декодер, энкодер и муксер:
MediaFormat sourceMf = mExtractor.getTrackFormat(trackNumber);
mDecoder = MediaCodec.createDecoderByType(sourceMf.getString(MediaFormat.KEY_MIME));
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);
  1. Декодеру и энкодеры установлены колбеки.

Конечная цель — из 6 каналов одной из дорожек видеофайла сделать двуканальный AAC.

В колбеках сделано так:

Декодер:

@Override
            public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
                ByteBuffer byteBuffer = codec.getInputBuffer(index);
                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;
                    }

                    codec.queueInputBuffer(index, offset, size, presentationTimeUs, flags);
                } else {
                    Log.e(TAG, "onInputBufferAvailable = null");
                }
            }
@Override
            public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
                ByteBuffer byteBuffer = codec.getOutputBuffer(index);

                if (byteBuffer != null) {

                    ByteBuffer buffer2 = ByteBuffer.allocate(info.size);
                    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, "onOutputBufferAvailable ПОЛОЖИЛИ");
                        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();
            }

Энкодер:

@Override
            public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
                Log.i(TAG, "onInputBufferAvailable: индекс=" + index);
                Pair<ByteBuffer, MediaCodec.BufferInfo> mediaChunk;
                if ((mediaChunk = mQueue.poll()) != null) {
                    Log.i(TAG, "onInputBufferAvailable: ВЗЯЛИ");
                    ByteBuffer byteBuffer = codec.getInputBuffer(index);
                    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);

                        Log.i(TAG, String.format("onInputBufferAvailable: %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);
                    }
                } else {
                    Log.e(TAG, "onInputBufferAvailable: очередь пуста");
                }
            }
 @Override
            public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
                ByteBuffer byteBuffer = codec.getOutputBuffer(index);

                if (byteBuffer != null) {
                    mMuxer.writeSampleData(mTrackNumber, byteBuffer, info);
                    Log.i(TAG, "onOutputBufferAvailable: ЗАПИСАЛИ");
                    if (info.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
                        finish();
                    } else {
                        codec.releaseOutputBuffer(index, false);
                    }
                } else {
                    Log.e(TAG, "onOutputBufferAvailable: 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();
            }

Однако приложение падает:

2020-02-10 10:39:20.448 26534-26534/? 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}
2020-02-10 10:39:20.452 26534-26534/? I/Codec: onOutputBufferAvailable ПОЛОЖИЛИ
2020-02-10 10:39:20.457 26534-26534/? I/chatty: uid=10136(opensource.umnik.media2media) identical 2 lines
2020-02-10 10:39:20.460 26534-26534/? I/Codec: onOutputBufferAvailable ПОЛОЖИЛИ
2020-02-10 10:39:20.461 26534-26534/? I/Codec: onInputBufferAvailable: индекс=0
2020-02-10 10:39:20.461 26534-26534/? I/Codec: onInputBufferAvailable: ВЗЯЛИ
2020-02-10 10:39:20.461 26534-26534/? I/Codec: onInputBufferAvailable: java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
    0 0 0 0
2020-02-10 10:39:20.461 26534-26534/? I/Codec: onInputBufferAvailable: индекс=1
2020-02-10 10:39:20.461 26534-26534/? I/Codec: onInputBufferAvailable: ВЗЯЛИ
2020-02-10 10:39:20.461 26534-26534/? I/Codec: onInputBufferAvailable: java.nio.HeapByteBuffer[pos=0 lim=0 cap=0]
    0 0 21333 0
2020-02-10 10:39:20.462 26534-26534/? I/Codec: onInputBufferAvailable: индекс=2
2020-02-10 10:39:20.462 26534-26534/? I/Codec: onInputBufferAvailable: ВЗЯЛИ
2020-02-10 10:39:20.462 26534-26534/? I/Codec: onInputBufferAvailable: java.nio.HeapByteBuffer[pos=12288 lim=12288 cap=12288]
    0 12288 42666 0
2020-02-10 10:39:20.462 26534-26534/? D/AndroidRuntime: Shutting down VM


    --------- beginning of crash
2020-02-10 10:39:20.463 26534-26534/? E/AndroidRuntime: FATAL EXCEPTION: main
    Process: opensource.umnik.media2media, PID: 26534
    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:139)
        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)
...

Что не так?


Ответы (0 шт):