Android音频开发(5):音频数据的编解码

  

前面四篇文章分别介绍了音频开发必备的基础知识,如何采集一帧音频,如何播放一帧音频,如何存储和解析wav格式的文件,建议有兴趣的小伙伴们先读一读,本文则重点关注如何对一帧音频数据进行编码和解码。


<强> 1。Android官方的MediaCodec API


首先,我们了解一下安卓官方提供的音频编解码的API,即MediaCodec类,该API是在Andorid 4.1 API(16)版本引入的,因此只能工作于Android 4.1以上的手机上。


<强> 1.1 MediaCodec基本介绍


(1)提供了一套访问Android底层多媒体模块的接口,主要是音视频的编解码接口


(2) Android底层多媒体模块采用的是OpenMax框架,任何Android底层编解码模块的实现,都必须遵循OpenMax标准.Google官方默认提供了一系列的软件编×××:包括:OMX.google.h364.encoder OMX.google.h364。编码器,OMX.google.aac。编码器,OMX.google.aac.decoder等等,而硬件编解码功能,则需要由芯片厂商依照OpenMax框架标准来完成,所以,一般采用不同芯片型号的手机,硬件编解码的实现和性能是不同的


(3) Android应用层统一由MediaCodec API来提供各种音视频编解码功能,由参数配置来决定采用何种编解码算法,是否采用硬件编解码加速等等


<强> 1.2 MediaCodec核心原理


我不准备详细介绍MediaCodec API的每个函数是怎么用,示例代码大家可以在后面给出的资源链接中查看和学习。


这里我准备重点介绍一下MediaCodec的核心工作原理,因为只有搞清楚了这一点,你才会明白为什么MediaCodec API提供的接口是这个样子的。


MediaCodec使用的基本流程是:


安康;createEncoderByType/createDecoderByType   安康;配置   开始的;   安康;(1),{   ,,,安康;dequeueInputBuffer   ,,,安康;queueInputBuffer   ,,,安康;dequeueOutputBuffer   ,,,安康;releaseOutputBuffer   }   安康;停止   - release


由此可以看到,Buffer 队列的操作是其最核心的部分之一,关于 MediaCodec 的 Buffer 队列 ,示意图如下:

Android音频开发(5):音频数据的编解码



MediaCodec 架构上采用了2个缓冲区队列,异步处理数据,下面描述的 Client 和 MediaCodec 模块是并行工作的(注:这里的 Client 就是指 “开发者,API 的使用者”):


(1)Client 从 input 缓冲区队列申请 empty buffer [dequeueInputBuffer]

(2)Client 把需要编解码的数据拷贝到 empty buffer,然后放入 input 缓冲区队列 [queueInputBuffer] 

(3)MediaCodec 模块从 input 缓冲区队列取一帧数据进行编解码处理

(4)编解码处理结束后,MediaCodec 将原始数据 buffer 置为 empty 后放回 input 缓冲区队列,将编解码后的数据放入到 output 缓冲区队列

(5)Client 从 output 缓冲区队列申请编解码后的 buffer [dequeueOutputBuffer]

(6)Client 对编解码后的 buffer 进行渲染/播放

(7)渲染/播放完成后,Client 再将该 buffer 放回 output 缓冲区队列 [releaseOutputBuffer]


MediaCodec 在架构上,其实是采用了一种基于“环形缓冲区”的“生产者-消费者”模式,它设计了 2 个基于 idx 序号的“环形缓冲区” ,注意,是 2 个,一个在 input 端, 一个在 output 端。


我曾经在 Github 上分享过一段 Linux C 代码,名叫:“rw_queue”,就是这种环形缓冲区的简化版,大家有兴趣可以看看,地址:https://github.com/Jhuster/clib/tree/master/rw_queue


基于 idx 的环形缓冲区的总体示意图如下,图中,wp 代表 “写指针”,指向的是 “empty buffer”, 而 rp 代表 “读指针”,指向的是 “filled buffer”:


Android音频开发(5):音频数据的编解码

“生产者”和“消费者”其实是共用这一个缓冲区队列,“生产者”负责从队列中取出未使用的 Buffer,填入数据,然后放回队列,“消费者”则负责取出填入数据后的 Buffer,进行处理,处理结束后,再把 Buffer 标记为“空”,退回到队列中去以供“生产者”继续填充数据。


在 input 端,“Client”是这个环形缓冲区“生产者”,“MediaoCodec 模块”是“消费者”。

Android音频开发(5):音频数据的编解码