王強 洪蕾



摘? 要: 隨著Android終端設(shè)備的普及,基于Android平臺的音頻應(yīng)用大批涌現(xiàn)。對于音頻的處理,Android提供了MediaPlayer來滿足開發(fā)者對音頻的處理,MediaPlayer在音頻采集、解碼和播放,需要將音頻數(shù)據(jù)從Java層拷貝到native層,對系統(tǒng)資源的消耗是巨大的。為了減少數(shù)據(jù)的拷貝,開發(fā)更加高效的Android音頻應(yīng)用,能夠直接在native層處理音頻數(shù)據(jù)顯得尤為重要。本文介紹將FFmpeg與OpenSL ES的數(shù)據(jù)結(jié)構(gòu),在native層使用FFmpeg的解碼過程,及使用OpenSL ES對音頻數(shù)據(jù)的播放處理研究。
關(guān)鍵詞: Android;Ffmpeg;OpenSLES;解碼;音頻播放
中圖分類號: TP311.52? ? 文獻(xiàn)標(biāo)識碼: A? ? DOI:10.3969/j.issn.1003-6970.2020.10.008
本文著錄格式:王強,洪蕾. 基于Android平臺的音頻播放處理研究與實現(xiàn)[J]. 軟件,2020,41(10):3133
【Abstract】: With the popularity of Android terminal devices, a large number of audio applications based on the Android platform have emerged. For audio processing, Android provides MediaPlayer to meet the needs of developers for audio processing. MediaPlayer needs to copy audio data from the Java layer to the native layer for audio acquisition, decoding and playing, which consumes a lot of system resources. In order to reduce the copy of data, to develop more efficient Android audio applications, it is particularly important to be able to directly process audio data in the native layer. This paper introduces the data structure of FFmpeg and OpenSL ES, the decoding process of FFmpeg in the native layer, and the playback processing of audio data using OpenSL ES.
【Key words】: Android; FFmpeg; OpenSLES; Decoded; Audio playback
0? 引言
Android[1-4]終端設(shè)備的普及,讓人們對安卓應(yīng)用的體驗有著越來越高的需求。音頻的處理包含著許多方面,如音樂播放[5],音頻錄制等。手機性能的局限導(dǎo)致對安卓應(yīng)用在控制性能消耗有著高的需求,所以開發(fā)人員在完成音頻應(yīng)用開發(fā)的時候選擇一個合適的方案是必要的。
Android提供了MediaPlayer對音頻進(jìn)行播放處理,而MediaPlayer在處理音頻上對系統(tǒng)資源有著巨大的消耗。采用FFmpeg與OpenSLES,可以讓應(yīng)用層傳遞目標(biāo)音頻的資源地址,使得FFmpeg直接在native層對資源進(jìn)行訪問,解碼音頻,然后將數(shù)據(jù)傳遞給OpenSLES進(jìn)行處理。降低性能消耗。
1? 關(guān)鍵技術(shù)研究
1.1? FFmpeg
FFmpeg[6]是一套用來記錄,轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源程序。提供了轉(zhuǎn)換、錄制與音視頻流化的完整解決方案,不僅包含了編解碼、流化音頻文件的功能,還可以對音頻文件進(jìn)行錄制、轉(zhuǎn)換等工作[7]。
通過對FFmpeg的研究,給出其對音頻數(shù)據(jù)的解碼流程,如圖1所示。
1.2? OpenSL ES
OpenSL ES[8]是針對嵌入式系統(tǒng)調(diào)整的無授權(quán)費,跨平臺,硬件加速的音頻API。它為嵌入式移動多媒體設(shè)備上的應(yīng)用程序開發(fā)人員提供了一套標(biāo)準(zhǔn)化,高性能,低延遲的音頻處理方案,從而實現(xiàn)了硬件和軟件音頻功能的直接跨平臺部署。
OpenSL ES作為音頻開發(fā)的API,它相較于Android提供的Java層API,如MediaPlayer,減少了數(shù)據(jù)在Java層和native層的拷貝,提高效率,并配合FFmpeg,播放解碼轉(zhuǎn)碼后的PCM音頻數(shù)據(jù)。
通過對OpenSL ES的研究,給出其對音頻數(shù)據(jù)的播放流程,如圖2所示。
2? 程序結(jié)構(gòu)與方案設(shè)計
2.1? 程序結(jié)構(gòu)設(shè)計
對于程序的結(jié)構(gòu)設(shè)計,給出其結(jié)構(gòu)圖,如圖3所示。
為了方便開發(fā)者的調(diào)用,在對native層的調(diào)用上封裝了一層代碼,即程序結(jié)構(gòu)圖中的AudioPlayer類,向外提供操作接口,實現(xiàn)了播放,暫停,停止與循環(huán)等功能。
在AudioPlayer類中,使用native關(guān)鍵字聲明與native層交互的函數(shù),并一一對應(yīng)在native-lib類中進(jìn)行實現(xiàn)。通過Java的JNI機制讓Java層與底層C++進(jìn)行交互,即對native-lib類函數(shù)進(jìn)行調(diào)用,具體表現(xiàn)為,向native-lib類傳遞音頻的資源地址,通過對AudioPlayer類提供的音頻控制函數(shù),調(diào)用native-lib類中對應(yīng)的控制方法,對音頻的播放暫停,占用資源釋放與循環(huán)進(jìn)行控制。
在native-lib類中,通過對Decoder類的調(diào)用完成解碼,播放,暫停,資源釋放的功能。
在Decoder類中,首先通過FFmpeg完成對音頻信息的采集,解碼器的初始化與音頻解碼的工作,并將解碼完成的音頻數(shù)據(jù)緩存入SafeQueue類中,再通過調(diào)用Audio類進(jìn)行對音頻的處理工作。
通過Decoder類的調(diào)用,Audio類會進(jìn)行對OpenSL ES的相關(guān)初始化,并等待FFmpeg解碼完成的數(shù)據(jù),與音頻編碼轉(zhuǎn)碼,然后進(jìn)行播放。
2.2? 方案設(shè)計
對于FFmpeg的解碼[9]設(shè)計,給出其結(jié)構(gòu)圖,如圖4所示。
通過FFmpeg進(jìn)行對數(shù)據(jù)源的解碼。這里涉及兩個重要的結(jié)構(gòu)體。AVFormatContext,用于存儲音頻格式中的信息,AVCodecContext,用于存儲音頻解碼器信息[10]。由于解碼是個耗時操作,需要開啟子線程進(jìn)行解碼,由于Android系統(tǒng)是基于Linux內(nèi)核,而Linux又是遵循POSIX線程標(biāo)準(zhǔn)的,所以采用POSIX線程創(chuàng)建子線程。完成解碼需要執(zhí)行以下幾個操作:①FFmpeg通過av_register_all()注冊編解碼器,avformat_network_ init()進(jìn)行網(wǎng)絡(luò)初始化,以便FFmpeg可以直接訪問網(wǎng)絡(luò)地址。②通過avformat_open_input(),打開輸入文件流,讀取數(shù)據(jù)并判斷文件編碼格式,將格式信息存入AVFormatContext結(jié)構(gòu)體中。③通過avformat_find_ stream_info(),獲得文件的編碼信息,將信息存入AVFormatContext結(jié)構(gòu)體中。④遍歷AVFormatContext結(jié)構(gòu)體中的數(shù)據(jù)流,根據(jù)類型判斷,找到音頻流,保存音頻流的索引,并保存至音頻類對象Audio中。⑤通過avcodec_find_decoder()函數(shù)與AVFormatContext中保存的文件編碼格式找到相應(yīng)的解碼器⑥通過avcodec_parameters_to_context(),將音頻流信息保存至AVCodecContext結(jié)構(gòu)體中⑦通過avcodec_open2()打開解碼器。⑧通過av_read_frame()讀取原始音頻數(shù)據(jù)幀AVPacket,如果讀取失敗,則回調(diào)AudioPlayer類,通知調(diào)用者,反之,如果成功,則將數(shù)據(jù)存入SafeQueue類的幀隊列中。由于解碼的速度往往遠(yuǎn)大于音頻播放的速度,所以需要對解碼完成后的數(shù)據(jù)進(jìn)行緩存,先解碼好的先播放,利用隊列這個數(shù)據(jù)結(jié)構(gòu)。實現(xiàn)隊列的存、取、獲取隊列長度與清空隊列操作。因為音頻數(shù)據(jù)是邊解碼邊播放的,在對數(shù)據(jù)的存與取時可能會產(chǎn)生沖突,所以對于隊列的存與取需要進(jìn)行同步操作,這里通過POSIX線程,進(jìn)行加鎖,實現(xiàn)對隊列的同步。
由于利用OpenSL ES進(jìn)行播放,在播放之前需要對OpenSL ES進(jìn)行初始化,在循環(huán)解碼原始數(shù)據(jù)幀的同時,進(jìn)行OpenSL ES初始化,并啟用回調(diào)函數(shù)。
對于播放的配置回調(diào)給出具體流程圖,如圖5所示。
通過接口對象的創(chuàng)建,設(shè)置播放數(shù)據(jù)類型為PCM數(shù)據(jù),16位量化位數(shù),雙聲道,立體聲與采樣率,播放狀態(tài)為播放,并設(shè)置播放回調(diào),監(jiān)測數(shù)據(jù)的傳遞。
由于音頻編碼格式多樣,需要對原始音頻幀AVPacket進(jìn)行重采樣,生成PCM數(shù)據(jù),采樣標(biāo)準(zhǔn)為,每秒采樣音頻個數(shù)44100 HZ,采樣位數(shù)16 bit,輸出聲道為雙聲道。
給出每個采樣點數(shù)據(jù)大小的計算公式:
size(數(shù)據(jù)長度) = 采樣個數(shù) * 聲道數(shù) * 單個采樣點大小
對于重采樣并實現(xiàn)播放的具體流程圖,如圖6所示。
從SafeQueue類的緩沖隊列中通過popAVPacket()函數(shù)獲得原始音頻數(shù)據(jù)AVPacket,通過avcodec_ send_packet()函數(shù)進(jìn)行解封裝,得到音頻幀,并將其保存在AVCodecContext中,通過avcodec_receive_frame()函數(shù)獲得音頻幀,利用swr_alloc_set_opts()函數(shù)設(shè)置轉(zhuǎn)碼后的PCM音頻數(shù)據(jù)參數(shù),最后通過swr_convert()函數(shù)從音頻幀中獲得轉(zhuǎn)碼后的一幀PCM數(shù)據(jù),這時OpenSL ES監(jiān)測到回調(diào),將PCM數(shù)據(jù)通過Enqueue()函數(shù)加入播放隊列,完成播放。
3? 對比MediaPlayer
3.1? 音頻數(shù)據(jù)加載時間對比
MediaPlayer的加載時間,與本文方案(AudioPlayer)加載時間(單位:秒)對比如表1所示。
經(jīng)過試驗測算,在對同一資源地址進(jìn)行播放時,AudioPlayer所需要的時間約為0.2 S,MediaPlayer所需的時間約為0.45 s。
3.2? 播放音頻時內(nèi)存增量對比
MediaPlayer與本文方案(AudioPlayer)的播放音頻時內(nèi)存的增量對比如圖7所示。
經(jīng)過試驗測算,在對同一資源地址進(jìn)行播放時,AudioPlayer所占用的內(nèi)存均值約為2.5 MB,MediaPlayer所占用的內(nèi)容存均值約為4.6 MB。
4? 結(jié)論
采用FFmpeg與OpenSL ES實現(xiàn)了對音頻數(shù)據(jù)的播放處理,提供了一個較好的解決方案。相比與MediaPlayer不僅提高了數(shù)據(jù)的加載效率,還減少了Java層和native層之間的數(shù)據(jù)拷貝,符合了手機性能的需求,提升了用戶的體驗性。
參考文獻(xiàn)
[1]王翠香, 邵星. 基于安卓的大學(xué)生掌上論壇系統(tǒng)設(shè)計[J]. 軟件, 2015, 36(10): 33-35.
[2]何艷江, 呂鵬, 顏溯, 等. 基于安卓平臺的復(fù)合地基處理軟件開發(fā)[J]. 軟件, 2015, 36(12): 42-44.
[3]姚永明, 梅雨凱, 章香, 等. 基于安卓的南郵通達(dá)掌上校園 APP 的需求分析[J]. 軟件, 2018, 39(8): 45-47.
[4]姚永明, 梅雨凱, 章香, 等. 基于安卓的南郵通達(dá)掌上校園 APP 的實現(xiàn)[J]. 軟件, 2018, 39(8): 48-51.
[5]張小琴, 張庚. 基于 Android 平臺的音樂播放器設(shè)計與實現(xiàn)[J]. 軟件, 2018, 39(9): 113-116.
[6]鄧正良. 基于FFmpeg和SDL的視頻流播放存儲研究綜述[J]. 現(xiàn)代計算機, 2019(22): 47-50.
[7]石佩青. 基于Android系統(tǒng)在線音樂播放器的設(shè)計與實現(xiàn)[D]. 北京郵電大學(xué), 2017.
[8]張希龍. 基于Android平臺的助聽器系統(tǒng)設(shè)計和實現(xiàn)[D]. 東南大學(xué), 2017.
[9]羅瀟. Android多媒體平臺下基于FFMPEG的音視頻處理方案研究[D]. 暨南大學(xué), 2016.
[10]Research and Implementation of Video Codec Based on FFmpeg. ZENG Hao, ZHANG Zhi-yong, SHI Lul-in. 2016 International Conference on Network and Information Systems for Computers. 2016.