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音效、音訊播放、音訊錄製以及音樂體驗增強(低音增強和環境混響)
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