简体   繁体   English

MediaCodec 解码 && 即时编码

[英]MediaCodec decode && encode on-the-fly

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.我想将视频文件中的一些音轨从 AAC 5.1(不仅是用于启动)转换为 AAC 2(仅仅因为 MediaMuxer 不支持 ac3,而 vorbis 和 opus 编码器在 Pixel 2 中没有硬件支持)并使用 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 at android.media.MediaCodec.queueInputBuffer(MediaCodec.java:2450) ← 这是编码器的缓冲区

What I did wrong?我做错了什么?

UPD: code updated some times. UPD:代码更新了几次。 UPD2: more logs UPD2:更多日志

The cause of the IllegalArgumentException is ultimately that you have specified a size of 12288 bytes for an input ByteBuffer that only has a capacity of 4096 bytes. IllegalArgumentException的原因最终是您为只有 4096 字节容量的输入ByteBuffer指定了 12288 字节的大小。

Note that, for the first two calls to queueInputBuffer() (encoder), you are not putting in any data;请注意,对于queueInputBuffer() (编码器)的前两次调用,您没有输入任何数据; they are both 0-byte writes, therefor they are trivial, and they succeed.它们都是 0 字节写入,因此它们是微不足道的,并且它们成功了。 (Personally, I'd just skip the 0-byte writes). (就个人而言,我只是跳过 0 字节写入)。

The 3rd iteration is the first time you actually have some data to encode.第 3 次迭代是您第一次真正有一些数据要编码。 But your byteBuffer.put(mediaChunk.first) isn't actually doing anything, because mediaChunk.first is already "played out".但是您的byteBuffer.put(mediaChunk.first)实际上并没有做任何事情,因为mediaChunk.first已经“播放完毕”。 It has 0 bytes remaining.它还有 0 个字节剩余。 You need to "rewind" it before you do the put() :在执行put()之前,您需要“倒带”它:

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.当然,现在的问题是,您有 12288 个字节要写入,但目标只能容纳 4096 个字节,因此put()无疑会抛出。 You must write a smaller amount.你必须写一个较小的数量。 This will avoid the IllegalArgumentException .这将避免IllegalArgumentException

As I mentioned in a comment, your decoded audio contains 6 channels of 5.1 audio.正如我在评论中提到的,您解码的音频包含 6 个 5.1 音频通道。 (`1024 samples x 2 bytes per sample x 6 channels == 12288). (`1024 个样本 x 每个样本 2 个字节 x 6 个通道 == 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).您的编码器需要 2048 个音频样本(每个样本 2 个字节;我假设您已将编码器配置为 1 个通道/每个样本 2 个字节)。 This may be a good time to decide how you'd like to do the "5.1 -> mono" audio conversion;这可能是决定如何进行“5.1 -> 单声道”音频转换的好时机; that will help reduce the amount of decoded audio that needs to be copied into the encoder.这将有助于减少需要复制到编码器中的解码音频量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM