上篇博客传送门:Android音视频学习笔记(二)
经过了前面的一系列联系,这篇博客我们来总结一下如何实现一个完整的音视频录制功能
录制界面
录制界面由一个预览的窗口以及控制按钮组成,相关的布局文件如下:
1 | "1.0" encoding="utf-8" xml version= |
界面代码如下所示:
1 | public class VideoActivity extends AppCompatActivity implements SurfaceHolder.Callback, View.OnClickListener { |
界面的Activity主要是把各个模块的逻辑汇总协同工作的地方,接下来我们来单独分析每个部分
音频录制
音频录制部分主要有:
- PCM录制器
- AAC格式编码器
PCM录制器
1 | public class PcmRecorder { |
PCM录制使用的是官方的AudioRecord
官方API文档:https://developer.android.com/reference/android/media/AudioRecord
相关要注意的事项已经在学习笔记(一)中提及,这里不再赘述
AAC格式编码器
录制出视频的PCM数据后,我们需要把其编码为AAC格式,相关代码如下:
1 | public class AacEncoder { |
这里使用的是系统提供的MediaCodec接口进行硬件编码,官方文档如下:
https://developer.android.com/reference/android/media/MediaCodec
这里我们使用的MIME为audio/mp4a-latm,同时我们用了一个BlockingQueue阻塞队列来循环编码,每当一段PCM数据准备好之后,就读取并塞到MediaCodec中进行编码
由于MediaCodec编码出来的AAC数据只是原始的数据流,因此我们需要为其补上元数据(即adts头部信息)
有关adts头部信息相关的内容可以参考下面的博客:
https://blog.csdn.net/simongyley/article/details/8754157
具体的头部信息填写可参考上面代码中的方法addADTSHeader
有一个需要注意的点是,由于MediaCodec输出的编码时间有点问题(具体原因未明,主要表现是输出的时间有增有减),因此这里使用的是getPresentationTimeUs方法来计算帧时间,防止后面使用MediaMuxer混合出现卡死的情况
值得注意的是,据博主了解与尝试,由于Android碎片化的缘故,音频使用硬件编码质量并不十分稳定,而音频软件编码的耗时与稳定性都相对不错,因此建议大家学习一下使用如FFMPEG的工具来软件编码
编码后,输出的AAC帧如下所示:
1 | public class AacFrame { |
视频录制
视频录制部分主要是把摄像头回调回来的YUV数据经过处理后,使用硬件编码成H264格式输出
相关代码如下:
1 | public class H264Encoder { |
这里大部分内容与学习笔记(二)中所提及的一样,主要注意的就是YUV格式的转换以及画面内容旋转的问题,在此不再赘述
还有当我们要把音频与视频打包合成MP4的时候,一些MediaCodec.BufferInfo的flag信息以及视频时间戳也是必不可少的
编码后的H264帧如下所示:
1 | public class H264Frame { |
音视频打包混合
在分别编码出aac音频以及h264视频数据后,我们需要把他们打包混合成MP4文件
相关代码如下:
1 | public class MediaMuxerWrapper { |
这里我们使用的是官方的MediaMuxer接口,官方文档如下:
https://developer.android.com/reference/android/media/MediaMuxer
这里我们通过两个BlockingQueue阻塞队列来持续提供数据,分别使用两个线程来添加音频帧与视频帧数据
由于音频与视频编码速度存在差异(实践下经常是音频编码比视频快很多),这里我们使用CyclicBarrier来控制混合流程的开始与结束