Android音视频之MediaCodec音频解码

简介

MediaCodec:负责媒体文件的编码和解码工作,内部方法均为native方法。

正文

音频播放主要步骤如下:

  • 将资源加载到MediaExtractor
  • 获取音频所在轨道
  • 设置MediaExtractor选中视频所在轨道
  • 创建音频解码的MediaCodec
  • 循环开始
  • 将extractor中资源以一个单位填充到decorder的输入缓存区中
  • decorder之后,将解码的音频填充到输出缓冲区中
  • 将缓冲区中的PCM数据加载到到AudioTrack中
  • 循环结束,直至音频资源的末尾
  • 释放资源

音频解码创建一个解码线程

private static ExecutorService mExecutorService = Executors.newSingleThreadExecutor();

1. 初始化,获取音频所在轨道,设置MediaExtractor选中视频所在轨道,创建音频解码的MediaCodec

private void initDecorder() throws IOException {
        mMediaExtractor = new MediaExtractor();
        mMediaExtractor.setDataSource(mMusicPath);
        int numTracks = mMediaExtractor.getTrackCount();
        for (int i = 0; i < numTracks; i++) {
            MediaFormat mediaFormat = mMediaExtractor.getTrackFormat(i);
            String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
            if (mime.startsWith("audio")) {
                mMediaExtractor.selectTrack(i);
                mDecoder = MediaCodec.createDecoderByType(mime);
                if (mime.equals(MediaFormat.MIMETYPE_AUDIO_AAC)) {
                    // AAC ADTS头部处理
                    ByteBuffer csd = mediaFormat.getByteBuffer("csd-0");
                    for (int k = 0; k < csd.capacity(); ++k) {
                        Log.e(TAG, "csd : " + csd.array()[k]);
                    }
                }
                mSampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
                channel = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
                mediaFormat = makeADTSData(MediaCodecInfo.CodecProfileLevel.AACObjectLC, mSampleRate, channel);
            }
            mDecoder.configure(mediaFormat, null, null, 0);
            break;
        }
    }

2. 音频解码decoder

public void decode() {
        mInputByteBuffers = mDecoder.getInputBuffers();
        mOutputByteBuffers = mDecoder.getOutputBuffers();
        mBufferInfo = new MediaCodec.BufferInfo();

        while (!eosReceived) {
            int inIndex = mDecoder.dequeueInputBuffer(TIMEOUT_US);
            if (inIndex >= 0) {
                ByteBuffer buffer = mInputByteBuffers[inIndex];
                int sampleSize = mMediaExtractor.readSampleData(buffer, 0);
                if (sampleSize < 0) {
                    // We shouldn't stop the playback at this point, just pass the EOS
                    // flag to mDecoder, we will get it again from the
                    // dequeueOutputBuffer
                    Log.d(TAG, "InputBuffer BUFFER_FLAG_END_OF_STREAM");
                    mDecoder.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);

                } else {
                    mDecoder.queueInputBuffer(inIndex, 0, sampleSize, mMediaExtractor.getSampleTime(), 0);
                    mMediaExtractor.advance();
                }

                int outIndex = mDecoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
                switch (outIndex) {
                    case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
                        Log.d(TAG, "INFO_OUTPUT_BUFFERS_CHANGED");
                        mOutputByteBuffers = mDecoder.getOutputBuffers();
                        break;

                    case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
                        MediaFormat format = mDecoder.getOutputFormat();
                        Log.d(TAG, "New format " + format);

                        break;
                    case MediaCodec.INFO_TRY_AGAIN_LATER:
                        Log.d(TAG, "dequeueOutputBuffer timed out!");
                        break;

                    default:
                        ByteBuffer outBuffer = mOutputByteBuffers[outIndex];
                        Log.v(TAG, "We can't use this buffer but render it due to the API limit, " + outBuffer);

                        final byte[] chunk = new byte[mBufferInfo.size];
                        outBuffer.get(chunk); // Read the buffer all at once
                        outBuffer.clear(); // ** MUST DO!!! OTHERWISE THE NEXT TIME YOU GET THIS SAME BUFFER BAD THINGS WILL HAPPEN

                        MediaFormat mFormat = mDecoder.getOutputFormat();
                        mOnCapturePCMListener.capturePCM(chunk, mFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE), mFormat.getInteger
                                (MediaFormat.KEY_CHANNEL_COUNT));
                        mDecoder.releaseOutputBuffer(outIndex, false);
                        break;
                }

                // All decoded frames have been rendered, we can stop playing now
                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    Log.d(TAG, "OutputBuffer BUFFER_FLAG_END_OF_STREAM");
                    break;
                }
            }
        }

        mDecoder.stop();
        mDecoder.release();
        mDecoder = null;

        mMediaExtractor.release();
        mMediaExtractor = null;
        eosReceived = true;

        mExecutorService.shutdown();
        mExecutorService = null;
    }

总结

了解mediacodec和MediaExtractor的api和原理,按音频播放的步骤进行解码播放。

学习一步一个脚印,谢谢今天的自己。

你可能感兴趣的:(Android音视频之MediaCodec音频解码)