MediaCodec Pcm to Aac, missing the last 50ms of data

16 Views Asked by At

When I use mediaCodec to convert PCM to AAC, AAC is missing the last 50ms of data. Here is my code.

public class PcmToAAc {

    private static final String TAG = "PcmToAac";
    private static final int sampleRate = 16000;
    private static final int bitRate = sampleRate * 2;
    private static final int channelCount = 1;
    private static final int SAMPLE_RATE_INDEX = 8;

    private MediaCodec mMediaCodec;
    private MediaCodec.BufferInfo encodeBufferInfo;
    private FileOutputStream fos;
    private FileInputStream fis;



    public void test(Context context) throws IOException {
        String pcmPath = context.getExternalFilesDir(null).getPath() + "/pcm";
        String aacPath = context.getExternalFilesDir(null).getPath() + "/aac";
        fis = new FileInputStream(pcmPath);
        fos = new FileOutputStream(aacPath);
        byte[] pcmData = new byte[640];
        init();
        while (true) {
            int readSize = fis.read(pcmData);
            if (readSize < 0) {
                break;
            }
            byte[] tempBuf = new byte[readSize];
            Log.d(TAG, "read size = " + readSize);
            System.arraycopy(pcmData, 0, tempBuf, 0, readSize);
            encode(tempBuf);
        }
        inputEndFlag();
        ensureDataOutput();
        release();
    }


    public void release() {
        try {
            fos.flush();
            fos.close();
            fis.close();
            mMediaCodec.stop();
            mMediaCodec.release();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void init() {
        MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);
        audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
        audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);

        try {
            mMediaCodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);
            mMediaCodec.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            mMediaCodec.start();
            encodeBufferInfo = new MediaCodec.BufferInfo();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void encode(byte[] pcmData) {
        int inputBufferIndex = mMediaCodec.dequeueInputBuffer(-1);
        Log.d(TAG, "encode pcm data size = " + pcmData.length);
        if (inputBufferIndex >= 0) {
            ByteBuffer inputBuffer = mMediaCodec.getInputBuffer(inputBufferIndex);
            inputBuffer.clear();
            inputBuffer.put(pcmData);
            mMediaCodec.queueInputBuffer(inputBufferIndex, 0, pcmData.length, 0 ,0);
            Log.d(TAG, "queue input buffer");
        }

        int outputBufferIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0);
        while (outputBufferIndex >= 0) {
            fetchEncodeAndReleaseOutputBuffer(outputBufferIndex);
            outputBufferIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 0);
        }
    }

    void inputEndFlag() {
        int inputIndex = mMediaCodec.dequeueInputBuffer(-1);
        if (inputIndex >= 0) {
            ByteBuffer inputByteBuf = mMediaCodec.getInputBuffer(inputIndex);
            int inputBufferSize = 0;
            int flag = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
            mMediaCodec.queueInputBuffer(inputIndex, 0, inputBufferSize, 0, flag);
        }
    }

    void ensureDataOutput() {
        int outputIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 5000);
        while (encodeBufferInfo.flags != MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
            if (outputIndex >= 0) {
                fetchEncodeAndReleaseOutputBuffer(outputIndex);
                outputIndex = mMediaCodec.dequeueOutputBuffer(encodeBufferInfo, 5000);
            }
        }
        if (outputIndex >= 0) {
            fetchEncodeAndReleaseOutputBuffer(outputIndex);
        }
    }


    private void fetchEncodeAndReleaseOutputBuffer(int outputBufferIndex) {
        //获取缓存信息的长度
        int byteBufSize = encodeBufferInfo.size;
        //添加ADTS头部后的长度
        int bytePacketSize = byteBufSize + 7;
        ByteBuffer outputBuffer = mMediaCodec.getOutputBuffer(outputBufferIndex);
        outputBuffer.position(encodeBufferInfo.offset);
        outputBuffer.limit(encodeBufferInfo.offset + encodeBufferInfo.size);

        byte[] aacData = new byte[bytePacketSize];
        addADTStoPacket(aacData, bytePacketSize);

        outputBuffer.get(aacData, 7, byteBufSize);

        outputBuffer.position(encodeBufferInfo.offset);

        Log.d("TAG", "get aacData from output buf, size = " + (aacData.length - 7));
        try {
            fos.write(aacData);
        } catch (IOException e) {
            e.printStackTrace();
        }

        mMediaCodec.releaseOutputBuffer(outputBufferIndex, false);
    }

    private void addADTStoPacket(byte[] packet, int packetLen) {
        // AAC LC
        int profile = 2;
        // 16KHz
        int freqIdx = SAMPLE_RATE_INDEX;
        // CPE
        int chanCfg = channelCount;
        // fill in ADTS data
        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;
    }

}

I have used a lot of PCM files for testing, and after converting to AAC, the last 50ms or so of data will be lost. I have no idea. I use BUFFER_FLAG_END_OF_STREAM to make sure all data is output.

Please help me to find the reason for missing.

1

There are 1 best solutions below

0
dev.bmax On

Looks like every time mMediaCodec.dequeueOutputBuffer(...) returns INFO_TRY_AGAIN_LATER (inside the encode function) one buffer of data is being lost.

Also, consider using existing components for reading and writing audio files (MediaExtractor and MediaMuxer).