FFmpeg从入门到入魔(4):OpenSL ES播放PCM音频

本文介绍了OpenSL ES的基本原理和在Android平台上的应用,特别是如何使用OpenSL ES与FFmpeg结合播放PCM音频。通过创建Engine Object、Audio Player对象和Output Mix对象,设置缓冲队列接口,实现音频解码和播放。文中还提供了Android项目实战的流程,包括解码线程和播放线程的详细操作。
摘要由CSDN通过智能技术生成

1. OpenSL ES原理

 OpenSL ES(Open Sound Library for Embedded Systems),即嵌入式音频加速标准,是一个无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API库。它为嵌入移动多媒体设备上的本地应用程序开发者提供了标准化、高性能、低相应时间的音频开发方案,并实现软/硬件音频性能的直接跨平台部署,被广泛应用于3D音效、音频播放、音频录制以及音乐体验增强(低音增强和环境混响)等方面。对于Android平台而言,我们可以使用OpenSL ES库直接在native层处理音频数据,比如录制音频、播放音频等。OpenSL ES嵌入式设备中部署的软/硬件层次结构,如下图所示:

1.1 OpenSL ES核心API讲解

 虽然OpenSL ES是基于C语言开发,但是它的实现思想是基于面向对象的,与FFmpeg框架提供一系列的函数接口不同,OpenSL ES是以Interface的方式来提供API,即除了slCreateEngine这个函数用来创建声音引擎对象接口之外,其他所有的操作都是通过调用接口的成员函数完成的,这与JNI非常相似。比如我们调用OpenSL ES的API是这样的:

SLObjectItf pEngineObject = NULL;
(*pEngineObject)->Realize(pEngineObject, SL_BOOLEAN_FALSE);

【腾讯文档】FFmpegWebRTCRTMPRTSPHLSRTP播放器-音视频流媒体高级开发-资料领取
https://docs.qq.com/doc/DYU5ORlBOdkpCUkNxicon-default.png?t=M85Bhttps://docs.qq.com/doc/DYU5ORlBOdkpCUkNx


1.1.1 对象(Object)与接口(Interface)

 Object和Interface是OpenSL ES库的两个非常重要的概念,OpenSL ES的整个框架就是基于这两个概念构成的,后续对该库的使用也是基于此来实现。具体关系如下:  1.每个Object可能存在一个或者多个Interface,而每个Interface封装了相关的功能函数。比如当我们获取一个Audio Player对象后,可以通过该对象得到音频播放Interface、音量Interface、缓存队列Interface,然后调用这些Interface的功能函数实现音频播放、音量调节等功能;

// OpenSL ES引擎Interface
SLEngineItf pEngineItf = NULL;
...
SLObjectItf pPlayerObject = NULL;  // Audio Player对象
SLPlayItf pPlayerItf = NULL;	   // 播放接口
SLVolumeItf pVolumeItf = NULL;	   // 音量接口
SLAndroidSimpleBufferQueueItf pBufferItf = NULL; // 缓存队列接口
(*pEngineItf)->CreateAudioPlayer(pEngineItf,&pPlayerObject,..);
(*pPlayerObject)->Realize(pPlayerObject,SL_BOOLEAN_FALSE);               
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_PLAY,&pPlayerItf); 
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_VOLUME,&pVolumeItf);     
(*pPlayerObject)->GetInterface(pPlayerObject,SL_IID_BUFFERQUEUE,&pBufferItf);  

 2.每个Object对象提供了一些最基础的"管理"操作,比如它的Realize、Destory函数用于分配、释放资源,Resume函数用于结束SL_OBJECT_STATE_SUSPENED状态等等。如果系统使用该对象支持的功能函数,就需要通过该对象的GetInterface函数获取相应的Interface接口,然后通过该Interface接口来访问功能函数。下面以调节音量为例:

// OpenSL ES引擎Interface
SLEngineItf pEngineItf = NULL;
...
SLObjectItf pPlayerObject = NULL;  
// 首先,创建Audio Player对象
(*pEngineItf)->CreateAudioPlayer(pEngineItf,&pPlayerObject,..); 
// 其次,初始化Audio Player对象,即分配资源
(*pPlayerObject)->Realize(pPlayerObject,SL_BOOLEAN_FALSE); 
// 第三,获取Audio Player对象的音量(Volume)Interface
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_VOLUME,&pVolumeItf);  
// 最后,调用Volume Interface的调节音量功能函数
(*pVolumeItf)->SetVolumeLevel(&pVolumeItf,level);

注意:由于OpenSL ES库是跨平台的,但是并非所有平台都实现了某个对象(Object)定义的所有接口(Interface),因此在使用的过程中,最好还是对获取的Interface作一些选择和判断,以免出现预料不到的错误。

1.1.2 OpenSL ES的状态机制

 OpenSL ES有个比较重要的概念-状态机制,即对于任何OpenSL ES的对象,在被创建成功后都会进入SL_OBJECT_STATE_UNREALIZE状态,此时系统不会为该对象分配任何资源;当调用对象的Realize()成员函数后,该对象就会进入SL_OBJECT_STATE_REALIZE状态,此时对象的各个功能和资源才能正常被访问;当然,当突然出现一些系统事件,比如系统错误或者Audio设备被其他应用抢占,该对象就会进入SL_OBJECT_STATE_SUSPENED状态,如果我们希望恢复正常使用,就需要调用对象的Resume函数;最后,我们可以调用对象的Destory函数,来释放资源,此时对象的状态会回到SL_OBJECT_STATE_UNREALIZE状态。OpenSL ES状态机制转化流程如下图所示:

1.1.3 OpenSL ES重要接口

(1) SLObjectItf:对象接口,是Sound Library Object Interface的缩写,表示一个泛指对象,就像Java中的Object一样,OpenSL ES中的所有对象均由它来表示,但具体代表是哪个对象,就由SLEngineItf的相关函数决定(注:SLEngineItf是Engine Object的接口)。创建对象:

// 创建OpenSL SL引擎对象(Engine Object)
SLObjectItf pEngineObject = NULL;
slCreateEngine(&pEngineObject, 0, NULL, 0, NULL, NULL);
// 创建混音器对象(OutputMix Object)
SLObjectItf pOutputMixObject = NULL;
(*pEngineItf)->CreateOutputMix(pEngineItf,&pOutputMixObject,...);
// 创建播放器对象(Player Object)
SLObjectItf pPlayerObject = NULL;
(*pEngineItf)->CreateAudioPlayer(pEngineItf,&pPlayerObject,...);

  如果是销毁某个对象,调用它的Destory成员函数即可。

if (pEngineObject) {
	(*pEngineObject)->Destroy(pEngineObject);
}
if (pOutputMixObject) {
	(*pOutputMixObject)->Destroy(pOutputMixObject);
}
if (pPlayerObject) {
	(*pPlayerObject)->Destroy(pPlayerObject);
}

 重难讲一下,Engine Object是OpenSL ES API的入口点,是OpenSL ES中最核心的对象,用于管理Audio Engine生命周期和创建OpenSL ES中所有其他的对象。Engine Object由slCreateEngine函数创建,创建的结果是得到Engine Object的一个接口-SLEngineItf,在这个接口的结构体中封装了创建其他各种对象的函数。SLObjectItf结构体定义如下,位于.../SLES/OpenSLES.h头文件中:

struct SLObjectItf_
Table of Contents PART 1: USER MANUAL ......................................................................................... 1 1 OVERVIEW ..................................................................................................... 2 2 FEATURES AND PROFILES .............................................................................. 9 3 DESIGN OVERVIEW ...................................................................................... 14 FUNCTIONAL OVERVIEW .............................................................................. 30 PART 2: API REFERENCE ..................................................................................... 60 5 BASE TYPES AND UNITS ............................................................................... 61 6 FUNCTIONS .................................................................................................. 64 7 OBJECT DEFINITIONS .................................................................................. 67 8 INTERFACE DEFINITIONS ............................................................................ 93 9 MACROS AND TYPEDEFS............................................................................. 439 PART 3: APPENDICES ........................................................................................ 501 APPENDIX A: REFERENCES ............................................................................... 502 APPENDIX B: SAMPLE CODE.............................................................................. 504 APPENDIX C: USE CASE SAMPLE CODE .............................................................. 555 APPENDIX D: OBJECT-INTERFACE MAPPING..................................................... 585
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值