1. 程式人生 > >Android MediaPlayer 視訊中的Audio部分的播放

Android MediaPlayer 視訊中的Audio部分的播放

本文轉自:

http://blog.csdn.net/myzhzygh/article/details/7429687

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 Media Framework層狀態機

一個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階段建立的。