stagefright框架(一)Video Playback的流程
在Android上,預設的多媒體框架(multimedia framework)是OpenCORE
。 OpenCORE的優點是兼顧了跨平台的移植性,而且已經過多方驗證,所以相對來說較為穩定;但是其缺點是過於龐大複雜,需要耗費相當多的時間去維護。從 Android 2.0開始,Google引進了架構稍為簡潔的Stagefright,並且有逐漸取代OpenCORE的趨勢 (註1) 。
[圖1] Stagefright在Android多媒體架構中的位置。
[圖2] Stagefright所涵蓋的模組 (註2) 。
以下我們就先來看看Stagefright是如何播放一個影片檔。
Stagefright 在Android中是以shared library的形式存在(libstagefright.so),其中的module -- AwesomePlayer可用來播放video/audio (註3) 。AwesomePlayer提供許多API,可以讓上層的應用程式(Java/JNI)來呼叫,我們以一個簡單的程式來說明video playback的流程。
在Java中,若要播放一個影片檔,我們會這樣寫:
MediaPlayer mp = new MediaPlayer();
mp.setDataSource(PATH_TO_FILE); ...... (1)
mp.prepare(); ........................ (2) 、 (3)
mp.start(); .......................... (4)
在Stagefright中,則會看到相對應的處理;
(1) 將檔案的絕對路徑指定給mUri
status_t AwesomePlayer: : setDataSource( const char * uri, . . . ) { return setDataSource_l( uri, . . . ) ; } status_t AwesomePlayer: : setDataSource_l( const char * uri, . . . ) { mUri = uri; } |
(2) 啟動mQueue,作為event handler
status_t AwesomePlayer: : prepare( ) { return prepare_l( ) ; } status_t AwesomePlayer: : prepare_l( ) { prepareAsync_l( ) ; while ( mFlags & PREPARING) { mPreparedCondition. wait( mLock) ; } } status_t AwesomePlayer: : prepareAsync_l( ) { mQueue. start( ) ; mFlags | = PREPARING; mAsyncPrepareEvent = new AwesomeEvent( this & AwesomePlayer: : onPrepareAsyncEvent) ; mQueue. postEvent( mAsyncPrepareEvent) ; } |
(3) onPrepare AsyncEvent被觸發
void AwesomePlayer: : onPrepareAsyncEvent( ) { finishSetDataSource_l( ) ; initVideoDecoder( ) ; . . . . . . ( 3. 3) initAudioDecoder( ) ; } status_t AwesomePlayer: : finishSetDataSource_l( ) { dataSource = DataSource: : CreateFromURI( mUri. string ( ) , . . . ) ; sp< MediaExtractor> extractor = MediaExtractor: : Create( dataSource) ; . . . . . ( 3. 1) return setDataSource_l( extractor) ; . . . . . . . . . . . . . . . . . . . . . . . . . ( 3. 2) } |
(3.1) 解析mUri所指定的檔案,並且根據其header來選擇對應的extractor
sp< MediaExtractor> MediaExtractor: : Create( const sp< DataSource> & source, . . . ) { source- > sniff( & tmp, . . . ) ; mime = tmp. string ( ) ; if ( ! strcasecmp( mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) { return new MPEG4Extractor( source) ; } else if ( ! strcasecmp( mime, MEDIA_MIMETYPE_AUDIO_MPEG) ) { return new MP3Extractor( source) ; } else if ( ! strcasecmp( mime, MEDIA_MIMETYPE_AUDIO_AMR_NB) { return new AMRExtractor( source) ; } } |
(3.2) 使用extractor對檔案做A/V的分離 (mVideoTrack/mAudioTrack)
status_t AwesomePlayer: : setDataSource_l( const sp< MediaExtractor> & extractor) { for ( size_t i = 0; i < extractor- > countTracks( ) ; + + i) { sp< MetaData> meta = extractor- > getTrackMetaData( i) ; CHECK( meta- > findCString( kKeyMIMEType, & mime) ) ; if ( ! haveVideo & & ! strncasecmp( mime, "video/" , 6) ) { setVideoSource( extractor- > getTrack( i) ) ; haveVideo = true ; } else if ( ! haveAudio & & ! strncasecmp( mime, "audio/" , 6) ) { setAudioSource( extractor- > getTrack( i) ) ; haveAudio = true ; } } } void AwesomePlayer: : setVideoSource( sp< MediaSource> source) { mVideoTrack = source; } |
(3.3) 根據mVideoTrack中的編碼類型來選擇video decoder (mVideoSource)
status_t AwesomePlayer: : initVideoDecoder( ) { mVideoSource = OMXCodec: : Create( mClient. interface( ) , mVideoTrack- > getFormat( ) , false , mVideoTrack) ; } |
(4) 將mVideoEvent放入mQueue中,開始解碼播放,並交由mVideoRenderer來畫出
status_t AwesomePlayer: : play( ) { return play_l( ) ; } status_t AwesomePlayer: : play_l( ) { postVideoEvent_l( ) ; } void AwesomePlayer: : postVideoEvent_l( int64_t delayUs) { mQueue. postEventWithDelay( mVideoEvent, delayUs) ; } void AwesomePlayer: : onVideoEvent( ) { mVideoSource- > read ( & mVideoBuffer, & options) ; [ Check Timestamp] mVideoRenderer- > render( mVideoBuffer) ; postVideoEvent_l( ) ; } |
(註1) 從Android2.3 (Gingerbread) 開始,預設的多媒體框架為 Stagefright。
(註2) Stagefright的架構尚不斷在演進中,本系列文章並未含括所有的模組。
(註3) Audio的播放是交由 AudioPlayer 來處理,請參考《 Stagefright (6) - Audio Playback的流程 》。