EasyPlayer实现Android MediaMuxer录像MP4(支持G711/AAC/G726音频)

来源:互联网 发布:mac itunes 设置铃声 编辑:程序博客网 时间:2024/06/05 04:46

本文转自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

阅读全文
0 0
原创粉丝点击