日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

【Android FFMPEG 开发】OpenSLES 播放音频 ( 创建引擎 | 输出混音设置 | 配置输入输出 | 创建播放器 | 获取播放/队列接口 | 回调函数 | 开始播放 | 激活回调 )

發(fā)布時間:2025/6/17 Android 31 豆豆

文章目錄

        • I . FFMPEG 播放視頻流程
        • II . OpenSLES 播放音頻流程
        • III . OpenSLES 播放參考 Google 官方示例
        • IV . OpenSL ES 播放代碼 ( 詳細注釋 )
        • IV . OpenSLES slAndroidSimpleBufferQueueCallback 回調(diào)函數(shù)聲明及實現(xiàn)代碼



I . FFMPEG 播放視頻流程



FFMPEG 播放視頻流程 : 視頻中包含圖像和音頻 ;


① FFMPEG 初始化 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 初始化 ( 網(wǎng)絡(luò)初始化 | 打開音視頻 | 查找音視頻流 )

② FFMPEG 獲取 AVStream 音視頻流 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 獲取 AVStream 音視頻流 ( AVFormatContext 結(jié)構(gòu)體 | 獲取音視頻流信息 | 獲取音視頻流個數(shù) | 獲取音視頻流 )

③ FFMPEG 獲取 AVCodec 編解碼器 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 獲取編解碼器 ( 獲取編解碼參數(shù) | 查找編解碼器 | 獲取編解碼器上下文 | 設(shè)置上下文參數(shù) | 打開編解碼器 )

④ FFMPEG 讀取音視頻流中的數(shù)據(jù)到 AVPacket : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 讀取音視頻流中的數(shù)據(jù)到 AVPacket ( 初始化 AVPacket 數(shù)據(jù) | 讀取 AVPacket )

⑤ FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame ( 音頻 / 視頻數(shù)據(jù)解碼 ) : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 解碼 AVPacket 數(shù)據(jù)到 AVFrame ( AVPacket->解碼器 | 初始化 AVFrame | 解碼為 AVFrame 數(shù)據(jù) )

⑥ FFMPEG AVFrame 圖像格式轉(zhuǎn)換 YUV -> RGBA : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG AVFrame 圖像格式轉(zhuǎn)換 YUV -> RGBA ( 獲取 SwsContext | 初始化圖像數(shù)據(jù)存儲內(nèi)存 | 圖像格式轉(zhuǎn)換 )

⑦ FFMPEG ANativeWindow 原生繪制 準備 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG ANativeWindow 原生繪制 ( Java 層獲取 Surface | 傳遞畫布到本地 | 創(chuàng)建 ANativeWindow )

⑧ FFMPEG ANativeWindow 原生繪制 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG ANativeWindow 原生繪制 ( 設(shè)置 ANativeWindow 緩沖區(qū)屬性 | 獲取繪制緩沖區(qū) | 填充數(shù)據(jù)到緩沖區(qū) | 啟動繪制 )

⑨ FFMPEG 音頻重采樣 : 參考博客 【Android FFMPEG 開發(fā)】FFMPEG 音頻重采樣 ( 初始化音頻重采樣上下文 SwrContext | 計算音頻延遲 | 計算輸出樣本個數(shù) | 音頻重采樣 swr_convert )



II . OpenSLES 播放音頻流程



OpenSLES 播放音頻流程 :


〇 視頻播放操作 : FFMPEG 環(huán)境初始化 , 獲取 AVStream 音視頻流 , 獲取 AVCodec 編解碼器 , 讀取音視頻流中的數(shù)據(jù)到 AVPacket , 解碼 AVPacket 數(shù)據(jù)到 AVFrame , AVFrame 圖像格式轉(zhuǎn)換 YUV -> RGBA , ANativeWindow 原生繪制 ;


〇 音頻播放操作 : FFMPEG 環(huán)境初始化 , 獲取 AVStream 音視頻流 , 獲取 AVCodec 編解碼器 , 讀取音視頻流中的數(shù)據(jù)到 AVPacket , 解碼 AVPacket 數(shù)據(jù)到 AVFrame , 音頻重采樣 , 然后使用 OpenSLES 播放重采樣后的音頻 ;


① 創(chuàng)建引擎 : 先創(chuàng)建引擎對象 , 再實現(xiàn)引擎對象 , 最后從引擎對象中 , 獲取引擎接口 ;

SLresult result;// 創(chuàng)建引擎 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);// 實現(xiàn)引擎 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);// 獲取引擎接口 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

② 設(shè)置輸出混音器 : 創(chuàng)建輸出混音器對象 , 實現(xiàn)輸出混音器 ;

// 創(chuàng)建輸出混音器對象 , 可以指定一個混響效果參數(shù) ( 該混淆參數(shù)可選 ) const SLInterfaceID ids_engine[1] = {SL_IID_ENVIRONMENTALREVERB}; const SLboolean req_engine[1] = {SL_BOOLEAN_FALSE}; result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids_engine, req_engine);// 實現(xiàn)輸出混音器 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);

③ 獲取混響接口并設(shè)置混響 : 該步驟不是必須操作 , 另外獲取混響接口可能失敗 ;

// 獲取混響接口 result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb); // 設(shè)置混響 if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result; }

④ 配置音源輸入 : 配置音頻數(shù)據(jù)源緩沖隊列 , 和 音源格式 ( 采樣率 , 樣本位數(shù) , 通道數(shù) , 樣本大小端格式 ) ;

//1 . 配置音源輸入// 配置要播放的音頻輸入緩沖隊列屬性參數(shù) , 緩沖區(qū)大小 , 音頻格式 , 采樣率 , 樣本位數(shù) , 通道數(shù) , 樣本大小端格式 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};// PCM 格式 SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, //PCM 格式2, //兩個聲道SL_SAMPLINGRATE_44_1, //采樣率 44100 HzSL_PCMSAMPLEFORMAT_FIXED_16, //采樣位數(shù) 16位SL_PCMSAMPLEFORMAT_FIXED_16, //容器為 16 位SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, //左右雙聲道SL_BYTEORDER_LITTLEENDIAN}; //小端格式// 設(shè)置音頻數(shù)據(jù)源 , 配置緩沖區(qū) ( loc_bufq ) 與 音頻格式 (format_pcm) SLDataSource audioSrc = {&loc_bufq, &format_pcm};

⑤ 配置音頻輸出 : 裝載輸出混音器對象 到 SLDataLocator_OutputMix , 在將 SLDataLocator_OutputMix 結(jié)構(gòu)體裝載到 SLDataSink 中 ;

// 配置混音器 : 將 outputMixObject 混音器對象裝載入 SLDataLocator_OutputMix 結(jié)構(gòu)體中 SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; // 將 SLDataLocator_OutputMix 結(jié)構(gòu)體裝載到 SLDataSink 中 // 音頻輸出通過 loc_outmix 輸出 , 實際上是通過 outputMixObject 混音器對象輸出的 SLDataSink audioSnk = {&loc_outmix, NULL};

⑥ 創(chuàng)建并實現(xiàn)播放器 : 先使用 引擎 , 音源輸入 , 音頻輸出 , 采樣率 , 接口隊列ID 等參數(shù)創(chuàng)建播放器 , 再實現(xiàn)播放器對象 ;

// 操作隊列接口 , 如果需要 特效接口 , 添加 SL_IID_EFFECTSEND const SLInterfaceID ids_player[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_EFFECTSEND,/*SL_IID_MUTESOLO,*/}; const SLboolean req_player[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,/*SL_BOOLEAN_TRUE,*/ };// 創(chuàng)建播放器 result = (*engineEngine)->CreateAudioPlayer(engineEngine,&bqPlayerObject,&audioSrc, //音頻輸入&audioSnk, //音頻商戶處bqPlayerSampleRate? 2 : 3,//ids_player,req_player);// 創(chuàng)建播放器對象 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);

⑦ 獲取播放器接口 和 緩沖隊列接口 : 獲取的接口 對應(yīng) 播放器創(chuàng)建時的接口 ID 數(shù)組參數(shù) ;

// 獲取播放器 Player 接口 : 該接口用于設(shè)置播放器狀態(tài) , 開始 暫停 停止 播放 等操作 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);// 獲取播放器 緩沖隊列 接口 : 該接口用于控制 音頻 緩沖區(qū)數(shù)據(jù) 播放 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,&bqPlayerBufferQueue);

⑧ 注冊回調(diào)函數(shù) : 按照指定的回調(diào)函數(shù)類型 , 聲明并實現(xiàn)該回調(diào)函數(shù) , 并將該回調(diào)函數(shù)注冊給播放器緩沖隊列接口 ;

// 注冊緩沖區(qū)隊列的回調(diào)函數(shù) , 每次播放完數(shù)據(jù)后 , 會自動回調(diào)該函數(shù) // 傳入?yún)?shù) this , 就是 bqPlayerCallback 函數(shù)中的 context 參數(shù) result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);

回調(diào)函數(shù)類型 :

typedef void (SLAPIENTRY *slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller,void *pContext );

回調(diào)函數(shù)實現(xiàn) :

//每當(dāng)緩沖數(shù)據(jù)播放完畢后 , 會自動回調(diào)該回調(diào)函數(shù) void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {...//通過播放器隊列接口 , 將 PCM 數(shù)據(jù)加入到該隊列緩沖區(qū)后 , 就會自動播放這段音頻(*bq)->Enqueue(bq, audioChannel->data, data_size);}

⑨ 獲取效果器接口 和 音量控制接口 : 這兩個接口不是必須的 , 可選選項 ;

// 獲取效果器發(fā)送接口 ( get the effect send interface )bqPlayerEffectSend = NULL;if( 0 == bqPlayerSampleRate) {result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,&bqPlayerEffectSend);}// 獲取音量控制接口 ( get the volume interface ) [ 如果需要調(diào)節(jié)音量可以獲取該接口 ]result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);

⑩ 設(shè)置播放狀態(tài) : 設(shè)置播放狀態(tài)為 SL_PLAYSTATE_PLAYING ;

// 設(shè)置播放器正在播放狀態(tài) ( set the player's state to playing ) result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);

? 手動調(diào)用激活回調(diào)函數(shù) : 第一次激活回調(diào)函數(shù)調(diào)用 , 需要手動激活 ;

// 手動激活 , 手動調(diào)用一次 bqPlayerCallback 回調(diào)函數(shù) bqPlayerCallback(bqPlayerBufferQueue, this);

III . OpenSLES 播放參考 Google 官方示例



1 . Google 官方示例 : 關(guān)于 OpenSL ES 音頻播放 , 在 Google 的官方示例 native-audio 中 , 有現(xiàn)成的代碼可供使用 ;


① Google 官方示例 參考地址 : native-audio

② OpenSL ES 播放代碼 : native-audio-jni.c


2 . FFMPEG 播放 : 在 FFMPEG 中可以原封不動的拷貝 native-audio 項目中的關(guān)于 OpenSL ES 播放相關(guān)的代碼 , 但是在 slAndroidSimpleBufferQueueCallback 回調(diào)函數(shù)中播放的音頻 , 是 FFMPEG 中音頻從 AVPacket 解碼成的 AVFrame 重采樣后的音頻 , 關(guān)于音頻重采樣參考 【Android FFMPEG 開發(fā)】FFMPEG 音頻重采樣 ( 初始化音頻重采樣上下文 SwrContext | 計算音頻延遲 | 計算輸出樣本個數(shù) | 音頻重采樣 swr_convert ) ;



IV . OpenSL ES 播放代碼 ( 詳細注釋 )



OpenSL ES 播放部分的代碼 : 細節(jié)內(nèi)容看注釋吧 , 不再展開一條一條的寫了 ;

// I . 創(chuàng)建 OpenSLES 引擎并獲取引擎的接口 ( 相關(guān)代碼拷貝自 Google 官方示例 native-audio )// 參考 : https://github.com/android/ndk-samples/blob/master/native-audio/app/src/main/cpp/native-audio-jni.c//聲明每個方法執(zhí)行的返回結(jié)果 , 一般情況下返回 SL_RESULT_SUCCESS 即執(zhí)行成功// 該類型本質(zhì)是 int 類型 , 定義的是各種類型的異常SLresult result;// 創(chuàng)建引擎result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);// 返回 0 成功 , 否則失敗 , 一旦失敗就中斷退出assert(SL_RESULT_SUCCESS == result);(void)result;// 實現(xiàn)引擎result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void)result;// 獲取引擎接口 , 使用該接口創(chuàng)建輸出混音器 , 音頻播放器等其它對象// 引擎對象不提供任何調(diào)用的方法 , 引擎調(diào)用的方法都定義在接口中result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);assert(SL_RESULT_SUCCESS == result);(void)result;// II . 設(shè)置輸出混音器// 輸出聲音 , 添加各種音效 ( 混響 , 重低音 , 環(huán)繞音 , 均衡器 等 ) , 都要通過混音器實現(xiàn) ;// 創(chuàng)建輸出混音器對象 , 可以指定一個混響效果參數(shù) ( 該混淆參數(shù)可選 )const SLInterfaceID ids_engine[1] = {SL_IID_ENVIRONMENTALREVERB};const SLboolean req_engine[1] = {SL_BOOLEAN_FALSE};result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids_engine, req_engine);assert(SL_RESULT_SUCCESS == result);(void)result;// 實現(xiàn)輸出混音器result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void)result;// III . 獲取混響接口 并 設(shè)置混響 ( 可能會失敗 )// 獲取環(huán)境混響接口// 如果環(huán)境混響效果不可用 , 該操作可能失敗// either because the feature is not present, excessive CPU load, or// the required MODIFY_AUDIO_SETTINGS permission was not requested and grantedresult = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,&outputMixEnvironmentalReverb);if (SL_RESULT_SUCCESS == result) {result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(outputMixEnvironmentalReverb, &reverbSettings);(void)result;}//IV . 配置音源輸入// 配置要播放的音頻輸入緩沖隊列屬性參數(shù) , 緩沖區(qū)大小 , 音頻格式 , 采樣率 , 樣本位數(shù) , 通道數(shù) , 樣本大小端格式SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};/*typedef struct SLDataFormat_PCM_ {SLuint32 formatType; //數(shù)據(jù)格式 SL_DATAFORMAT_PCMSLuint32 numChannels; //通道數(shù) , 左右聲道 2個 2SLuint32 samplesPerSec; //采樣率 44100Hz SL_SAMPLINGRATE_44_1SLuint32 bitsPerSample; //采樣位數(shù) 16位 SL_PCMSAMPLEFORMAT_FIXED_16SLuint32 containerSize; //容器大小 SL_PCMSAMPLEFORMAT_FIXED_16SLuint32 channelMask; //通道 SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHTSLuint32 endianness; //小端格式 SL_BYTEORDER_LITTLEENDIAN} SLDataFormat_PCM;*/SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, //PCM 格式2, //兩個聲道SL_SAMPLINGRATE_44_1, //采樣率 44100 HzSL_PCMSAMPLEFORMAT_FIXED_16, //采樣位數(shù) 16位SL_PCMSAMPLEFORMAT_FIXED_16, //容器為 16 位SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, //左右雙聲道SL_BYTEORDER_LITTLEENDIAN}; //小端格式// 設(shè)置音頻數(shù)據(jù)源 , 配置緩沖區(qū) ( loc_bufq ) 與 音頻格式 (format_pcm)SLDataSource audioSrc = {&loc_bufq, &format_pcm};// V . 配置音頻輸出// 配置混音器 : 將 outputMixObject 混音器對象裝載入 SLDataLocator_OutputMix 結(jié)構(gòu)體中SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};// 將 SLDataLocator_OutputMix 結(jié)構(gòu)體裝載到 SLDataSink 中// 音頻輸出通過 loc_outmix 輸出 , 實際上是通過 outputMixObject 混音器對象輸出的SLDataSink audioSnk = {&loc_outmix, NULL};// VI . 創(chuàng)建并實現(xiàn)播放器/** 創(chuàng)建音頻播放器:* 如果需要效果器時 , 不支持高性能音頻* ( fast audio does not support when SL_IID_EFFECTSEND is required, skip it* for fast audio case )*/// 操作隊列接口 , 如果需要 特效接口 , 添加 SL_IID_EFFECTSENDconst SLInterfaceID ids_player[3] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_EFFECTSEND,/*SL_IID_MUTESOLO,*/};const SLboolean req_player[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,/*SL_BOOLEAN_TRUE,*/ };// 創(chuàng)建播放器result = (*engineEngine)->CreateAudioPlayer(engineEngine,&bqPlayerObject,&audioSrc, //音頻輸入&audioSnk, //音頻商戶處bqPlayerSampleRate? 2 : 3,//ids_player,req_player);assert(SL_RESULT_SUCCESS == result);(void)result;// 創(chuàng)建播放器對象result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);assert(SL_RESULT_SUCCESS == result);(void)result;// VII . 獲取播放器接口 和 緩沖隊列接口// 獲取播放器 Player 接口 : 該接口用于設(shè)置播放器狀態(tài) , 開始 暫停 停止 播放 等操作result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);assert(SL_RESULT_SUCCESS == result);(void)result;// 獲取播放器 緩沖隊列 接口 : 該接口用于控制 音頻 緩沖區(qū)數(shù)據(jù) 播放result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,&bqPlayerBufferQueue);assert(SL_RESULT_SUCCESS == result);(void)result;// VIII . 注冊回調(diào)函數(shù)// 注冊緩沖區(qū)隊列的回調(diào)函數(shù) , 每次播放完數(shù)據(jù)后 , 會自動回調(diào)該函數(shù)// 傳入?yún)?shù) this , 就是 bqPlayerCallback 函數(shù)中的 context 參數(shù)result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);assert(SL_RESULT_SUCCESS == result);(void)result;// IX . 獲取效果器接口 和 音量控制接口 ( 不是必須的 )// 獲取效果器發(fā)送接口 ( get the effect send interface )bqPlayerEffectSend = NULL;if( 0 == bqPlayerSampleRate) {result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND,&bqPlayerEffectSend);assert(SL_RESULT_SUCCESS == result);(void)result;}#if 0 // mute/solo is not supported for sources that are known to be mono, as this is// get the mute/solo interfaceresult = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);assert(SL_RESULT_SUCCESS == result);(void)result; #endif// 獲取音量控制接口// 獲取音量控制接口 ( get the volume interface ) [ 如果需要調(diào)節(jié)音量可以獲取該接口 ]result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);assert(SL_RESULT_SUCCESS == result);(void)result;// X . 設(shè)置播放狀態(tài)// 設(shè)置播放器正在播放狀態(tài) ( set the player's state to playing )result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);assert(SL_RESULT_SUCCESS == result);(void)result;// XI. 手動調(diào)用激活回調(diào)函數(shù)// 手動激活 , 手動調(diào)用一次 bqPlayerCallback 回調(diào)函數(shù)bqPlayerCallback(bqPlayerBufferQueue, this);

IV . OpenSLES slAndroidSimpleBufferQueueCallback 回調(diào)函數(shù)聲明及實現(xiàn)代碼



1 . 回調(diào)函數(shù)原型 :

typedef void (SLAPIENTRY *slAndroidSimpleBufferQueueCallback)(SLAndroidSimpleBufferQueueItf caller,void *pContext );

2 . 回調(diào)函數(shù)聲明及實現(xiàn) :

//每當(dāng)緩沖數(shù)據(jù)播放完畢后 , 會自動回調(diào)該回調(diào)函數(shù) // this callback handler is called every time a buffer finishes playing void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {//獲取 PCM 采樣數(shù)據(jù) , 將重采樣的數(shù)據(jù)放到 data 中int data_size ; //進行 FFMPEG 音頻重采樣 ... 大塊代碼參考上一篇博客 //開始播放if ( data_size > 0 ){//通過播放器隊列接口 , 將 PCM 數(shù)據(jù)加入到該隊列緩沖區(qū)后 , 就會自動播放這段音頻// 注意 , 最后一個參數(shù)是樣本字節(jié)數(shù)(*bq)->Enqueue(bq, audioChannel->data, data_size);}}

3 . 回調(diào)函數(shù)注冊 :

// VIII . 注冊回調(diào)函數(shù)// 注冊緩沖區(qū)隊列的回調(diào)函數(shù) , 每次播放完數(shù)據(jù)后 , 會自動回調(diào)該函數(shù)// 傳入?yún)?shù) this , 就是 bqPlayerCallback 函數(shù)中的 context 參數(shù)result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this);assert(SL_RESULT_SUCCESS == result);(void)result;

總結(jié)

以上是生活随笔為你收集整理的【Android FFMPEG 开发】OpenSLES 播放音频 ( 创建引擎 | 输出混音设置 | 配置输入输出 | 创建播放器 | 获取播放/队列接口 | 回调函数 | 开始播放 | 激活回调 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。