使用Android MediaCodec对AAC进行编解码,从输出缓冲区中获取结果,结果计数混乱

问题描述

我正在为 Unity Android 编写一个录制/播放系统库。我使用 MediaCodec 作为 PCM 和 AAC 之间的编解码器。我想如果我将一个包含 1024 个样本的 PCM 数据块放入 MediaCodec 的输入缓冲区,那么我最终会得到一个 AAC 数据块。解码时反之亦然。

但我的代码不能以一对一的方式工作。

编码器部分。一旦我初始化了编码器,我就可以立即从它的输出缓冲区中获取一个结果。不喂食不知道是哪里来的。

解码器部分的行为更奇怪。我测试了有 4 个输入缓冲区索引和 4 个输出缓冲区索引。我不断地喂入输入缓冲区。然后我计算了总馈送输入缓冲区次数并从输出缓冲区次数中获得结果。我试了几次,我发现输出计数总是比喂食计数少 2。似乎有 2 帧我数不出来。

我是一名 Android 初学者,我不知道为什么会发生这种情况。

我用 Android SDK29 编译并在索尼手机上测试。

下面是我的编码器代码。

e

这是我的解码器代码。


import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;

public class AACEncoder {
    private static final String TAG = "AACEncoder";

    private int KEY_CHANNEL_COUNT = 1;

    private int KEY_SAMPLE_RATE = 44100;

    private int KEY_BIT_RATE = 128000;

    private MediaCodec mEncoder;

    private int freqIdx = 0xb;

    private Queue<byte[]> m_pcmDataQueue = new LinkedBlockingQueue<>();

    private Boolean m_threadRunning=false;
    private AACEncoder.AACThread m_aacEncodeThread;

    private Queue<byte[]> m_aacDataQueue = new LinkedBlockingQueue<>();

    private String name;

    public AACEncoder() {
        this.name = UUID.randomUUID().toString().replace("-","");
    }

    /**
     * This is actual param values I set in Unity
     */
    public void initCodec() {
        this.initCodec(16000,1,256000);
    }

    protected void initCodec(int sampleRate,int channelCount,int bitRate) {
        KEY_CHANNEL_COUNT = channelCount;
        KEY_SAMPLE_RATE = sampleRate;
        KEY_BIT_RATE = bitRate;
        switch (sampleRate)
        {
            case 96000:
                freqIdx = 0x00;
                break;
            case 88200:
                freqIdx = 0x01;
                break;
            case 64000:
                freqIdx = 0x02;
                break;
            case 48000:
                freqIdx = 0x03;
                break;
            case 44100:
                freqIdx = 0x04;
                break;
            case 32000:
                freqIdx = 0x05;
                break;
            case 24000:
                freqIdx = 0x06;
                break;
            case 22050:
                freqIdx = 0x07;
                break;
            case 16000:
                freqIdx = 0x08;
                break;
            case 12000:
                freqIdx = 0x09;
                break;
            case 11025:
                freqIdx = 0x0a;
                break;
            case 8000:
                freqIdx = 0x0b;
                break;
            case 7350:
                freqIdx = 0x0c;
                break;
        }
        prepare();
        m_aacEncodeThread = new AACEncoder.AACThread();
        m_threadRunning = true;
        m_aacEncodeThread.start();
    }

    private boolean prepare() {
        try {

            String mine = "audio/mp4a-latm";

            mEncoder = MediaCodec.createEncoderByType(mine);

            MediaFormat format = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC,KEY_SAMPLE_RATE,1);
            format.setInteger(MediaFormat.KEY_BIT_RATE,KEY_BIT_RATE);
            format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE,1024 * 1024);

            mEncoder.configure(format,null,MediaCodec.CONFIGURE_FLAG_ENCODE);

            Log.d(TAG,"initCodec: " + mEncoder.getName());


        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        if (mEncoder == null) {
            return false;
        }
        mEncoder.start();
        return true;
    }

    /**
     * encode API
     */
    public void encode(byte[] buf,int offset,int length) {
        m_pcmDataQueue.add(buf);
    }

    public void clear() {
        m_pcmDataQueue.clear();
        m_aacDataQueue.clear();
    }

    /**
     * get encoded aac API
     */
    public byte[] getAACData() {
        byte[] output = m_aacDataQueue.poll();
        return output;
    }

    private void encodeAAC(byte[] buf,int length) {
        if(buf == null || length ==0 || offset < 0 || offset >= buf.length){
            return ;
        }

        long kTimeOutUs = 0;
        try {

            int inputBufIndex = mEncoder.dequeueInputBuffer(kTimeOutUs);
            if (inputBufIndex >= 0) {

                ByteBuffer dstBuf = mEncoder.getInputBuffer(inputBufIndex);//codecInputBuffers[inputBufIndex]
                if(dstBuf!=null){

                    dstBuf.clear();

                    dstBuf.put(buf,offset,length);
                    dstBuf.limit(length);

                    mEncoder.queueInputBuffer(inputBufIndex,length,0);
                }
            }

        } catch (Exception e) {
            Log.e(TAG,"Audio encode error " + e.toString());
            e.printStackTrace();
        }
    }

    private void pollEncoderResult() {

        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();

        long kTimeOutUs = 0;

        int outputBufferIndex = mEncoder.dequeueOutputBuffer(info,kTimeOutUs);


        while (outputBufferIndex >= 0) {

            ByteBuffer outputBuffer = mEncoder.getOutputBuffer(outputBufferIndex);//codecOutputBuffers[outputBufferIndex];

            if(outputBuffer != null){

                int byteBufSize = info.size;
                int bytePacketSize =byteBufSize + 7;
                byte[] outData = new byte[bytePacketSize];
                addADTStoPacket(outData,bytePacketSize);
                outputBuffer.get(outData,7,byteBufSize);

                m_aacDataQueue.add(outData);

                mEncoder.releaseOutputBuffer(outputBufferIndex,false);

                outputBuffer.clear();
            }
            outputBufferIndex = mEncoder.dequeueOutputBuffer(info,kTimeOutUs);
        }
    }

    private void addADTStoPacket(byte[] packet,int packetLen) {
        int profile = 2;  //AAC LC
        int chanCfg = 1;  //CPE
        packet[0] = (byte) 0xFF;
        packet[1] = (byte) 0xF9;
        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
        packet[6] = (byte) 0xFC;
    }

    /**
     * release API
     */
    public void release() {
        try {
            m_threadRunning = false;
            m_aacEncodeThread.join();

            if (mEncoder != null) {
                mEncoder.stop();
                mEncoder.release();
            }
            clear();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class AACThread extends Thread {
        private final static String TAG = "AACEncodeThread";

        private ThreadSleeper sleeper = new ThreadSleeper(64,TAG); // sleep 64millis

        public void run() {
            Log.d(TAG,"AAC encode thread start" );
            while(m_threadRunning){
                try {
                    sleeper.sleep(); // sleep 64millis
                    byte[] pcmData = m_pcmDataQueue.poll();
                    if(pcmData != null) {
                        encodeAAC(pcmData,pcmData.length);
                    }
                    pollEncoderResult();

                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            Log.d(TAG,"AAC encode thread stop" );
        }
    }
}

谢谢!

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)