http://blog.csdn.net/lskshz/article/details/17264113
1 Android多媒體框架結構
Android 多媒體系統縱向跨越了Android系統的所有4個層次: Java應用程序層、Java框架層、本地代碼層、Linux驅動層。多媒體本地代碼層是多媒體系統的重點。
Android媒體播放器的模塊結構如圖1所示。
從上圖可以看到,Android系統中多媒體框架的分層結構。
Framework層,對外提供構建與媒體相關應用程序的API接口。
Native層作為Android多媒體系統的核心,針對上圖,主要由MediaPlayer、MediaPlayerService、Stagefrightplayer、AudioFlinger和Audio HAL幾個部分組成。針對Native層,我們可以將其划分為兩個邏輯范疇進行分析。
從媒體框架的角度來說,MediaPlayer、MediaPlayerService和Stagefrightplayer三個部分構成了Android多媒體的基本框架。多媒體框架部分采用了C/S的結構,MediaPlayer作為C/S結構的Client端,MediaPlayerService和Stagefrightplayer作為C/S結構Server端,承擔着播放多媒體文件的責任,通過Stagefrightplayer,Server端完成Client端的請求並作出響應。
從Audio的角度來說,MediaPlayer、MediaPlayerService、Stagefrightplayer、AudioFlinger和Audio HAL又構成了Android系統中Audio系統的框架。MediaPlayer、MediaPlayerService和Stagefrightplayer作為Audio系統框架的Client端,最終由StagefrightPlayer將Audio數據交給Server端的AudioFlinger處理,由AudioFlinger將最終的Audio數據交由Audio HAL層輸出到硬件設備上,完成Audio的播放。
從縱向的角度來看,上層的應用程序將媒體的URI作為輸入,交給Java Framework層的MediaPlayer,經過JNI將請求交給本地框架層的MediaPlayer,可以認為Java Framework層的MediaPlayer模塊就是本地MediaPlayer在Java層的代理,然后由本地框架層的MediaPlayer向MediaPlayerService發出請求,MediaPlayerService將接收到的請求交給Stagefrightplyaer組件進行處理。Stagefrightplayer將處理的結果反饋給MediaPlayerService,最后由MediaPlayerService將處理后的Audio數據交給AudioFlinger處理,最終AudioFlinger將處理的數據通過Audio HAL層交給硬件驅動層,完成Audio的播放。
Stagefright 是Android多媒體本地實現的核心。Stagefright中包括的內容很多,單從播放的角度來看StagefrightPlayer輸入的是文件或網絡媒體流,輸出的是送往音視頻輸出設備播放的數據流,基本功能包括了媒體流控制、文件解析、音視頻文件解碼等方面。
2多媒體系統音頻數據播放的流程
從多媒體系統具體實現的角度來看,音頻數據播放主要經過音頻格式文件解析、音頻編碼流解碼、PCM 輸出播放3個階段。音頻播放器的基本結構如圖2所示。
基於Android多媒體系統音頻播放流程,對音頻文件的播放主要有以下4個流程:
(1)音頻文件的識別;
(2)音頻文件的解析;
(3)編碼數據的讀取;
(4)編碼數據的解碼和輸出。
2.1 MediaFramework層狀態機
一個Android媒體播放應用程序在進行媒體文件播放的過程中,有多種狀態。文件播放的過程就是這些狀態的轉換過程。基本的狀態及其轉換過程如下圖所示:
Notice: About the details of each state, please reference the contents on the following URL:
http://developer.android.com/reference/android/media/MediaPlayer.html
2.2 Media Native層框架
Native層的調用關系如下圖所示,從圖4中可以看到,Native層由三大部分組成,MediaPlayer、MediaPlayerService和用於完成具體播放功能的MediaPlayerBase實現部分。
Framework層的調用最終通過Native層的調用完成相應的功能。MediaPlayer和MediaPlayerService之間通過Binder進行通信。
從圖4中可以看到,MediaPlayerService端的播放任務最終會交由MediaPlayerBase抽象類的子類來完成。MediaPlayerBase作為執行具體播放模塊的超類,為了實現具體的播放功能派生了兩個子類,一個叫MediaPlayerHWInterface,用於提供直接通過硬件輸出的接口。另一個子類叫MediaPlayerInterface,StagefrightPlayer類作為MediaPlayerInterface的一個實現類。從邏輯上StagefrightPlayer擔當了執行具體播放功能的責任。
2.3 Media Stagefright框架
StageFright是一個framework,在這里也可以理解為是一個container,對上它提供了調用接口,對下它負責創建並管理那些實現具體功能的Palyer組件。
針對Audio播放,如圖5所示,圖中展現了Audio在Stagefright框架中的基本結構。通過這個基本的結構,Audio最終與Audio Server端AudioFlinger建立連接。
在具體的Stagefright框架實現中,StagefrightPlayer並沒有實現具體的方法,仍然作為Player的一個抽象層存在,具體的功能由其Wrapper類AwesomePlayer來完成的,所以Native層的具體動作是通過AwesomePlayer類來完成。
從圖中看到,Audio數據經過AwesomePlayer處理之后,具體應該是Decoder之后,交給AudioPlayer,在AudioSink存在的情況下,AudioSink作為MediaPlayerService的組成部分,負責完成Audio的輸出。AudioPlayer會通過AudioSink的實現類AudioOutput完成數據的輸出。而在具體實現中AudioOutput會創建用於聲音播放的AudioTrack,最終通過AudioTrack與AudioFlinger建立連接,實現聲音的輸出。
2.4 Native層Audio播放流程
以播放本地媒體文件為例,在媒體播放的數據源設置階段,對應Framework層的狀態圖,也就是IdleàInitialized的Initial階段,如圖6所示,在這個過程中需要進行setDataSource,Framework層的調用在AwesomePlayer中則會看到相應的處理,最終會通過調用AwesomePlayer對象的setDataSource方法來設置Native層的數據源;AwesomePlayer通過調用Stagefright包中的MediaExtractor類(提取器類的超類)的Create方法,通過識別數據源的格式,在獲得數據源對應的數據格式之后,創建該格式對應的提取器實例<XXX>Extractor,例如,如果數據源格式是AAC格式,那么就創建一個AACExtractor的實例,AACExtractor類繼承於MediaExtractor。在Create方法創建並返回一個<XXX>Extractor的實例之后,AwesomePlayer會通過該實例提供的接口方法解析數據源,獲得數據源的元數據(metadata)隨后通過調用<XXX>Extractor的getTrack方法獲得一個<XXX>Source的實例,如果數據格式為AAC,創建的實例名可能是AACSource,然后將獲得的實例進行保存。在這個階段,AwesomePlayer通過MediaExtractor提供的接口完成了對數據源中Audio和Video的Split(這時只是從邏輯上建立Audio和Video,還沒有真正地進行split,只是初步的分析,來創建響應的組件。),到此Media框架完成了整個數據源的設置過程;
StageFright創建了AwesomePlayer實例,AwesomePlayer通過MediaExtractor創建了真正的對應於具體文件格式的extractor,通過此具體的extractor創建了對應的讀取數據類,即<xxx>source類的實例。
setDataSource的動作到此結束,它的動作是完成了對文件的初步解析,確定了文件格式,最終的目的是為了創建相應的<xxx>extractor以及<xxx>source。此時播放還沒有開始。
值得注意的是:針對數據源的設置,Android媒體框架提供了多種方式,本地文件只是其中的一種形式方式,還有其它URL或者網絡媒體等形式。不同數據源形式處理的過程也不一樣。有同步的處理方式,也有異步的處理方式。
在完成了數據源的設置之后,到達Initialized狀態,下一個狀態就是對應Framework層的Prepared狀態。InitializedàPrepared階段有兩種Prepare方式,一種是同步prepare,另一種是異步Prepare。這個階段無論是同步還是異步,都需要初始化Audio和Video的Decoder組件。如果需要播放的媒體數據源(Audio和Video)是經過數據編碼壓縮過的,AwesomePlayer都會通過OMXCodec創建針對該壓縮編碼格式的OMXCodec對象,OMXCodec類繼承於MediaSource類,通過OMXCodec對象我們可以為AudioPlayer提供原始的Audio數據。
在通過OMXCodec創建原始Audio數據的過程中,我們必須通過OMXClient得到OMX的接口,通過這個接口,我們才能夠創建具體數據格式的OMXCodec對象。
在完成Prepare動作之后,到達Prepared狀態,下一個狀態就是對應Framework層的Started狀態。上層調用start方法之后,在AwesomePlayer層會調用play方法,針對Audio,AwesomePlayer層Play階段會創建一個AudioPlayer類的對象mAudioPlayer,並調用該對象的setSource方法,把之前OMXCodec::Create返回的mAudioSource作為參數,設置該對象的成員變量mSource;隨后調用該對象的start方法;開始Audio的播放(播放還是解碼?這里的播放時邏輯上的概念,實際是一邊播放,一邊解碼,異步過程)。
這里AudioPlayer就是對一個audio播放設備的模擬,通過該類完成audio數據的播放。
AudioPlayer 獲取OMXCodec對象中的相關參數:文件類型、采樣率、聲道數,之后調用AudioSink對象的open方法創建Audio數據的AudioTrack,因為最終的Audio播放都是通過AudioTrack和AudioFlinger打交道的。(調用此方法做什么?),並把AudioPlayer類的AudioSinkCallback方法做為回調函數傳給AudioSink。AudioPlayer先調用XXXDecoder解第一幀數據,並把該數據傳給AudioSink去播放,當播放完成后AudioSink會調用回調函數AudioSinkCallback再去取解碼后的數據,在這個函數中主要實現兩個功能,一是從解碼緩沖區中讀取解碼后的數據,二是把讀到的數據拷貝到AudioFlinger提供的環形緩沖區中。這個過程中,涉及數據從Codec緩沖區到AudioFlinger緩沖區的一次拷貝。(AudioSinkCallback函數的動作有哪些?)具體過程是AudioSinkCallback會調用FillBuffer函數獲取解碼后的原始數據,解碼后數據如果被取完后,AudioPlayer又會調用XXXDecoder解下一幀數據給AudioSink,來回反復,直到文件中數全部被播放。在拉動滾動條時,上層會傳來SeekTime,經AudioPlayer傳給XXXDecoder再傳給XXXExtractor,XXXExtractor根據上層傳來的SeekTime判斷出要播放的原始數據的起始位置,然后從該位置讀取一個數據包傳給XXXDecoder解碼。
AudioSink是audio播放過程的控制者,以一個thead的形式存在。它不停地調用decoder的接口來獲取數據,並將這些數據送往audio播放設備。(AudioSink與decoder沒太大關系,邏輯上沒關系)
在整個數據格式解碼播放過程中,主要涉及兩個模塊:XXXExtractor和XXXDecoder。
XXXExtractor主要執行數據格式文件解析和數據讀取功能;
XXXDecoder主要執行編碼數據的解碼功能,是由OMXCodec來提供的。
這里最終的一個東西就是AudioSinkCallback回調函數。首先我們要知道,這個函數是誰實現的:
size_tAudioPlayer::AudioSinkCallback(
MediaPlayerBase::AudioSink *audioSink,
void *buffer, size_t size, void*cookie) {
AudioPlayer *me = (AudioPlayer *)cookie;
return me->fillBuffer(buffer, size);
}
我們看到這個函數是由AudioPlayer類實現的。函數的內容直接說明了這個函數的功能,那就是填充所給的buffer。這個buffer是誰給的,這就要看是誰調用了這個函數。我們知道回調函數是實現出來給別人調用的,而不是自己調用。通過不斷的注冊,最終調用這個函數的類是AudioTrack。所以一切都明白了,是AudioTrack提供一個Buffer,讓AudioPlayer來填充這個Buffer。只不過這個注冊和調用的過程曲折了一點,注冊的時候通過AudioSink轉了個手,而調用的時候,也由AudioSink轉了個手。本質上在這個過程中AuidoSink並沒有做什么。AudioSink是銜接MediaPlayerService和AudioTrack的。是一個邏輯上的聲音池。MediaPlayerService處理后的Audio都要丟到這個聲音池中,而AudioTrack是由這個聲音池創建和控制。至於AudioSink在什么時候創建,由誰創建的,AudioSink的定義和結構就說明了這個,因為它是MediaPlayerService的組成部分,AudioSink是由MediaPlayerService在setDataSource階段創建的。