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