读书笔记--第四章 移动平台下的音视频渲染
作者:互联网
本章将主要讲解Android平台上的音视频渲染,即接收到音视频的原始数据之后,如何利用Android平台的API渲染到扬声器(Speaker)或者屏幕(View)上。声音的渲染在Android平台使用OpenSL ES或者AudioTrack这两类接口。而对于视频画面的渲染,为大家介绍一种跨平台的渲染技术,即OpenGL ES,本章将根据平台提供的API来构造出OpenGL ES环境,然后再利用统一的OpenGL程序(Program)将一张图片渲染到屏幕上。
4.1 Android平台的音频渲染
Android的SDK(指的是Java层提供的API,对应的NDK是Native层提供的API,即C或者C++层可以调用的API)提供了3套音频播放的API,分别是:MediaPlayer、SoundPool和AudioTrack。这三个API的使用场景各不相同,简单来说具体如下。
·MediaPlayer:适合在后台长时间播放本地音乐文件或者在线的流式媒体文件,它的封装层次比较高,使用方式比较简单。
SoundPool:适合播放比较短的音频片段,比如游戏声音、按键声音、铃声片段等,它可以同时播放多个音频。
·AudioTrack:适合低延迟的播放,是更加底层的API,提供了非常强大的控制能力,适合流媒体的播放等场景,由于其属于底层API,所以需要结合解码器来使用。
Android的NDK提供了OpenSL ES的C语言的接口,可以提供非常强大的音效处理、低延时播放等功能,比如在Android手机上可实现实时耳返的功能。本书的项目案例中会更多地使用到底层API的功能,下面就来详细地介绍AudioTrack与OpenSL ES这两个API的使用。
4.1.1 AudioTrack的使用
由于AudioTrack是Android SDK层提供的最底层的音频播放API,因此只允许输入裸数据。和MediaPlayer相比,对于一个压缩的音频文件(比如MP3、AAC等文件),它需要自行实现解码操作和缓冲区控制。因为这里只涉及AudioTrack的音频渲染端(解码部分已经在前面章节中介绍过了,对于缓冲区的控制机制,后续章节将会详细讲解),所以本节只介绍如何使用AudioTrack渲染音频PCM数据。
首先来看一下AudioTrack的工作流程,具体如下。
1)根据音频参数信息,配置出一个AudioTrack的实例。
2)调用play方法,将AudioTrack切换到播放状态。
3)启动播放线程,循环向AudioTrack的缓冲区中写入音频数据。
4)当数据写完或者停止播放的时候,停止播放线程,并且释放所有资源。
根据AudioTrack的上述工作流程,本节将以4个小部分分别介绍每个流程的详细步骤。
1.配置AudioTrack
先来看一下AudioTrack的参数配置,要想构造出一个AudioTrack类型的实例,必须先了解其构造函数原型,代码如下所示:
public AudioTrack(int streamType,int sampleRateInHz,int channelConfig,
int audioFormat,int bufferSizeInBytes,int mode);
其中构造函数的参数说明如下。
·streamType,Android手机上提供了多重音频管理策略(读者按一
下手机侧边的按键,可以看到有多个音量管理,这其实就是不同音频策略的音量控制展示),当系统有多个进程需要播放音频的时候,管理策略会决定最终的呈现效果,该参数的可选值将以常量的形式定义在类AudioManager中,主要包括以下内容。
STREAM_VOCIE_CALL:电话声音
STREAM_SYSTEM:系统声音
STREAM_RING:铃声
STREAM_MUSCI:音乐声
STREAM_ALARM:警告声
STREAM_NOTIFICATION:通知声
sampleRateInHz,采样率,即播放的音频每秒钟会有多少次采样,可选用的采样频率列表为:8000、16000、22050、24000、32000、44100、48000等,大家可以根据自己的应用场景进行合理的选择。
·channelConfig,声道数(通道数)的配置,可选值以常量的形式配置在类AudioFormat中,常用的是CHANNEL_IN_MONO(单声道)、CHANNEL_IN_STEREO(双声道),因为现在大多数手机的麦克风都是伪立体声的采集,为了性能考虑,笔者建议使用单声道进行采集,而转变为立体声的过程可以在声音的特效处理阶段来完成。
·audioFormat,该参数是用来配置“数据位宽”的,即采样格式,可选值以常量的形式定义在类AudioFormat中,分别为ENCODING_PCM_16BIT(16bit)、ENCODING_PCM_8BIT(8bit),注意,前者是可
以兼容所有Android手机的。
·bufferSizeInBytes,其配置的是AudioTrack内部的音频缓冲区的大小,AudioTrack类提供了一个帮助开发者确定bufferSizeInBytes的函数,其原型具体如下:
int getMinBufferSize(int sampleRateInHz,int channelConfig,int audioFormat);
·在实际开发中,强烈建议由该函数计算出需要传入的bufferSizeInBytes,而不是自己手动计算。
·mode,AudioTrack提供了两种播放模式,可选的值以常量的形式定义在类AudioTrack中,一个是MODE_STATIC,需要一次性将所有的数据都写入播放缓冲区中,简单高效,通常用于播放铃声、系统提醒的音频
片段;另一个是MODE_STREAM,需要按照一定的时间间隔不间断地写入音频数据,理论上它可以应用于任何音频播放的场景。
2.将AudioTrack切换到播放状态
首先判断AudioTrack实例是否初始化成功,如果当前状态处于初始化成功的状态,那么就调用它的play方法,并切换到播放状态,代码如下:
if (null!= audioTrack && audioTrack.getState()!= AudioTrack.STATE_UNINITIALIZED)
{
audioTrack.play();
}
3.开启播放线程
首先创建一个播放线程,代码如下:
playerThread = new Thread(new PlayerThread(),“playerThread”);
playerThread.start();
接下来看看该线程中执行的任务,代码如下:
class PlayerThread implements Runnable {
private short[]samples;
public void run() {
samples = new short[minBufferSize];
while(!isStop) {
int actualSize = decoder.readSamples(samples);
audioTrack.write(samples,actualSize);
}
}
}
线程中的minBufferSize是在初始化AudioTrack的时候获得的缓冲区大小,会对其进行换算,即以2个字节表示一个采样的大小,也就是2倍的关系(因为初始化的时候是以字节为单位的);decoder是一个解码器,假设已经初始化成功,最后将调用write方法把从解码器中获得的PCM采样数据写入AudioTrack的缓冲区中,注意此方法是阻塞的方法,比如:一般要写入200ms的音频数据需
要执行接近200ms的时间。
4.销毁资源
首先停止AudioTrack,代码如下:
if (null!= audioTrack && audioTrack.getState()!= AudioTrack.STATE_UNINITIALIZED)
{
audioTrack.stop();
}
然后停止线程:
4.2.2 OpenSL ES的使用
OpenSL ES全称为Open Sound Library for Embedded Systems,即嵌入式音频加速标准。OpenSL ES是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API。它为嵌入式移动多媒体设备上的本地应用程序开发者提供了标准化、高性能、低响应时间的音频功能实现方法,同时还实现了软/硬件音频性能的直接跨平台部署,不仅降低了执行难度,而且促进了高级音频市场的发展。
图4-4描述了OpenSL ES的架构,在Android中,High Level Audio Libs是音频Java层API输入输出,属于高级API,相对来说,OpenSL ES则是比较低层级的API,属于C语言API。在开发中,一般会使用高级API,除非遇到性能瓶颈,如语音实时聊天、3D Audio、某些Effects等,开发者可以直接通过C/C++开发基于OpenSL ES音频的应用。需要声明的是:本书中使用的是OpenSL ES
标签:读书笔记,--,AudioTrack,音频,音视频,API,Android,播放,ES 来源: https://blog.csdn.net/yaoming168/article/details/114295452