微信号:hongyangAndroid

介绍:你好,欢迎关注鸿洋的公众号,将长期为您推荐优秀博文、开源项目、视频等.你可以通过查看历史消息,查看所有已推送的文章.此外,本公众号开通投稿,如果你有原创的文章,希望通过本公众号发布,可以给我投稿.

Android硬编码——音频编码、视频编码及音视频混合

2017-01-18 07:19 湖广午王

本文作者:湖广午王

湖广午王的博客地址:

http://blog.csdn.net/junzia




视频编解码对许多Android程序员来说都是Android中比较难的一个知识点。在Android 4.1以前,Android并没有提供硬编硬解的API,所以之前基本上都是采用FFMpeg来做视频软件编解码的,现在FFMpeg在Android的编解码上依旧广泛应用。本篇博客主要讲到的是利用Android4.1增加的API MediaCodec和Android 4.3增加的API MediaMuxer进行Mp4视频的录制。


1

概述


通常来说,对于同一平台同一硬件环境,硬编硬解的速度是快于软件编解码的。而且相比软件编解码的高CPU占用率来说,硬件编解码也有很大的优势,所以在硬件支持的情况下,一般硬件编解码是我们的首选。 


在Android中,我们可以直接使用MediaRecord来进行录像,但是在很多适合MediaRecord并不能满足我们的需求,比如我们需要对录制的视频加水印或者其他处理后,所有的平台都按照同一的大小传输到服务器等。 


在本篇博客中,将会讲到的是利用AudioRecord录音,利用OpenGL渲染相机数据并做处理。然后利用MediaCodec对音频和视频分别进行编码,使用MediaMuxer将编码后的音视频进行混合保存为Mp4的编码过程与代码示例。 


值得注意的是,音视频编解码用到的MediaCodec是Android 4.1新增的API,音视频混合用到的MediaMuxer是Android 4.3新增的API,所以本篇博客的示例只实用于Android 4.3以上的设备。


2

AudioRecord(录音API)


AudioRecord是相对MediaRecord更为底层的API,使用AudioRecord也可以很方便的完成录音功能。AudioRecord录音录制的是原始的PCM音频数据,我们可以使用AudioTrack来播放PCM音频文件。 


AudioRecord最简单的使用代码如下:




按照上面的步骤,我们就能成功的录制PCM音频文件了,但是处于传输和存储方面的考虑,一般来说,我们是不会直接录制PCM音频文件的。而是在录制过程中就对音频数据进行编码为aac、mp3、wav等其他格式的音频文件。


3
MediaCodec(硬件编解码API)


理解MediaCodec


MediaCodec的使用在Android Developer官网上有详细的说明。官网上的图能够很好的说明MediaCodec的使用方式。我们只需理解这个图,然后熟悉下MediaCodec的API就可以很快的上手使用MediaCodec来进行音视频的编解码工作了。 
 
针对于上图,我们可以把InputBuffers和OutputBuffers简单的理解为它们共同组成了一个环形的传送带,传送带上铺满了空盒子。编解码开始后,我们需要得到一个空盒子(dequeueInputBuffer),然后往空盒子中填充原料(需要被编/解码的音/视频数据),并且放回到传送带你取出时候的那个位置上面(queueInputBuffer)。


传送带经过处理器(Codec)后,盒子里面的原料被加工成了你所期望的东西(编解码后的数据),你就可以按照你放入原料时候的顺序,连带着盒子一起取出加工好的东西(dequeueOutputBuffer),并将取出来的东西贴标签(加数据头之类的非必须)和装箱(组合编码后的帧数据)操作,同样之后也要把盒子放回到原来的位置(releaseOutputBuffer)。


音频编码实例


在官网上有更规范的使用示例,结合上面的音频录制,编码为AAC音频文件示例代码如下:




AAC编码加文件头的实现参照AAC编码规则,将数据填入就好了,网上很容易找到,具体实现如下:




这样,得到的文件就是AAC音频文件了,一般Android系统自带的播放器都可以直接播放。


视频编码实例


视频的编码和上面音频的编码也大同小异。摄像头的数据回调时间并不是确定的,就算你设置了摄像头FPS范围为30-30帧,它也不会每秒就一定给你30帧数据。Android摄像头的数据回调,受光线的影响非常严重,这是由HAL层的3A算法决定的,你可以将自动曝光补偿、自动白平光等等给关掉,这样你才有可能得到稳定的帧率。 


而我们录制并编码视频的时候,肯定是希望得到一个固定帧率的视频。所以在视频录制并进行编码的过程中,需要自己想些法子,让帧率固定下来。最简单也是最有效的做法就是,按照固定时间编码,如果没有新的摄像头数据回调来就用上一帧的数据。 


参考代码如下:



对于其他格式的音频视频编解码也大同小异了,只要MediaCodec支持就好。


4

MediaMuxer(音视频混合API)

MediaMuxer的使用很简单,在Android Developer官网上MediaMuxer的API说明中,也有其简单的使用示例代码:




参照官方的说明和代码示例,我们可以知道,音视频混合(也可以音频和音频混合),只需要将编码器的MediaFormat加入到MediaMuxer中,得到一个音轨视频轨的索引,然后每次从编码器中取出来的ByteBuffer,写入(writeSampleData)到编码器所在的轨道中就ok了。 


这里需要注意的是,一定要等编码器设置编码格式完成后,再将它加入到混合器中,编码器编码格式设置完成的标志是dequeueOutputBuffer得到返回值为MediaCodec.INFO_OUTPUT_FORMAT_CHANGED。


5
音视频录制MP4文件


上面已经给出了音频录制的代码和视频录制的代码,利用MediaMuxer将其结合起来,就可以和简单的完成录制有声音有图像的MP4文件的功能了。音频录制和视频录制的基本流程保持不变,在录制编码后,不再将编码的结果写入到文件流中,而是写入为混合器的sample data。以视频为例,更改循环编码的代码为:




当然是用MediaMuxer前,肯定是需要创建一个MediaMuxer的实例的:

mMuxer=new MediaMuxer(path+"."+postfix, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);


音频的操作和视频一样更改,将音频编码也加入MeidaMuxer的轨道中,得到一个轨道索引,将编码后的数据加入为MediaMuxer当前音轨的sample data。音轨和上面的视轨各自做各自的,结束录制时,都发送结束标志,然后在编码结束后,停止混合器就可以得到一个固定码率的MP4文件了。


6

总结


至此,本篇博客就结束了。但是在实际使用MediaCodec和MediaMuxer的过程中,总会遇到这样或者那样的问题,硬编硬解,和硬件相关比较紧密,Android虽然提供了一个很好的API,但是各个厂商在实现的过程中,总是会做些让自己变得独特的事情。当然他们的目的并不是为了独特,有的是为了让产品变得更优秀(虽然最后可能会做砸了),有的是为了省钱,用软件去弥补硬件的缺陷,最后的结果就是苦了做上层开发的码农们。 


从博主在使用MediaCodec和MediaMuxer的过程中遇到的问题,总结下需要注意主要有以下几点:


  1. MediaCodec是Android4.1新增API,MediaMuxer是Android4.3新增API。

  2. 颜色空间。按照Android本身的意思,COLOR_FormatYUV420Planar应该是所有硬件平台都支持的。但是实际上并不是这样。所以在设置颜色空间时,应该获取硬件平台所支持的颜色空间,确保它是支持你打算使用的颜色空间,不支持的话应该启用备用方案(使用其他当前硬件支持的颜色空间)。

  3. 视频尺寸,在一些手机上,视频录制的尺寸可以是任意的。但是有些手机,不支持的尺寸设置会导致录制的视频现错乱。博主在使用Oppo R7测试,360*640的视频,单独录制视频没问题,音视频混合后,出现了颜色错乱的情况,而在360F4手机上,却都是没问题的。将视频宽高都设置为16的倍数,可以解决这个问题。

  4. 编码器格式设置,诸如音频编码的采样率、比特率等,取值也需要结合硬件平台来设置,否则也会导致崩溃或其他问题。这个其实和颜色空间的选择一样。

  5. 网上看到许多queueInputBuffer中设置presentationTimeUs为System.nanoTime()/1000,这样做会导致编码出来的音视频,在播放时,总时长显示的是错误的。应该记录开始时候的nanoTime,然后设置presentationTimeUs为(System.nanoTime()-nanoTime)/1000。

  6. 录制结束时,应该发送结束标志MediaCodec.BUFFER_FLAG_END_OF_STREAM,在编码后区获得这个标志时再终止循环,而不是直接终止循环。


应该还有其他需要注意的问题。我暂时还没遇到。


源码在

  • https://github.com/doggycoder/AudioVideo/tree/master/codec

  • https://github.com/doggycoder/AudioVideo




如果你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可。


欢迎长按下图->识别图中二维码或者扫一扫关注我的公众号:

 
鸿洋 更多文章 Android仿淘宝头条垂直滚动广告条 [Android]五行代码实现 炫动滑动 卡片层叠布局 [Android]五行代码实现 炫动滑动 卡片层叠布局 Android 如何实现一个平滑过渡的ViewPager广告条 Android 如何实现一个平滑过渡的ViewPager广告条
猜您喜欢 【Android 原创】简单过搜狗输入法签名校验 The Swift Programming Language--语言指南--协议 安卓辅助性测试清单 华为网院杯2015全国大学生ICT技能大赛山东赛区复赛颁奖仪式暨华为人才联盟山东区招聘会 【视频+音频】一盒好吃到开除的月饼