1. 程式人生 > >Android直播開發之旅(13):使用FFmpeg+OpenSL ES播放PCM音訊

Android直播開發之旅(13):使用FFmpeg+OpenSL ES播放PCM音訊

Android直播開發之旅(12):初探FFmpeg開源框架一文中,我們詳細介紹了FFmpeg框架的架構、音視訊相關術語以及重要的結構體。為了能夠對這些重要的結構體有個深入的理解,本文將在此基礎上,利用FFmpeg解析rtsp資料流以獲取AAC音訊資料,再對AAC進行解碼為PCM,然後結合OpenSL ES庫本地實時播放解碼得到的PCM音訊資料。

1. OpenSL ES原理

 OpenSL ES(Open Sound Library for Embedded Systems),即嵌入式音訊加速標準,是一個無授權費、跨平臺、針對嵌入式系統精心優化的硬體音訊加速API庫。它為嵌入移動多媒體裝置上的本地應用程式開發者提供了標準化、高效能、低相應時間的音訊開發方案,並實現軟/硬體音訊效能的直接跨平臺部署,被廣泛應用於3D音效、音訊播放、音訊錄製以及音樂體驗增強(低音增強和環境混響)

等方面。對於Android平臺而言,我們可以使用OpenSL ES庫直接在native層處理音訊資料,比如錄製音訊、播放音訊等。OpenSL ES嵌入式裝置中部署的軟/硬體層次結構,如下圖所示:

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);
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_;
typedef const struct SLObjectItf_ * const * SLObjectItf;
struct SLObjectItf_ {
	// 初始化物件,即為物件分配資源
	SLresult (*Realize) (
		SLObjectItf self,
		SLboolean async
	);
	// 恢復物件正常使用,即結束SL_OBJECT_STATE_SUSPENED狀態
	SLresult (*Resume) (
		SLObjectItf self,
		SLboolean async
	);
	// 獲取物件的狀態
	SLresult (*GetState) (
		SLObjectItf self,
		SLuint32 * pState
	);
	// 獲取ID為iid的介面
	SLresult (*GetInterface) (
		SLObjectItf self,
		const SLInterfaceID iid,
		void * pInterface
	);
	// 註冊回撥介面
	SLresult (*RegisterCallback) (
		SLObjectItf self,
		slObjectCallback callback,
		void * pContext
	);
	void (*AbortAsyncOperation) (
		SLObjectItf self
	);
	// 銷燬當前物件,釋放資源
	void (*Destroy) (
		SLObjectItf self
	);
	// 設定優先順序
	SLresult (*SetPriority) (
		SLObjectItf self,
		SLint32 priority,
		SLboolean preemptable
	);
	SLresult (*GetPriority) (
		SLObjectItf self,
		SLint32 *pPriority,
		SLboolean *pPreemptable
	);
	SLresult (*SetLossOfControlInterfaces) (
		SLObjectItf self,
		SLint16 numInterfaces,
		SLInterfaceID * pInterfaceIDs,
		SLboolean enabled
	);
};

注:除了SLEngineItf介面之外,Engine Object還提供了SLEngineCapabilitiesItf和SLAudioIODeviceCapabilitiesItf裝置屬性資訊查詢介面等。
(2) SLEngineItf:引擎介面,Sound Library Engine Interface的縮寫。是Engine Object提供的管理介面,用於建立所有其他的Object物件。SLEngineItf結構體定義如下,位於.../SLES/OpenSLES.h標頭檔案中:

extern SL_API const SLInterfaceID SL_IID_ENGINE;
struct SLEngineItf_;
typedef const struct SLEngineItf_ * const * SLEngineItf;
struct SLEngineItf_ {
	SLresult (*CreateLEDDevice) (
		SLEngineItf self,
		SLObjectItf * pDevice,
		SLuint32 deviceID,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
	SLresult (*CreateVibraDevice) (
		SLEngineItf self,
		SLObjectItf * pDevice,
		SLuint32 deviceID,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
	// 建立播放器物件
	SLresult (*CreateAudioPlayer) (
		SLEngineItf self,        // Enghine Interface
		SLObjectItf * pPlayer,	 // Player Object
		SLDataSource *pAudioSrc, // 音訊資料來源,可為url、assets、pcm
		SLDataSink *pAudioSnk,   // 音訊輸出
		SLuint32 numInterfaces,	 // 要獲取的PlayerObject的介面數量
		const SLInterfaceID * pInterfaceIds, //PlayerObject需要支援的介面ID
		// 指定每個支援的介面是可選的標誌位陣列,如果要求支援的介面沒有實現,建立物件		
        // 時會失敗並返回錯誤碼SL_RESULT_FEATURE_UNSUPPORTED
		const SLboolean * pInterfaceRequired 
	);
	// 建立音訊錄製物件
	SLresult (*CreateAudioRecorder) (
		SLEngineItf self,
		SLObjectItf * pRecorder,
		SLDataSource *pAudioSrc,
		SLDataSink *pAudioSnk,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
	SLresult (*CreateMidiPlayer) (
		SLEngineItf self,
		SLObjectItf * pPlayer,
		SLDataSource *pMIDISrc,
		SLDataSource *pBankSrc,
		SLDataSink *pAudioOutput,
		SLDataSink *pVibra,
		SLDataSink *pLEDArray,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
	SLresult (*CreateListener) (
		SLEngineItf self,
		SLObjectItf * pListener,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
	SLresult (*Create3DGroup) (
		SLEngineItf self,
		SLObjectItf * pGroup,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
	// 建立混音輸出(mix output)物件
	SLresult (*CreateOutputMix) (
		SLEngineItf self,  // Engine Interface
		SLObjectItf * pMix,// OutputMix Object
		SLuint32 numInterfaces, // 要獲取的OutputMix Object介面數量
		const SLInterfaceID * pInterfaceIds, // 物件需要支援的介面ID陣列
		// 指定每個支援的介面是可選的標誌位陣列,如果要求支援的介面沒有實現,建立物件		
        // 時會失敗並返回錯誤碼SL_RESULT_FEATURE_UNSUPPORTED
		const SLboolean * pInterfaceRequired 
	);
	SLresult (*CreateMetadataExtractor) (
		SLEngineItf self,
		SLObjectItf * pMetadataExtractor,
		SLDataSource * pDataSource,
		SLuint32 numInterfaces,
		const SLInterfaceID * pInterfaceIds,
		const SLboolean * pInterfaceRequired
	);
    SLresult (*CreateExtensionObject) (
        SLEngineItf self,
        SLObjectItf * pObject,
        void * pParameters,
        SLuint32 objectID,
        SLuint32 numInterfaces,
        const SLInterfaceID * pInterfaceIds,
        const SLboolean * pInterfaceRequired
    );
	SLresult (*QueryNumSupportedInterfaces) (
		SLEngineItf self,
		SLuint32 objectID,
		SLuint32 * pNumSupportedInterfaces
	);
	SLresult (*QuerySupportedInterfaces) (
		SLEngineItf self,
		SLuint32 objectID,
		SLuint32 index,
		SLInterfaceID * pInterfaceId
	);
    SLresult (*QueryNumSupportedExtensions) (
        SLEngineItf self,
        SLuint32 * pNumExtensions
    );
    SLresult (*QuerySupportedExtension) (
        SLEngineItf self,
        SLuint32 index,
        SLchar * pExtensionName,
        SLint16 * pNameLength
    );
    SLresult (*IsExtensionSupported) (
        SLEngineItf self,
        const SLchar * pExtensionName,
        SLboolean * pSupported
    );
};

(3) SLEnvironmentalReverbItf:環境混響介面。可以理解為指定音訊混響輸出的效果,比如房間效果、劇院效果、禮堂效果等等…。SLEnvironmentalReverbItf結構體定義如下,位於.../SLES/OpenSLES.h標頭檔案中:

// 介面ID
extern SL_API const SLInterfaceID SL_IID_ENVIRONMENTALREVERB;
struct SLEnvironmentalReverbItf_ {
	SLresult (*SetRoomLevel) (
		SLEnvironmentalReverbItf self,
		SLmillibel room
	);
	SLresult (*GetRoomLevel) (
		SLEnvironmentalReverbItf self,
		SLmillibel *pRoom
	);
	...
	// 設定環境混響效果
	SLresult (*SetEnvironmentalReverbProperties) (
		SLEnvironmentalReverbItf self,
		const SLEnvironmentalReverbSettings *pProperties
	);
	SLresult (*GetEnvironmentalReverbProperties) (
		SLEnvironmentalReverbItf self,
		SLEnvironmentalReverbSettings *pProperties
	);
};
// OpenSL ES提供的環境混響效果
// 洞穴效果
#define SL_I3DL2_ENVIRONMENT_PRESET_CAVE \
	{ -1000,    0, 2910, 1300,  -602,  15,  -302,  22, 1000,1000 }
#define SL_I3DL2_ENVIRONMENT_PRESET_ARENA \
	{ -1000, -698, 7240,  330, -1166,  20,    16,  30, 1000,1000 }
#define SL_I3DL2_ENVIRONMENT_PRESET_HANGAR \
	{ -1000,-1000, 10050,  230,  -602,  20,   198,  30, 1000,1000 }
#define SL_I3DL2_ENVIRONMENT_PRESET_CARPETEDHALLWAY \
	{ -1000,-4000,  300,  100, -1831,   2, -1630,  30, 1000,1000 }
// 大廳效果
#define SL_I3DL2_ENVIRONMENT_PRESET_HALLWAY \
	{ -1000, -300, 1490,  590, -1219,   7,   441,  11, 1000,1000 }
// stone走廊效果
#define SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR \
	{ -1000, -237, 2700,  790, -1214,  13,   395,  20, 1000,1000 }
.... // 省略

(4) SLPlayItf:播放介面。用於設定播放狀態,獲取播放參數等,比如呼叫播放介面的setPlayState成員函式講播放狀態設定為SL_PLAYSTATE_PLAYING時,OpenSL SL即開始播放音訊。SLPlayItf結構體定義如下,位於.../SLES/OpenSLES.h標頭檔案中:

// 播放介面ID
extern SL_API const SLInterfaceID SL_IID_PLAY;
struct SLPlayItf_ {
	// 設定播放狀態
	// #define SL_PLAYSTATE_STOPPED	((SLuint32) 0x00000001)
	// #define SL_PLAYSTATE_PAUSED	((SLuint32) 0x00000002)
	// #define SL_PLAYSTATE_PLAYING	((SLuint32) 0x00000003)
	SLresult (*SetPlayState) (
		SLPlayItf self,
		SLuint32 state
	);
	SLresult (*GetPlayState) (
		SLPlayItf self,
		SLuint32 *pState
	);
	// 獲取播放時長
	SLresult (*GetDuration) (
		SLPlayItf self,
		SLmillisecond *pMsec
	);
	// 獲取播放位置
	SLresult (*GetPosition) (
		SLPlayItf self,
		SLmillisecond *pMsec
	);
	... 
};

(5) SLAndroidSimpleBufferQueueItf :緩衝佇列介面。該介面是Android NDK專為Android提供的一個緩衝佇列介面,位於.../SLES/openSLES_Android.h標頭檔案中,它的結構體定義如下:

// 快取佇列介面ID
extern SL_API const SLInterfaceID SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
struct SLAndroidSimpleBufferQueueItf_ {
	// 插入資料到緩衝佇列
	// 當執行完該函式後,會自動回調回調介面處理下一個資料
	SLresult (*Enqueue) (
		SLAndroidSimpleBufferQueueItf self,
		const void *pBuffer,
		SLuint32 size
	);
	// 清空緩衝佇列
	SLresult (*Clear) (
		SLAndroidSimpleBufferQueueItf self
	);
	// 獲取緩衝佇列狀態
	SLresult (*GetState) (
		SLAndroidSimpleBufferQueueItf self,
		SLAndroidSimpleBufferQueueState *pState
	);
	// 註冊回撥介面callback
	SLresult (*RegisterCallback) (
		SLAndroidSimpleBufferQueueItf self,
		slAndroidSimpleBufferQueueCallback callback,
		void* pContext
	);
};

 當然,這裡我們只是介紹了幾個常用的物件和介面,OpenSL ES中還有很多其他的物件及其介面,在以後用到了再作詳細的介紹。

1.2 OpenSL ES使用步驟

 OpenSL ES使用的場景比較多,本文只介紹下使用Audio Player物件播放音訊的場景。首先,建立OpenSL ES引擎物件(Engine Object)和介面(Engine Interface);然後,使用引擎介面SLEngineItf分別建立Audio Player物件和Output Mix物件,前者建立之後與Output mix相關聯用於音訊輸出。輸入以URI作為示例,Output Mix預設與系統相關的預設輸出裝置關聯,示意圖如下:
音訊播放場景

 由於NDK原生庫已經添加了對OpenSL ES庫的支援,因此在Android開發中我們可以非常容易地使用OpenSL ES來處理音訊。修改CmakeList.txt檔案如下所示:

...

# 查詢NDK原生庫OpenSLES
find_library(openSLES-lib openSLES)

# 連結所有庫到avstream
target_link_libraries(
		avstream
        ...
        ${openSLES-lib})
...

OpenSL ES音訊播放步驟:

1.建立OpenSL ES引擎,即初始化Engine Object和Engine Interface

SLObjectItf pEngineObject = NULL;
SLEngineItf pEngineItf = NULL; 
// 建立Engine物件
slCreateEngine(&pEngineObject, 0, NULL, 0, NULL, NULL);  
// 初始化Engine物件
(*pEngineObject)->Realize(pEngineObject, SL_BOOLEAN_FALSE);
// 得到Engine物件的Engine Interface(介面)
(*pEngineObject)->GetInterface(pEngineObject, SL_IID_ENGINE,&pEngineItf); 

2.建立混響輸出物件,指定環境混響效果和音訊輸出

// 建立混響輸出(output mix)物件
SLInterfaceID effect[1] = {SL_IID_ENVIRONMENTALREVERB};    
SLboolean boolValue[1] = {SL_BOOLEAN_FALSE};
(*pEngineItf)->CreateOutputMix(pEngineItf,&pOutputMixObject, 1, effect,
boolValue);
// 初始化混響輸出(output mix)物件
(*pOutputMixObject)->Realize(pOutputMixObject,SL_BOOLEAN_FALSE);
// 得到環境混響介面(Environmental Reverb)
(*pOutputMixObject)->GetInterface(pOutputMixObject,SL_IID_ENVIRONMENTALREVERB,
&outputMixEnvironmentalReverb);
// 指定環境混響效果為STONECORRIDOR
SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;
(*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
outputMixEnvironmentalReverb, &reverbSettings);
// 配置音訊輸出
SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, pOutputMixObject};
SLDataSink audioSnk = {&outputMix, NULL};

其中,SLEnvironmentalReverbSettings和SLDataLocator_OutputMix、SLDataSink是結構體,它們在…/SLES/openSLES.h標頭檔案的定義為:

// 環境混響效果結構體
typedef struct SLEnvironmentalReverbSettings_ {
	SLmillibel    roomLevel;
	SLmillibel    roomHFLevel;
	SLmillisecond decayTime;
	SLpermille    decayHFRatio;
	SLmillibel    reflectionsLevel;
	SLmillisecond reflectionsDelay;
	SLmillibel    reverbLevel;
	SLmillisecond reverbDelay;
	SLpermille    diffusion;
	SLpermille    density;
} SLEnvironmentalReverbSettings;

其中,#define SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR
{ -1000, -237, 2700, 790, -1214, 13, 395, 20, 1000,1000 }

// 混合輸出定位器結構體
typedef struct SLDataLocator_OutputMix {
	SLuint32 		locatorType; // 固定值:SL_DATALOCATOR_OUTPUTMIX
	SLObjectItf		outputMix;   // 混合輸出物件
} SLDataLocator_OutputMix;
// 音訊資料輸出結構體
typedef struct SLDataSink_ {
   void *pLocator; // SLDataLocator_OutputMix引用,即指定混合輸出
   void *pFormat;  // 格式,可為NULL
} SLDataSink;

3.建立播放(Audio Player)物件,獲取該物件的播放介面,以實現播放狀態控制操作

SLDataLocator_AndroidSimpleBufferQueue android_queue =	{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM format_pcm = {
    SL_DATAFORMAT_PCM,     // 播放PCM格式資料
    2,                     // 通道數量,立體聲
    SL_SAMPLINGRATE_44_1, // 取樣率
    SL_PCMSAMPLEFORMAT_FIXED_16, // 取樣深度
    SL_PCMSAMPLEFORMAT_FIXED_16,
    getChannelMask((SLuint32) nbChannels),  // 前作前右
    SL_BYTEORDER_LITTLEENDIAN       		// 結束標誌
};
// 指定資料來源和資料所在位置
SLDataSource pAudioSrc = {&android_queue, &format_pcm};
SLuint32 numInterfaces_audio = 2;
const SLInterfaceID ids_audio[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND};
const SLboolean requireds_audio[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
// 建立Audio Player物件pPlayerObject
SLObjectItf pPlayerObject = NULL;
(*pEngineItf)->CreateAudioPlayer(
    pEngineItf,       // Engine Interface                          
    &pPlayerObject,   // Audio Player Object
    &pAudioSrc,		  // 音訊源為PCM
    &audioSnk,		  // 音訊輸出
    numInterfaces_audio, // 物件所要支援介面數目:2
    ids_audio,			 // 物件所要支援介面的ID
    requireds_audio);	 // 標誌
// 初始化pPlayerObject物件
(*pPlayerObject)->Realize(pPlayerObject,SL_BOOLEAN_FALSE);     
// 獲取pPlayerObject物件的播放介面SLPlayItf
SLPlayItf pPlayerItf = NULL;
(*pPlayerObject)->GetInterface(pPlayerObject, SL_IID_PLAY,&pPlayerItf);      

其中,SLDataLocator_AndroidSimpleBufferQueue和SLDataFormat_PCM、SLDataSource是結構體,它們在…/SLES/openSLES_Android.h和…/SLES/openSLES.h標頭檔案的定義為:

// BufferQueue-based data locator definition
typedef struct SLDataLocator_AndroidSimpleBufferQueue {
	SLuint32	locatorType; // 固定值,SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
	SLuint32	numBuffers;  // 緩衝資料個數
} SLDataLocator_AndroidSimpleBufferQueue;

// PCM資料格式結構體
typedef struct SLDataFormat_PCM_ {
	SLuint32 		formatType;
	SLuint32 		numChannels;
	SLuint32 		samplesPerSec;
	SLuint32 		bitsPerSample;
	SLuint32 		containerSize;
	SLuint32 		channelMask;
	SLuint32		endianness;
} SLDataFormat_PCM;

// 音訊資料來源結構體
typedef struct SLDataSource_ {
	void *pLocator;  // 資料來源定位器
	void *pFormat;   // 資料格式
} SLDataSource;

4.獲取Audio Player物件的緩衝佇列介面,註冊回撥函式

SLAndroidSimpleBufferQueue