zoukankan      html  css  js  c++  java
  • EasyPlayer实现Android MediaMuxer录像MP4(支持G711/AAC/G726音频)

    本文转自EasyDarwin开源团队John的博客:http://blog.csdn.net/jyt0551/article/details/72787095

    Android平台的MediaMuxer是个非常好的录像库,它能将H.264视频+AAC音频存储成.mp4格式的文件,而且稳定性、同步效果都非常好。

    MediaMuxer在安卓版的EasyPlayer和EasyPusher都用到了该方法来进行本地录像。作者也写过两篇针对性的博客来做介绍,参考:
    http://blog.csdn.net/jyt0551/article/details/60152344

    http://blog.csdn.net/jyt0551/article/details/58714595

    MediaMuxer的接口定义相对而言比较简单,调用过程如下图所示。

    Created with Raphaël 2.1.0创建MediaMuxerAddVideoTrack、AddAudioTrackstart视频还是音频?writeVideoSamplestop releaseMP4 文件writeAudioSampleyesno

    简单来说,就是创建对象、添加音视频轨道、开始、持续写入音视频数据、关闭这样一个过程。

    遗憾的是,MediaMuxer并不支持对除AAC以外的音频编码格式的封装,然而在安防行业里G711音频格式的数据是大多数设备的默认编码格式。

    如何支持G711格式的数据呢?其实换种思路就会豁然开朗,我们可以先把G711数据解码成PCM,再用MediaCodec编码成AAC,这样曲线存储^_^。不光是G711,所有的音频编码格式都可以这样做哈哈。。

    所以前面的流程图里,writeAudioSample的部分就变成这样了:

    Created with Raphaël 2.1.0音频AAC格式数据?writeAudioSampledecodepcmencodeaacyesno

    下面是将解码后的PCM数据塞入Muxer的代码片段。

    package org.easydarwin.audio;
    
    import android.media.MediaCodec;
    import android.media.MediaCodecInfo;
    import android.media.MediaFormat;
    import android.util.Log;
    
    import org.easydarwin.video.EasyMuxer;
    
    import java.io.IOException;
    import java.nio.ByteBuffer;
    
    /**
     * 对EasyMuxer的扩展。支持对PCM格式的音频打包。
     */
    public class EasyAACMuxer extends EasyMuxer {
        MediaCodec mMediaCodec;
        String TAG = "EasyAACMuxer";
    
        protected MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
        protected ByteBuffer[] mBuffers = null;
    
        private MediaFormat mAudioFormat;
    
        public EasyAACMuxer(String path, long durationMillis) {
            super(path, durationMillis);
        }
    
        @Override
        public synchronized void addTrack(MediaFormat format, boolean isVideo) {
            super.addTrack(format, isVideo);
            if (!isVideo){
                mAudioFormat = format;
            }
        }
    
        public synchronized void pumpPCMStream(byte []pcm, int length, long timeUs) throws IOException {
            if (mMediaCodec == null) {// 启动AAC编码器。这里用MediaCodec来编码
                if (mAudioFormat == null) return;
                mMediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");
                Log.i(TAG, String.valueOf(mAudioFormat));
                mAudioFormat.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
                mAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,MediaCodecInfo.CodecProfileLevel.AACObjectLC);
                mAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, 16000);
    //            mAudioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 320);
    
                mMediaCodec.configure(mAudioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
                mMediaCodec.start();
                mBuffers = mMediaCodec.getOutputBuffers();
            }
            int index = 0;
            // 将pcm编码成AAC
            do {
                index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 1000);
                if (index >= 0) {
                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                        continue;
                    }
                    if (mBufferInfo.presentationTimeUs == 0){
                        continue;
                    }
                    if (VERBOSE) Log.d(TAG,String.format("dequeueOutputBuffer data length:%d,tmUS:%d", mBufferInfo.size, mBufferInfo.presentationTimeUs));
                    ByteBuffer outputBuffer = mBuffers[index];
                    // ok,编码成功了。将AAC数据写入muxer.
                    pumpStream(outputBuffer, mBufferInfo, false);
                    mMediaCodec.releaseOutputBuffer(index, false);
                } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    mBuffers = mMediaCodec.getOutputBuffers();
                } else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    Log.v(TAG, "output format changed...");
                    MediaFormat newFormat = mMediaCodec.getOutputFormat();
                    Log.v(TAG, "output format changed..." + newFormat);
                } else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    Log.v(TAG, "No buffer available...");
                } else {
                    Log.e(TAG, "Message: " + index);
                }
            } while (index >= 0 && !Thread.currentThread().isInterrupted());
    
            final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
            do {
                index = mMediaCodec.dequeueInputBuffer(1000);
                if (index >= 0) {
                    inputBuffers[index].clear();
                    inputBuffers[index].put(pcm, 0, length);
                    if (VERBOSE) Log.d(TAG,String.format("queueInputBuffer pcm data length:%d,tmUS:%d", length, timeUs));
                    mMediaCodec.queueInputBuffer(index, 0, length, timeUs, 0);
                }
            }
            while (!Thread.currentThread().isInterrupted() && index < 0);
        }
    
        @Override
        public synchronized void release() {
            if (mMediaCodec != null) mMediaCodec.release();
            mMediaCodec = null;
            super.release();
        }
    }
    

    一切都在代码中,不再过多解释,至此结束。

    更多代码请查看EasyPlayer Github:https://github.com/EasyDarwin/EasyPlayer

    获取更多信息

    邮件:support@easydarwin.org

    WEB:www.EasyDarwin.org

    Copyright © EasyDarwin.org 2012-2017

    EasyDarwin

  • 相关阅读:
    如何判定某个类的职责是否够"单一"?
    Guava中的Cache简易源码分析
    为什么尽量少用继承?
    聊聊抽象类和接口
    WINDOW 搭建 ELK 2.4.0
    摇摇棒,理工男的择偶权(上)
    C++值多态:传统多态与类型擦除之间
    C++值元编程
    STM32学习笔记——printf
    C++98/11/17表达式类别
  • 原文地址:https://www.cnblogs.com/babosa/p/7348770.html
Copyright © 2011-2022 走看看