在開發(fā)上,習(xí)慣的將音頻、視頻功能的使用稱之為多媒體,實際上如果講的寬泛一些的話,相機的使用,比如拍照,錄制視頻等,也可以劃分到多媒體的范疇里面。
從本節(jié)課開始,我們就來看看Android中多媒體的API使用和具體的功能。
本篇文章我們先從音頻開發(fā)聊起。
零、音頻開發(fā)場景、內(nèi)容和基本概念在說音頻開發(fā)之前,我們可以先想一想自己琢磨一下,哪些應(yīng)用場景會用到音頻開發(fā)。主要的應(yīng)用場景大致包括:
音頻播放器錄音機語音***音視頻監(jiān)控應(yīng)用音視頻直播應(yīng)用音頻編輯/處理軟件藍(lán)牙耳機/音箱……如果我們要成系統(tǒng)的學(xué)習(xí)多媒體和音視頻的開發(fā),大致會有涉及到哪些方面的知識呢,歸納來看主要有一下幾個方面的內(nèi)容:
音頻采集/播放:已有音頻如何播放;如何采集一段音頻;音頻算法處理:主要包括去噪、靜音檢測、回聲消除、音效處理、功放/增強、混音/分離,等等音頻的編解碼和格式轉(zhuǎn)換:不同格式之間的轉(zhuǎn)碼操作音頻傳輸協(xié)議的開發(fā):主要包括SIP,A2DP、AVRCP,等等另外,如果要進行音頻開發(fā),需要了解一些音頻的概念作為前置知識,一些常見的概念如下所示:
SampleRate:采樣率,每秒采集聲音的數(shù)量,它用赫茲(Hz)來表示。采樣頻率越高,音頻質(zhì)量越好。常用的音頻采樣頻率有:8kHz、16kHz、44.1kHz、48kHz等。Channel:聲道數(shù),表示聲音錄制時的音源數(shù)量或回放時相應(yīng)的揚聲器數(shù)量。常用的是單聲道(Mono)和雙聲道(Stereo)。要記住這兩個詞:Stereo和Mono。BitDepth:采樣精度,每個采樣點用多少數(shù)據(jù)量表示,它以位(Bit)為單位。位數(shù)越多,表示得就越精細(xì),聲音質(zhì)量自然就越好,當(dāng)然數(shù)據(jù)量也越大。常見的位寬是:8bit或者16bit。BitRate:比特率,每秒音頻占用的比特數(shù)量,單位是bps(BitPerSecond),比特率越高,壓縮比越小,聲音質(zhì)量越好,音頻體積也越大。一、音頻播放說到音視頻多媒體,首先就有一個概念叫:媒體格式。也就是我們常說的不同格式的音視頻文件。在Android這個***系統(tǒng)平臺中,支持的媒體格式還是很豐富的,詳細(xì)內(nèi)容如下:
音頻格式和編解碼器總結(jié)來說,Android中常見的音頻壓縮格式有:MP3,AAC,OGG,WMA,Opus,F(xiàn)LAC,APE,m4a,AMR,等等。
1.1音頻的播放1.1.1MediaPlayer首先認(rèn)識兩個基礎(chǔ)的概念和API:
MediaPlayer:用于播放聲音和視頻的主要API。Android多媒體框架支持播放各種常見媒體類型,可以輕松地將音頻、視頻和圖片集成到應(yīng)用中??梢允褂肕ediaPlayerAPI,播放存儲在應(yīng)用資源(原始資源)內(nèi)的媒體文件、文件系統(tǒng)中的獨立文件或者通過網(wǎng)絡(luò)連接獲得的數(shù)據(jù)流中的音頻或視頻。AudioManager:該類API用于管理設(shè)備上的音頻源和音頻輸出。另外需要說一下,MediaPlayerl除了能夠獲取、解碼以及播放音頻和視頻,而且只需很簡單的設(shè)置即可以外。它還支持多種不同的媒體源,比如:
本地資源:即res目錄下的音頻資源。URI:比如可能是通過ContentProvider解析到的某個資源URI網(wǎng)絡(luò):通過網(wǎng)絡(luò),獲取流式傳輸數(shù)據(jù)進行播放。使用步驟1、初始化MediaPlayer對象2、準(zhǔn)備播放工作:準(zhǔn)備工作主要是音頻數(shù)據(jù)源的獲取或者是音頻數(shù)據(jù)的解碼操作等,該過程屬于耗時操作,因此需要在工作線程中進行。3、音頻狀態(tài)管理:在準(zhǔn)備工作過后,可以對音頻進行播放、暫停等操作。同時需要注意的是MediaPlayer是有狀態(tài)的,包括:Idle、Initialized、Prepared、Started、Paused、PlaybackCompleted等狀態(tài)。當(dāng)在進行狀態(tài)的切換時,需要注意幾個點:①Started(開始)/Paused(暫停)到Stopped(停止)是單向轉(zhuǎn)換,無法再從Stopped直接轉(zhuǎn)換到Started,需要經(jīng)歷Prepared重新裝載才可以重新播放。②Initialized(初始化)狀態(tài)需要裝載數(shù)據(jù)才可以進行start()播放,但是如果使用prepareAsync()***異步準(zhǔn)備,需要等待準(zhǔn)備完成再開始播放,這里需要使用一個回調(diào)***:setOnPreparedListener(),它會在異步裝載完成后調(diào)用。③End(結(jié)束)狀態(tài)是游離在其他狀態(tài)之外的,在任何狀態(tài)皆可切換,一般在不需要繼續(xù)使用MediaPlayer的時候,才會使用release()回收資源。④Error(錯誤)狀態(tài)是游離在其他狀態(tài)之外的,只有在MediaPlayer發(fā)生錯誤的時候才會轉(zhuǎn)換。為了保持應(yīng)用的用戶體驗,通常會監(jiān)聽setOnErrorListener()回調(diào)***,它會在MediaPlayer發(fā)生錯誤的時候被回調(diào)。注意事項1、使用Service播放音頻。在使用MediaPlayer播放音頻流時,推薦使用一個Service來承載MediaPlayer,而不是直接在Activity里使用。2、使用喚醒鎖。Android系統(tǒng)的功耗設(shè)計里,為了節(jié)約電池消耗,如果設(shè)備處于睡眠狀態(tài),系統(tǒng)將試圖降低或者關(guān)閉一些沒設(shè)備必須的特性,包括CPU和Wifi硬件。如果是一個后臺播放音樂的應(yīng)用,降低CPU可能導(dǎo)致在后臺運行的時候干擾音頻的正常播放,關(guān)閉Wifi將可能導(dǎo)致網(wǎng)絡(luò)音頻流的獲取出現(xiàn)錯誤。因此為了保證功能的正常使用,我們必須阻止系統(tǒng)關(guān)閉服務(wù)??梢允褂脀akelocks(喚醒鎖),它會告訴系統(tǒng)你正在使用某些功能,這樣就可以一直保持該功能處于喚醒狀態(tài),即使鎖屏無操作也能繼續(xù)使用。這個鎖會在paused和stoped狀態(tài)下釋放。1.1.2SoundPool如果應(yīng)用程序經(jīng)常播放密集、急促而又短暫的音效(如游戲音效)那么使用MediaPlayer顯得有些不太適合了。因為MediaPlayer存在如下缺點:
1、延時時間較長,且資源占用率高。2、不支持多個音頻同時播放。Android中除了MediaPlayer播放音頻之外還提供了SoundPool來播放音效,SoundPool使用音效池的概念來管理多個短促的音效,例如它可以開始就加載20個音效,以后在程序中按音效的ID進行播放。SoundPool的特點和使用長江如下:
1、主要用于播放一些較短的聲音片段。2、SoundPool對CPU資源占用量低和反應(yīng)延遲小。3、SoundPool還支持自行設(shè)置聲音的品質(zhì)、音量、播放比率等參數(shù)。SoundPool的API說明如下:
1、SoundPool(intmaxStreams,intstreamType,intsrcQuality):指定它總共支持多少個聲音(也就是池的大?。?、聲音的品質(zhì)。該***屬于5.0以下版本使用。2、SoundPool.Builder:從5.0版本開始使用的是SoundPool.Builder模式。3、load(Contextcontext,intresld,intpriority):從resld所對應(yīng)的資源加載聲音。4、load(FileDescriptorfd,longoffset,longlength,intpriority):加載fd所對應(yīng)的文件的offset開始、長度為length的聲音。5、load(AssetFileDescriptorafd,intpriority):從afd所對應(yīng)的文件中加載聲音。6、load(Stringpath,intpriority):從path對應(yīng)的文件去加載聲音。說明:4個load***中都有一個priority參數(shù),該參數(shù)目前還沒有任何作用,Android建議將該參數(shù)設(shè)為1,保持和未來的兼容性。play(intsoundID,floatleftVolume,floatrightVolume,intpriority,intloop,floatrate):指定播放哪個聲音,2和3參數(shù)的意思是音量,priority指定播放的優(yōu)先級,數(shù)值越大優(yōu)先級越高;loop用于指定是否循環(huán),0不循環(huán),-1位循環(huán);rate指定播放的比率,可選值為0.5–2,1為正常比率。1.1.3AudioTrackAudioTrack屬于更偏底層的音頻播放,在Android的framework層有MediaPlayerService,其內(nèi)部就是使用了AudioTrack。AudioTrack用于單個音頻播放和管理,相比于MediaPlayer具有:精煉、高效的優(yōu)點。因此,對于AutioTrack可以總結(jié)如下:
使用場景:更適合實時產(chǎn)生播放數(shù)據(jù)的情況,如加密的音頻,MediaPlayer是束手無策的,AudioTrack可以處理。要求:AudioTrack用于播放PCM(PCM無壓縮的音頻格式)音樂流的回放,如果要播需放其它格式音頻,需要相應(yīng)的解碼器,這也是AudioTrack用的比較少的原因,原因在于需要程序開發(fā)者自己解碼音頻。播放模式:①AudioTrack播放音頻有兩種播放模式,一種是靜態(tài)模式,即加載的數(shù)據(jù)和資源可以直接全部加載完畢,加載方式簡單,效率也比較高。但是如果數(shù)據(jù)量很大,往往不適合;②流模式和網(wǎng)絡(luò)上播放視頻是類似的,即數(shù)據(jù)是按照一定規(guī)律不斷地傳遞給接收方的。音頻文件過大音頻屬性要求高,比如采樣率高、深度大的數(shù)據(jù);另外如果音頻數(shù)據(jù)是實時產(chǎn)生的,這種情況就只能用流模式。使用AudioTrack公有三個步驟:
共有三個步驟:
構(gòu)建AudioTrack對象,并且把PCM的參數(shù)傳到對象里面調(diào)用start調(diào)用write。另外,其實AudioTrack以外,還有一個Audio系統(tǒng),在該系統(tǒng)中主要包含三個核心的API,分別是:
AudioManager:主要是用來管理Audio系統(tǒng)的。AudioTrack:主要是用來播放聲音。AudioRecord:主要是用來錄音。1.1.4RingtoneManagerRingtone為***、通知和其他類似聲音提供快速播放的***,該種方式播放音頻時,還會涉及到一個核心的管理類”RingtoneManager”,該類作為管理類提供系統(tǒng)***列表檢索***,并且RingtoneManager可以生成Ringtone實例。具體的Ringtone的使用步驟和相關(guān)的***如下所示:
1、獲取Ringtone對象實例://1.通過***uri獲取staticRingtonegetRingtone(Contextcontext,UriringtoneUri)//2.通過***檢索位置獲取RingtonegetRingtone(intposition)2、RingtoneManager中重要的***:1.//兩個構(gòu)造***(Activityactivity)RingtoneManager(Contextcontext)?2.//獲取指定聲音類型(***、通知、鬧鈴等)的默認(rèn)聲音的UristaticUrigetDefaultUri(inttype)?3.//獲取系統(tǒng)所有Ringtone的cursorCursorgetCursor()?4.//獲取cursor指定位置的RingtoneuriUrigetRingtoneUri(intposition)?5.//判斷指定Uri是否為默認(rèn)***staticbooleanisDefault(UriringtoneUri)?6.//獲取指定uri的所屬類型staticintgetDefaultType(UridefaultRingtoneUri)?7.//將指定Uri設(shè)置為指定聲音類型的默認(rèn)聲音staticvoidsetActualDefaultRingtoneUri(Contextcontext,inttype,UriringtoneUri)8、//播放voidplay()9、//停止播放voidstop()1.1.5音頻及音效的播放總結(jié)經(jīng)過如上幾種音效的播放方式的講解,我們可以對音效的播放做簡單的總結(jié)如下所示:
1.對于延遲度要求不高,并且希望能夠更全面的控制音樂的播放,MediaPlayer比較適合。2.聲音短小,延遲度小,并且需要幾種聲音同時播放的場景,適合使用SoundPool。3.播放大文件音樂,如WAV無損音頻和PCM無壓縮音頻,可使用更底層的播放方式AudioTrack。它支持流式播放,可以讀取(可來自本地和網(wǎng)絡(luò))音頻流,卻播放延遲較小。4、AudioTrack直接支持WAV和PCM,其他音頻需要解碼成PCM格式才能播放。.jet的音頻比較少見(有的游戲中在使用),可使用專門的播放器JetPlayer播放。5.對于系統(tǒng)類聲音的播放和操作,Ringtone更適合。二、音頻的采集手機一般都有麥克風(fēng)和攝像頭,而Android系統(tǒng)就可以利用這些硬件來錄制音視頻了。為了增加對錄制音視頻的支持,Android系統(tǒng)提供了一個MediaRecorder的類。
與MediaPlayer類非常相似MediaRecorder也有它自己的狀態(tài)圖,MediaRecorder的各個狀態(tài)介紹如下:
Initial:初始化狀態(tài)。使用new()***創(chuàng)建MediaRecorder對象或者調(diào)用了reset()***時,該MediaRecorder對象處于Initial狀態(tài)。Initialized:已初始化狀態(tài),在Initial狀態(tài)調(diào)用setAudioSource()或setVideoSource()***進入該狀態(tài)。在這個狀態(tài)可以通過setOutputFormat()***設(shè)置輸出格式,此時MediaRecorder轉(zhuǎn)換為DataSourceConfigured狀態(tài)。另外,通過reset()重新進入Initial狀態(tài)。DataSourceConfigured:數(shù)據(jù)源配置狀態(tài),這期間可以設(shè)定編碼方式、輸出文件、屏幕旋轉(zhuǎn)、預(yù)覽顯示等等??梢栽贗nitialized狀態(tài)通過setOutputFormat()***進入該狀態(tài)??梢酝ㄟ^prepare()***到達Prepared狀態(tài)。Prepared:就緒狀態(tài),在DataSourceConfigured狀態(tài)通過prepare()***進入該狀態(tài)??梢酝ㄟ^start()進入錄制狀態(tài)。另外,可以通過reset()***回到Initialized狀態(tài)。Recording:錄制狀態(tài),通過調(diào)用start()***進入該狀態(tài)。另外,它可以通過stop()***或reset()***回到Initial狀態(tài)。Released:釋放狀態(tài),可以通過在Initial狀態(tài)調(diào)用release()***來進入這個狀態(tài),這時將會釋放所有和MediaRecorder對象綁定的資源。Error:錯誤狀態(tài),當(dāng)錯誤發(fā)生的時候進入這個狀態(tài),它可以通過reset()***進入Initial狀態(tài)。需要說明的是,與MediaPlayer相似,使用MediaRecorder錄音錄像時需要嚴(yán)格遵守狀態(tài)函數(shù)調(diào)用的先后順序,在不同的狀態(tài)調(diào)用不同的函數(shù),否則會出現(xiàn)異常。如上的文字描述可以轉(zhuǎn)換為如下狀態(tài)圖:
三、Android中多音視頻編解碼音視頻的原始數(shù)據(jù)非常龐大,難以存儲和傳輸。要解決音視頻數(shù)據(jù)的存儲和傳輸問題,需要做如下處理:
音視頻編碼:即對數(shù)據(jù)進行壓縮,音視頻數(shù)據(jù)壓縮技術(shù)就是音視頻編碼。編碼的目的就是在最小圖像或音頻信息丟失情況下得到最大的壓縮。音視頻解碼:解碼是相對編碼的,其目的是最大限度的還原原始圖像或聲音信息。編解碼的作用:編解碼的意義就是便于數(shù)據(jù)傳輸和存儲。而我們知道音視頻編解碼格式非常多(h264、h265、vp8、vp9、aac、opus……),實現(xiàn)每種編解碼都需要引入外部庫,導(dǎo)致項目臃腫、包體積過大且運行性能差。
因此Google提出了一套標(biāo)準(zhǔn),這就是MediaCodec。具體來說,了解MediaCodec可以從以下幾個方面來說:
定義:MediaCodec是Google公司專門為Android開發(fā)者和芯片廠商搭建的一套用于調(diào)用硬件編解碼器組件的統(tǒng)一接口,全部遵循該接口規(guī)范即可簡單的使用,主要的目的在于統(tǒng)一標(biāo)準(zhǔn)。特點:與常規(guī)編解碼庫相比,MediaCodec具有非常明顯的優(yōu)勢,它速度快、效率高、CPU占用率低、內(nèi)存小、節(jié)省包體積。使用MediaCodec可以解決項目臃腫、減小包體積和提升編解碼性能。關(guān)于MediaCodec的工作原理,可以參見下圖所示:
工作步驟如下所示:
MediaCodec處理輸入數(shù)據(jù)后生成輸出數(shù)據(jù)。通過異步方式處理數(shù)據(jù),并使用一組輸入和輸出緩沖區(qū)。輸入端:請求一個空的輸入緩沖區(qū),用數(shù)據(jù)填充它并將其發(fā)送到編解碼器進行處理。輸出端:編解碼器處理完數(shù)據(jù)并將其轉(zhuǎn)換到一個空的輸出緩沖區(qū)。最后,請求一個已填滿的輸出緩沖區(qū),使用它的內(nèi)容并將其釋放回編解碼器。可以操作的數(shù)據(jù)類型MediaCodec可以對三種數(shù)據(jù)進行操作,分別是:
編碼數(shù)據(jù)原始音頻數(shù)據(jù)原始視頻數(shù)據(jù)MediaCodec的狀態(tài)管理MediaCodec存在三種狀態(tài):停止(stoped)、執(zhí)行(executing)、釋放(released)。
停止?fàn)顟B(tài):包含三個子狀態(tài):配置(configured)、未初始化(uninitialized)、錯誤(error)執(zhí)行狀態(tài):包含三個子狀態(tài):刷新(flushed)、運行(running)、結(jié)束流(end-of-stream)MediaCodec發(fā)展
Android系統(tǒng)中關(guān)于MediaCodec的介紹,可以參考Android的官方網(wǎng)站提供的信息:https://developer.android.google.cn/reference/kotlin/android/media/MediaCodec
MediaCodec是在Android4.1版本(API16)中出現(xiàn)并可用的,它提供了一種極其原始的接口。MediaCodec類同時存在Java和C++層中,但是只有前者是公共訪問***。
在Android4.3(API18)中,MediaCodec被擴展為通過Surface提供輸入的***(通過createInputSurface***),允許來自于相機的預(yù)覽或者是經(jīng)過OpenGLES呈現(xiàn)。在該版本中,MediaCodec是第一個過了CTS測試的版本。所謂的CTS,全稱是CompatibilityTestSuite,主要是google推出的一種設(shè)備兼容性測試規(guī)范,用來保證不同設(shè)備一致的用戶體驗的規(guī)范。
除此之外,4.3版本還引入了MediaMuxer。MediaMuxer允許將AVC編解碼器(原始H.264基本流)的輸出轉(zhuǎn)換為.MP4格式,可以和音頻流一起轉(zhuǎn)碼也可以單獨轉(zhuǎn)換。
音視頻編輯MediaCodec通常與MediaExtractor、MediaSync、MediaMuxer、MediaCrypto、MediaDrm、Image、Surface和AudioTrack一起使用,幾乎可以實現(xiàn)大部分音視頻相關(guān)功能。主要的操作步驟如下所示:
1、初始化。2、MediaExtractor:提取音視頻編碼數(shù)據(jù),MediaExtractor用于對音視頻文件解封裝,提取出已編碼的媒體數(shù)據(jù)。3、MediaCodec:使用解碼器進行解碼。4、處理:對音視頻進行處理。5、編碼:使用MediaCodec編碼器對音視頻數(shù)據(jù)編碼。6、合成:MediaMuxer合成音視頻文件。MediaMuxer用于封裝編碼后的音視頻數(shù)據(jù),目前支持MP4、Webm和3GP文件作為輸出。7、釋放資源。代碼中的體現(xiàn)如下:
-createEncoderByType/createDecoderByType-configure-start-while(true){-dequeueInputBuffer-queueInputBuffer-dequeueOutputBuffer-releaseOutputBuffer}-stop-release使用MediaCodec編碼音頻初始化MediaCodec對象,如下所示:privatestaticMediaCodeccreateAudioEncoder()throwsIOException{MediaCodeccodec=MediaCodec.createEncoderByType(AUDIO_MIME);MediaFormatformat=newMediaFormat();format.setString(MediaFormat.KEY_MIME,AUDIO_MIME);format.setInteger(MediaFormat.KEY_BIT_RATE,64000);format.setInteger(MediaFormat.KEY_CHANNEL_COUNT,1);format.setInteger(MediaFormat.KEY_SAMPLE_RATE,44100);format.setInteger(MediaFormat.KEY_AAC_PROFILE,MediaCodecInfo.CodecProfileLevel.AACObjectLC);codec.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);returncodec;}讀取PCM數(shù)據(jù),執(zhí)行編碼操作。...while(!sawOutputEOS){if(!sawInputEOS){inputBufIndex=audioEncoder.dequeueInputBuffer(10_000);if(inputBufIndex>=0){ByteBufferinputBuffer=audioInputBuffers[inputBufIndex];//先清空緩沖區(qū)inputBuffer.clear();intbufferSize=inputBuffer.remaining();if(bufferSize!=rawInputBytes.length){rawInputBytes=newbyte[bufferSize];}//讀取readRawAudioCount=fisRawAudio.read(rawInputBytes);//判斷是否到文件的末尾if(readRawAudioCount==-1){readRawAudioEOS=true;}if(readRawAudioEOS){audioEncoder.queueInputBuffer(inputBufIndex,0,0,0,MediaCodec.BUFFER_FLAG_END_OF_STREAM);sawInputEOS=true;}else{//放入緩沖區(qū)inputBuffer.put(rawInputBytes,0,readRawAudioCount);rawAudioSize+=readRawAudioCount;//放入編碼隊列audioEncoder.queueInputBuffer(inputBufIndex,0,readRawAudioCount,audioTimeUs,0);audioTimeUs=(long)(1_000_000*((float)rawAudioSize/AUDIO_BYTES_PER_SAMPLE));}}}?//輸出端outputBufIndex=audioEncoder.dequeueOutputBuffer(outBufferInfo,10_000);if(outputBufIndex>=0){//Simplyignorecodecconfigbuffers.if((outBufferInfo.flags&MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!=0){Log.i(TAG,"audioencoder:codecconfigbuffer");audioEncoder.releaseOutputBuffer(outputBufIndex,false);continue;}if(outBufferInfo.size!=0){ByteBufferoutBuffer=audioOutputBuffers[outputBufIndex];outBuffer.position(outBufferInfo.offset);outBuffer.limit(outBufferInfo.offset+outBufferInfo.size);//Log.v(TAG,String.format("writingaudiosample:size=%s,presentationTimeUs=%s",outBufferInfo.size,outBufferInfo.presentationTimeUs));if(lastAudioPresentationTimeUs<=outBufferInfo.presentationTimeUs){lastAudioPresentationTimeUs=outBufferInfo.presentationTimeUs;intoutBufSize=outBufferInfo.size;intoutPacketSize=outBufSize+7;outBuffer.position(outBufferInfo.offset);outBuffer.limit(outBufferInfo.offset+outBufSize);byte[]outData=newbyte[outPacketSize];addADTStoPacket(outData,outPacketSize);outBuffer.get(outData,7,outBufSize);fosAccAudio.write(outData,0,outData.length);//Log.v(TAG,outData.length+"byteswritten.");}else{Log.e(TAG,"errorsample!itspresentationTimeUsshouldnotlowerthanbefore.");}}audioEncoder.releaseOutputBuffer(outputBufIndex,false);if((outBufferInfo.flags&MediaCodec.BUFFER_FLAG_END_OF_STREAM)!=0){sawOutputEOS=true;}}elseif(outputBufIndex==MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED){audioOutputBuffers=audioEncoder.getOutputBuffers();}elseif(outputBufIndex==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){MediaFormataudioFormat=audioEncoder.getOutputFormat();Log.i(TAG,"formatchange:"+audioFormat);}}...以上是MediaCodec的編碼執(zhí)行操作。如果是解碼,與編碼過程相反即可完成。
總結(jié)
優(yōu)點:MediaCodec是Android重要的底層多媒體組件,合理使用MediaCodec可以實現(xiàn)播放器、直播、視頻編輯、視頻錄制、視頻通話、視頻會議等幾乎所有音視頻相關(guān)的編解碼功能,且與常規(guī)編解碼庫相比擁有絕對的性能優(yōu)勢。不足:MediaCodec也存在一些缺點,兼容性、穩(wěn)定性都比較差,開發(fā)過程中會經(jīng)常遇到機型、版本等適配問題,這些都可以通過適配合理解決。四、音頻NDKAPI開發(fā)如果遇到一些要求更高的項目開發(fā),對音頻有高性能的需求,比如說:所需的不僅僅是簡單的聲音播放或錄制功能。它們需要響應(yīng)式實時系統(tǒng)行為。一些典型用例如:音頻合成器、電子鼓、音樂學(xué)習(xí)應(yīng)用、DJ混音、音效、視頻/音頻會議等這類要求特別高的需求時。就要從更深層次的底層來提供功能支持,這里就會用到NDK開發(fā)。
首先來了解一下NDK,全稱是NativeDevelopmentKit,翻譯為原生開發(fā)工具包,主要的作用是可以讓開發(fā)者在Android應(yīng)用中利用C和c++代碼的工具,可用以從自己的源代碼構(gòu)建,或者利用現(xiàn)有的預(yù)構(gòu)建庫。
本部分的內(nèi)容可以在如下的Android官方網(wǎng)站中進行查看和學(xué)習(xí):https://developer.android.google.cn/ndk/guides/audio
Android官方給提供了如下選擇:
OpenSLES:全稱為OpenSoundLibraryforEmbeddedSystems,嵌入式音頻加速標(biāo)準(zhǔn)。OpenSLES是無授權(quán)費、跨平臺、針對嵌入式系統(tǒng)精心優(yōu)化的硬件音頻加速API,為嵌入式移動多媒體設(shè)備上的本地應(yīng)用程序開發(fā)者提供了標(biāo)準(zhǔn)化、高性能、低響應(yīng)時間的音頻功能實現(xiàn)***,同時還實現(xiàn)了軟/硬件音頻性能的直接跨平臺部署,不僅降低了執(zhí)行難度,而且促進了高級音頻市場的發(fā)展。與Android的關(guān)系:Android2.3即API9時開始支持OpenSLES標(biāo)準(zhǔn),通過NDK提供相應(yīng)的API開發(fā)接口。Android實現(xiàn)的OpenSLES只是OpenSL的子集,然后進行了擴展。Android中OpenSLES的相關(guān)資料:https://developer.android.google.cn/ndk/guides/audio/openslAAudio:在Android8.0版本后引入的音頻庫,該音頻庫需要使用C語言在Native層進行調(diào)用,屬于NDK開發(fā)范疇。AAudio是OpenSLES庫的輕量級實現(xiàn),同樣具有低延遲,高性能的特點。需要特別注意的是,AAudio作為一款定位為輕量級的音頻庫,只提供寫入音頻流進行發(fā)音的功能,不負(fù)責(zé)音頻設(shè)備管理,文件I/O,音頻編解碼等操作;音頻輸入:從話筒,耳機等音頻輸入設(shè)備中,使用AAudio音頻流采集音頻數(shù)據(jù),讀取性能高,低延遲。音頻輸出:將音頻流寫入到AAudio,以極高性能方式將音頻流輸出到發(fā)音設(shè)備中。Oboe:該庫是基于AAudio封裝的一個開源庫,在github上有開源的地址,鏈接如下:https://github.com/google/oboe該庫與AAudio是使用C++編寫的適用于Android開發(fā)的高效率的音頻開發(fā),依然屬于NDK開發(fā)的范疇。Google官方推薦使用該庫。五、音頻算法的開源庫FFmpeg:路人皆知FFmpeg是一套可以用來記錄、轉(zhuǎn)換數(shù)字音頻、視頻,并能將其轉(zhuǎn)化為流的開源程序。它提供了錄制、轉(zhuǎn)換以及流化音視頻的完整解決方案。它包含了非常先進的音頻/視頻編解碼庫libavcodec,為了保證高可移植性和編解碼質(zhì)量,libavcodec里很多code都是從頭開發(fā)的。
只要是做音視頻開發(fā)的開發(fā)者,幾乎沒有不知道FFmpeg庫的。在github上可以找到FFmpeg的主頁地址如下:https://github.com/FFmpeg/FFmpeg官方網(wǎng)站的地址是:https://ffmpeg.org/
其中包含的庫主要包括:
libavcodec:音/視頻編碼庫。libavformat:音視頻格式的生成和解析等操作。libavutil:公共的工具函數(shù)。該程序最初在Linux平臺上開發(fā)和使用,目前在windows、mac上均可以使用。
在Android中使用FFmpeg如果需要在Android中使用FFmpeg,需要進行集成。需要經(jīng)過幾個步驟:
編譯:首先要下載FFmpeg,并進行編譯,編譯出Android中需要的文件。將編譯后的內(nèi)容集成到Android項目中。測試并調(diào)用集成的FFmpeg中的***。SpeexSpeex主要是針對語音的開源免費,無專利保護的一種音頻壓縮格式,是專門為碼率在2-44kbps的語音壓縮而設(shè)計。Speex的特點主要包括:
窄帶(8kHz),寬帶(16kHz)和超寬帶(32kHz)壓縮于同一位流可變比特率(VBR)非連續(xù)傳輸(DTX)感官回聲消除(AEC)噪音屏蔽SlikSlik算法主要的作用是實現(xiàn)語音和音頻的編解碼,其主要的特點是:
支持4種采樣率:8KHz、12KHz、16KHz、24KHz;三種復(fù)雜度:低、中、高。編碼碼率在6~40kbps。提供了定點C代碼,非常有利于向ARM、DSP移植和優(yōu)化。六、總結(jié)本篇文檔,我們用很長的篇幅介紹了多媒體開發(fā)中的音頻功能的開發(fā)和使用,在具體的開發(fā)和應(yīng)用中,重點應(yīng)該放在對整體知識的理解和架構(gòu)的梳理上,不要拘泥于某個API的使用,參數(shù)的作用等。歸根到底,不同的實現(xiàn)方案,不同的解決方案最終的落腳點和代碼操作步驟幾乎是相同的。再次回顧總結(jié)我們本篇內(nèi)容:
音頻的播放:MediaPlayer、SoundPool、AudioTrack、RingtoneManager音頻的采集:MediaRecorder音頻格式的轉(zhuǎn)換:MediaCodec底層庫的支持和使用:OpenSLES、AAudio、Oboe開源庫的了解和介紹:FFmpeg