在Android多媒体类,MediaMuxer和MediaCodec这是一个相对年轻,他们是JB 4.1和JB 4.3据介绍。

前者被用来产生一个混合的音频和视频的多媒体文件。的缺点是,现在可以只支持一个audio track而一个video track,而唯一支持mp4出口。然是新生事物,相信之后的版本号应该会有大的改进。MediaCodec用于将音视频进行压缩编码,它有个比較牛X的地方是能够对Surface内容进行编码,如KK 4.4中屏幕录像功能就是用它实现的。



注意它们和其他一些多媒体相关类的关系和差别:MediaExtractor用于音视频分路,和MediaMuxer正好是反过程。MediaFormat用于描写叙述多媒体数据的格式。MediaRecorder用于录像+压缩编码。生成编码好的文件如mp4, 3gpp,视频主要是用于录制Camera preview。MediaPlayer用于播放压缩编码后的音视频文件。

AudioRecord用于录制PCM数据。AudioTrack用于播放PCM数据。PCM即原始音频採样数据。能够用如vlc播放器播放。

当然了,通道採样率之类的要自己设。由于原始採样数据是没有文件头的,如:

vlc --demux=rawaud --rawaud-channels 2 --rawaud-samplerate 44100 audio.pcm



回到MediaMuxer和MediaCodec这两个类,它们的參考文档见http://developer.android.com/reference/android/media/MediaMuxer.html和http://developer.android.com/reference/android/media/MediaCodec.html。里边有使用的框架。这个组合能够实现非常多功能,比方音视频文件的编辑(结合MediaExtractor),用OpenGL绘制Surface并生成mp4文件,屏幕录像以及类似Camera
app里的录像功能(尽管这个用MediaRecorder更合适)等。

这里以一个非常无聊的功能为例,就是在一个Surface上绘图编码生成视频。同一时候用MIC录音编码生成音频,然后将音视频混合生成mp4文件。

程序本身没什么用。可是演示样例了MediaMuxer和MediaCodec的基本使用方法。本程序主要是基于两个測试程序:一个是Grafika中的SoftInputSurfaceActivity和HWEncoderExperiments。它们一个是生成视频,一个生成音频,这里把它们结合一下,同一时候生成音频和视频。基本框架和流程例如以下:

首先是录音线程,主要參考HWEncoderExperiments。

通过AudioRecord类接收来自麦克风的採样数据,然后丢给Encoder准备编码:

AudioRecord audio_recorder;
audio_recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, buffer_size);
// ...
audio_recorder.startRecording();
while (is_recording) {
byte[] this_buffer = new byte[frame_buffer_size];
read_result = audio_recorder.read(this_buffer, 0, frame_buffer_size); // read audio raw data
// …
presentationTimeStamp = System.nanoTime() / 1000;
audioEncoder.offerAudioEncoder(this_buffer.clone(), presentationTimeStamp); // feed to audio encoder }

这里也能够设置AudioRecord的回调(通过setRecordPositionUpdateListener())来触发音频数据的读取。offerAudioEncoder()里主要是把audio採样数据送入音频MediaCodec的InputBuffer进行编码:

ByteBuffer[] inputBuffers = mAudioEncoder.getInputBuffers();
int inputBufferIndex = mAudioEncoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(this_buffer);
...
mAudioEncoder.queueInputBuffer(inputBufferIndex, 0, this_buffer.length, presentationTimeStamp, 0);
}

以下,參考Grafika-SoftInputSurfaceActivity,并增加音频处理。

主循环大体分四部分:

try {
// Part 1
prepareEncoder(outputFile);
...
// Part 2
for (int i = 0; i < NUM_FRAMES; i++) {
generateFrame(i);
drainVideoEncoder(false);
drainAudioEncoder(false);
}
// Part 3
...
drainVideoEncoder(true);
drainAudioEncoder(true);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
// Part 4
releaseEncoder();
}

第1部分是准备工作。除了video的MediaCodec,这里还初始化了audio的MediaCodec:

MediaFormat audioFormat = new MediaFormat();
audioFormat.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
audioFormat.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
...
mAudioEncoder = MediaCodec.createEncoderByType(AUDIO_MIME_TYPE);
mAudioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mAudioEncoder.start();

第2部分进入主循环,app在Surface上直接画图,因为这个Surface是从MediaCodec中用createInputSurface()申请来的,所以画完后不用显式用queueInputBuffer()交给Encoder。

drainVideoEncoder()和drainAudioEncoder()分别将编码好的音视频从buffer中拿出来(通过dequeueOutputBuffer()),然后交由MediaMuxer进行混合(通过writeSampleData())。

注意音视频通过PTS(Presentation
time stamp。决定了某一帧的音视频数据何时显示或播放)来同步,音频的time stamp需在AudioRecord从MIC採集到数据时获取并放到对应的bufferInfo中,视频因为是在Surface上画,因此直接用dequeueOutputBuffer()出来的bufferInfo中的即可,最后将编码好的数据送去MediaMuxer进行多路混合。

注意这里Muxer要等把audio track和video track都增加了再開始。

MediaCodec在一開始调用dequeueOutputBuffer()时会返回一次INFO_OUTPUT_FORMAT_CHANGED消息。

我们仅仅需在这里获取该MediaCodec的format,并注冊到MediaMuxer里。

接着推断当前audio track和video track是否都已就绪,假设是的话就启动Muxer。



总结来说,drainVideoEncoder()的主逻辑大致例如以下,drainAudioEncoder也是类似的。仅仅是把video的MediaCodec换成audio的MediaCodec就可以。

while(true) {
int encoderStatus = mVideoEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
...
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
encoderOutputBuffers = mVideoEncoder.getOutputBuffers();
} else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat newFormat = mAudioEncoder.getOutputFormat();
mAudioTrackIndex = mMuxer.addTrack(newFormat);
mNumTracksAdded++;
if (mNumTracksAdded == TOTAL_NUM_TRACKS) {
mMuxer.start();
}
} else if (encoderStatus < 0) {
...
} else {
ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
...
if (mBufferInfo.size != 0) {
mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
}
mVideoEncoder.releaseOutputBuffer(encoderStatus, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}
} }

第3部分是结束录制。发送EOS信息。这样在drainVideoEncoder()和drainAudioEncoder中就能够依据EOS退出内循环。

第4部分为清理工作。

把audio和video的MediaCodec,MediaCodec用的Surface及MediaMuxer对象释放。



最后几点注意:

1. 在AndroidManifest.xml里加上录音权限,否则创建AudioRecord对象时铁定失败:

 <uses-permission android:name="android.permission.RECORD_AUDIO"/>

2. 音视频通过PTS同步,两个的单位要一致。

3. MediaMuxer的使用要依照Constructor -> addTrack -> start -> writeSampleData -> stop 的顺序。假设既有音频又有视频,在stop前两个都要writeSampleData()过。



Code references:

Grafika: https://github.com/google/grafika

Bigflake: http://bigflake.com/mediacodec/

HWEncoderExperiments:https://github.com/OnlyInAmerica/HWEncoderExperiments/tree/audioonly/HWEncoderExperiments/src/main/java/net/openwatch/hwencoderexperiments

Android test:http://androidxref.com/4.4.2_r2/xref/cts/tests/tests/media/src/android/media/cts/ 

http://androidxref.com/4.4.2_r2/xref/pdk/apps/TestingCamera2/src/com/android/testingcamera2/CameraRecordingStream.java

Android在MediaMuxer和MediaCodec用例 - audio+video的更多相关文章

  1. android 原生 MediaPlayer 和 MediaCodec 的区别和联系(三)

    目录:     (4)Android 官方网站 对 MediaCodec的介绍 注:编解码器特定数据(Code-specific Data,简写为csd) 部分结合网上资料加入了补充和个人理解.请悉知 ...

  2. Android BottomSheet:以选取图片为例(2)

     Android BottomSheet:以选取图片为例(2) 附录文章5简单介绍了常见的分享面板在BottomSheet中的具体应用.本文再以常见的选取图片为例写一个例子. 布局文件: < ...

  3. 使用HTML5抓取 Audio & Video

    原文地址: http://www.html5rocks.com/en/tutorials/getusermedia/intro/ 本地化的文章: http://www.html5rocks.com/z ...

  4. [jPlayer] HTML5 Audio & Video for jQuery

    ---------------------------------------------------------------------------------------------------- ...

  5. The jQuery HTML5 Audio / Video Library (jQuery jPlayer插件给你的站点增加视频和音频功能)

    http://jplayer.org/ The jQuery HTML5 Audio / Video Library jPlayer is the completely free and open s ...

  6. HTML5 Audio/Video 标签,属性,方法,事件汇总

    HTML5 Audio/Video 标签,属性,方法,事件汇总 (转) 2011-06-28 13:16:48   <audio> 标签属性:src:音乐的URLpreload:预加载au ...

  7. Capturing Audio & Video in HTML5

    使用HTML5抓取 Audio & Video 原文地址: http://www.html5rocks.com/en/tutorials/getusermedia/intro/ 本地化的文章: ...

  8. [转载]HTML5 Audio/Video 标签,属性,方法,事件汇总

    <audio> 标签属性: src:音乐的URL preload:预加载 autoplay:自动播放 loop:循环播放 controls:浏览器自带的控制条 <audio id=& ...

  9. 如何让windows版Safari支持H5 audio/video?

    今天在windows版Safari上看效果的时候惊奇地发现它竟然不支持HTML5的audio/video, 这样的话就无法复现不少ios上出现的问题. 在同事提醒下, 发现Safari HTML5 A ...

随机推荐

  1. bzoj3237

    首先我们可以把没有询问过的边处理掉,重构图 当然这样也不影响复杂度 考虑到每次询问要删除的边很少,我们完全可以整体处理 把询问划分成两个集合,在前半部分询问未出现边我们可以整体处理掉,缩点重编号(询问 ...

  2. 如何将域中的AD数据导入SharePoint

    转:http://www.cnblogs.com/wallis0922/archive/2010/09/29/1838292.html 最近刚装好sharepoint2010,想要研究一下,第一件想做 ...

  3. SafeHandle和Dispose z

    SafeHandle最大的意义是封装一个托管资源且本身会执行.NET中的资源释放模式(所谓的Dispose Pattern),这样,开发者在使用非托管资源时,不可以不需要执行繁琐的资源释放模式,而直接 ...

  4. 50道经典的JAVA编程题(46-50)

    50道经典的JAVA编程题(46-50),最后五道题了,这是一个美妙的过程,编程真的能让我忘掉一切投入其中,感觉很棒.今天下午考完微机原理了,大三上学期就这样度过了,这学期算是解放了,可是感觉我还是没 ...

  5. Ext.useShims=true

    Extjs的panel中嵌套ActiveX的插件,如PDF,但是Ext控件被遮罩 eg.在panel的tbar中加入下拉框,结果其下拉值看不到,原因就是被PDF给遮住了, 此时只需设置Ext.useS ...

  6. 开启sa账户以及如何用JDBC进行连接

    做实验需要用Java与SQL Server连接,因为使用的 SQL 2008 Express Edition 是基于 Visual Studio2010 安装包安装时一起安装的,所以为了方便数据库的操 ...

  7. 50个Java多线程面试题

    不管你是新程序员还是老手,你一定在面试中遇到过有关线程的问题.Java 语言一个重要的特点就是内置了对并发的支持,让 Java 大受企业和程序员的欢迎.大多数待遇丰厚的 Java 开发职位都要求开发者 ...

  8. 说说单节点集群里安装hive、3\5节点集群里安装hive的诡异区别

    这几天,无意之间,被这件事情给迷惑,不解!先暂时贴于此,以后再解决! 详细问题如下: 在hive的安装目录下(我这里是 /home/hadoop/app/hive-1.2.1),hive的安装目录的l ...

  9. ABAP报表程序编写规范 .

    一.              程序的结构: 典型的ABAP报表程序是由多个代码区域组成,各代码区的存放位置以下列顺序为准,具体格式如下: *----------------------------- ...

  10. ALM11 OTA API接口的问题

    ALM11 在安装的时候好像不会自动加载OTA接口. 正常情况下, OTA的接口文件的路径为: C:\Program Files\Common Files\Mercury Interactive\Qu ...