Android MP4视频录制(思路篇,无DEMO)

环境

1、android 开发环境
2、sdk >= 18


阅读对象

android开发者


音视频合成

根据数据源合成视频文件需要用到MediaMuxer这个类,可以参考这一篇文章(http://www.jianshu.com/p/aeadf260258a)

MediaMuxer进行音视频合成的步骤

1、创建MediaMuxer,参数为outputPath, videoFormat

1、outputPath:视频文件的输出路径
2、videoFormat:视频文件的格式,如 MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4

2、addTrack,参数为dataFormat:声音/图像的格式 MediaFormat

1、声音和图像都要调用一次 
2、return: 结果返回一个trackIndex
public synchronized int addTrack(final MediaFormat format) {
    if (mIsStarted) {
        throw new IllegalStateException("muxer already started");
    }
    final int trackIx = mMediaMuxer.addTrack(format);
    return trackIx;
}

3、start

当所有数据都添加track完成后,调用start,之后等待各个track数据的到来   
 public synchronized boolean start() {
        mStartedCount++;
        if ((mEncoderCount > 0) && (mStartedCount == mEncoderCount)) {
            mMediaMuxer.start();
            mIsStarted = true;
            notifyAll();
        }
        return mIsStarted;
    }

4、writeSampleData,参数为(int trackIndex, final ByteBuffer byteBuf, final MediaCodec.BufferInfo bufferInfo)

1、每个track产生数据时,调用此方法往mp4文件写数据(如何生成源数据?后续会讲到)
2、bufferInfo.presentationTimeUs 必须给出正确的时间戳,注意单位是 us (此坑我踩过,而且当时出错还没意识到问题,一直断点...)
 
注:每次只能添加一帧视频数据或者单个Sample的音频数据,并且BufferInfo对象的值一定要设置正确:
bufferInfo.presentationTimeUs 必须给出正确的时间戳,注意单位是 us (此坑我踩过,而且当时出错还没意识到问题,一直断点...)
public synchronized void writeSampleData(final int trackIndex, final ByteBuffer byteBuf, final MediaCodec.BufferInfo bufferInfo) {
        if (!mIsStarted) {
            return ;
        }
        if (mStartedCount > 0) {
            mMediaMuxer.writeSampleData(trackIndex, byteBuf, bufferInfo);
        }
    }

5、stop&release

结束录制时,要把所有的track都关闭,再调用stop和release;期间最好加个标志位,方便控制与调试
public synchronized void stop() {
        if (!mIsStarted) {
            LogTools.d("not started");
            return ;
        }
        mStartedCount--;
        if ((mEncoderCount > 0) && (mStartedCount <= 0)) {
            mMediaMuxer.stop();
            mMediaMuxer.release();
            mIsStarted = false;
        }
    }

音频采集

AudioRecord、MediaCodec

1、准备:

AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)

audioSource : MediaRecorder.AudioSource,详见(http://blog.csdn.net/m0_37039192/article/details/77776844)

sampleRateInHz : 采样率,如44100,取值范围必须在 4000Hz~192000Hz 之间

channelConfig : 通道数,如AudioFormat.CHANNEL_IN_MONO(单通道)、AudioFormat.CHANNEL_IN_STEREO(双通道)等

audioFormat : 配置“数据位宽”,如 ENCODING_PCM_16BIT(16bit)可以保证兼容所有Android手机

bufferSizeInBytes : 配置的是 AudioTrack 内部的音频缓冲区的大小,该缓冲区的值不能低于一帧“音频帧”(Frame)的大小

一帧音频帧的大小:size = 采样率 x 位宽 x 采样时间 x 通道数,可以直接调用
int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat);来计算

2、启动,AudioRecord.startRecording()

3、录制:

  • 开启异步线程,读取音频数据
public void run() {
    while (isRunning) {
        int size = audioRecord.read(audioBuffer, 0, audioBuffer.length);
        if (isRunning && audioCore != null && size > 0) {
            audioCore.queueAudio(audioBuffer);
        }
    }
 }
  • 放到缓冲队列,可以进行声音处理

  • 传送给音频编码器的缓冲队列中(MediaCodec)(异步线程+队列)

dstAudioEncoder.queueInputBuffer(eibIndex, 0, orignAudioBuff.buff.length, nowTimeMs * 1000, 0);
  • 音频编码器循环对队列中的数据进行处理得到ByteBuffer和MediaCodec.BufferInfo,然后分发给其他端,如媒体流或文件合成
 ByteBuffer realData = dstAudioEncoder.getOutputBuffers()[eobIndex];
 realData.position(eInfo.offset);
 realData.limit(eInfo.offset + eInfo.size);
 if (isMuxerEnable && mMuxerStarted) {
     eInfo.presentationTimeUs = getPTSUs();
     muxer.writeSampleData(mTrackIndex, realData, eInfo);
     prevOutputPTSUs = eInfo.presentationTimeUs;
 }

4、停止录制,AudioRecord.stop()


视频/图像采集

MediaCodec、TextureView或GLSurfaceView

注:打开摄像头(后续再讲);opengl处理的后续和demo一起讲解
  • 使用TextureView或GLSurfaceView承载摄像头展示画面
  • 视频数据可用时,通知绘制画布(opengl相关),在数据传递给视频编码器之前可以使用opengl做特效,最终到达视频数据处理线程
  • 视频数据处理线程得到ByteBuffer和MediaCodec.BufferInfo,然后分发给其他端,如媒体流或文件合成
  • 停止录制

总结

图片1.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容