stagefright簡介

1、    StageFright介紹      Android froyo版本多媒體引擎做了變動,新添加了stagefright框架,並且默認情況android選擇stagefright,並沒有完全拋棄 opencore,主要是做了一個OMX層,僅僅是對 opencore的omx-component部分做了引用。stagefright是在MediaPlayerService這一層加入的,和 opencore是並列的。Stagefright在 Android中是以shared library的形式存在(libstagefright.so),其中的module -- AwesomePlayer可用來播放video/audio。 AwesomePlayer提供許多API,可以讓上層的應用程序(Java/JNI)來調用。  

2、    StageFright數據流封裝     

    2.1》由數據源DataSource生成MediaExtractor。

            通過MediaExtractor::Create(dataSource)來 實現。

            Create方法通過兩步來生成相應的 MediaExtractor(MediaExtractor.cpp):通過dataSource->sniff來探測數據類型生成相應的Extractor:     

             if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG4) || !strcasecmp(mime, "audio/mp4")) {        

                   return new MPEG4Extractor(source);     

             } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {        

                 return new MP3Extractor(source, meta);     

            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)  

             || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {        

                 return new AMRExtractor(source);     

            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_WAV)) {        

                 return new WAVExtractor(source);     

            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_OGG)) {        

                return new OggExtractor(source);     

            } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {        

              return new MatroskaExtractor(source);     

           } else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {        

              return new MPEG2TSExtractor(source);    

          }      

    2.2》把音視頻軌道分離,生成mVideoTrack和mAudioTrack兩個MediaSource。代碼如下(AwesomePlayer.cpp):    

           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;   

           }      

    2.3》得到的這兩個MediaSource,只具有parser功能,沒有decode功能。

        還需要對這兩個MediaSource做進一步的包裝,獲取了兩個MediaSource(具有parse和decode功能):

                mVideoSource = OMXCodec::Create(

                                                          mClient.interface(), mVideoTrack->getFormat(), 

                                                          false, // createEncoder            

                                                          mVideoTrack,            

                                                          NULL,

                                                          flags); 

                mAudioSource = OMXCodec::Create(                 

                                                          mClient.interface(), mAudioTrack->getFormat(),                

                                                          false, // createEncoder                

                                                          mAudioTrack);     

              當調用MediaSource.start()方法後,它的內部就會開始從數據源獲取數據並解析,等到緩衝區滿後便停止。

               在AwesomePlayer裏就可以調用MediaSource的read方法讀取解碼後的數據。     

               對於mVideoSource來說,讀取的數據:mVideoSource->read(&mVideoBuffer, &options)交給顯示模塊進行渲染,mVideoRenderer->render(mVideoBuffer);    

              對mAudioSource來說,用mAudioPlayer對mAudioSource進行封裝,然後由mAudioPlayer負責讀取數據和播放控制。  

   3、    StageFright的Decode 經過“數據流的封裝”得到的兩個MediaSource,

            其實是兩個OMXCodec。AwesomePlayer和mAudioPlayer都是從 MediaSource中得到數據進行播放。AwesomePlayer得到的是最終需要渲染的原始視頻數據,而mAudioPlayer讀取的是最終需 要播放的原始音頻數據。也就是說,從OMXCodec中讀到的數據已經是原始數據了。     

              OMXCodec是怎麼把數據源經過parse、decode兩步以後轉化成原始數據的。

             從OMXCodec::Create這個構造方法開始,它的參數:IOMX  &omx指的是一個OMXNodeInstance對象的實例。     

             MetaData  &meta這個參數由MediaSource.getFormat獲取得到。這個對象的主要成員就是一個 KeyedVector<uint32_t, typed_data> mItems,裏面存放了一些代表MediaSource格式信息的名值對。     

             bool createEncoder指明這個OMXCodec是編碼還是解碼。    

             MediaSource &source是一個MediaExtractor。     

             char *matchComponentName指定一種Codec用於生成這個OMXCodec。     

             先使用findMatchingCodecs尋找對應的Codec,找到以後爲當前IOMX分配節點並註冊事件監聽器:omx->allocateNode(componentName, observer, &node)。

            最後,把IOMX封裝進一個OMXCodec:

                   sp<OMXCodec> codec = new OMXCodec(                    

                                                                             omx, node, quirks,                     

                                                                             createEncoder, mime, componentName,                    

                                                                             source); 

            這樣就得到了OMXCodec。AwesomePlayer中得到這個OMXCodec後,首先調用mVideoSource->start()進行初始化。

            OMXCodec初始化主要是做兩件事: 向OpenMAX發送開始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)  調用allocateBuffers()分配兩個緩衝區,存放在Vector<BufferInfo> mPortBuffers[2]中,分別用於輸入和輸出。AwesomePlayer開始播放後,通過mVideoSource->read(&mVideoBuffer, &options)讀取數據。mVideoSource->read(&mVideoBuffer, &options)具體是調用OMXCodec.read來讀取數據。

            而OMXCodec.read主要分兩步來實現數據的讀取: 通過調用drainInputBuffers()對mPortBuffers[kPortIndexInput]進行填充,這一步完成 parse。由OpenMAX從數據源把demux後的數據讀取到輸入緩衝區,作爲OpenMAX的輸入。    

           通過fillOutputBuffers()對mPortBuffers[kPortIndexOutput]進行填充,這一步完成 decode。由OpenMAX對輸入緩衝區中的數據進行解碼,然後把解碼後可以顯示的視頻數據輸出到輸出緩衝區。     

           AwesomePlayer通過mVideoRenderer->render(mVideoBuffer)對經過parse和decode 處理的數據進行渲染。

          一個mVideoRenderer其實就是一個包裝了IOMXRenderer的

             AwesomeRemoteRenderer: 

                mVideoRenderer = new AwesomeRemoteRenderer(                

                                                               mClient.interface()->createRenderer(                        

                                                                          mISurface, component,                         

                                                                          (OMX_COLOR_FORMATTYPE)format,                        

                                                                           decodedWidth, decodedHeight,                                     

                                                                           mVideoWidth, mVideoHeight,                        

                                                                           rotationDegrees));  

4、    StageFright處理流程     

                   Audioplayer 爲AwesomePlayer的成員,audioplayer通過callback來驅動數據的獲取,awesomeplayer則是通過 videoevent來驅動。二者有個共性,就是數據的獲取都抽象成mSource->Read()來完成,且read內部把parser和dec 綁在一起。Stagefright AV同步部分,audio完全是callback驅動數據流,video部分在onVideoEvent裏會獲取audio的時間戳,是傳統的AV時間戳 做同步。       

    4.1》AwesomePlayer的Video主要有以下幾個成員:    

              mVideoSource(解碼視頻)     

              mVideoTrack(從多媒體文件中讀取視頻數據)     

             mVideoRenderer(對解碼好的視頻進行格式轉換,android使用的格式爲RGB565)    

             mISurface(重繪圖層)    

             mQueue(event事件隊列)          

     4.2》stagefright運行時的Audio部分抽象流程如下:    

                 設置mUri的路徑     

                 啓動mQueue,創建一個線程來運行 threadEntry(命名爲TimedEventQueue,這個線程就是event調度器)     

                 打開mUri所指定的文件的頭部,則會根據類型選擇不同的分離器(如MPEG4Extractor)     

                 使用 MPEG4Extractor對MP4進行音視頻軌道的分離,並返回MPEG4Source類型的視頻軌道給mVideoTrack     

                 根據 mVideoTrack中的編碼類型來選擇解碼器,avc的編碼類型會選擇AVCDecoder,並返回給mVideoSource,並設置mVideoSource中的mSource爲mVideoTrack     插入onVideoEvent到Queue中,開始解碼播放     

                 通過mVideoSource對象來讀取解析好的視頻buffer 

                 如果解析好的buffer還沒到AV時間戳同步的時刻,則推遲到下一輪操作     

                 mVideoRenderer爲空,則進行初始化(如果不使用 OMX會將mVideoRenderer設置爲AwesomeLocalRenderer)     

                通過mVideoRenderer對象將解析好的視頻buffer轉換成RGB565格式,併發給display模塊進行圖像繪製     

                將onVideoEvent重新插入event調度器來循環      

     4.3》數據由源到最終解碼後的流程如下:                

                           URI,FD                   

                              |             

                         DataSource                    

                              |           

                     MediaExtractor                    

                               |     

                      mVideoTrack   mAudioTrack//音視頻數據流                   

                              |     

                     mVideoSource   mAudioSource//音視頻解碼器         

                             |                      |       

                      mVideoBuffer    mAudioPlayer 

               說明:     

                   設置DataSource,數據源可以兩種URI和FD。

               URI可以http://,rtsp://等。FD是一個本地文件描述符,能過FD,可以找到對應的文件。     

              由DataSource生成MediaExtractor。通過sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);來實現。

              MediaExtractor::Create(dataSource)會根據不同的數據內容創建不同的數據讀取對象。     

              通過調用setVideoSource由MediaExtractor分解生成音頻數據流(mAudioTrack)和視頻數據流(mVideoTrack)。     

             onPrepareAsyncEvent()如果DataSource是URL的話,根據地址獲取數據,並開始緩衝,直到獲取到mVideoTrack和 mAudioTrack。mVideoTrack和mAudioTrack通過調用initVideoDecoder()和 initAudioDecoder()來生成 mVideoSource和mAudioSource這兩個音視頻解碼器。然後調用postBufferingEvent_l()提交事件開啓緩衝。     

               數據緩衝的執行函數是onBufferingUpdate()。緩衝區有足夠的數據可以播放時,調用play_l()開始播放。play_l()中關鍵是 調用了postVideoEvent_l(),提交了 mVideoEvent。這個事件執行時會調用函數onVideoEvent()。這個函數通過調用 mVideoSource->read(&mVideoBuffer, &options)進行視頻解碼。音頻解碼通過mAudioPlayer實現。     

               視頻解碼器解碼後通過mVideoSource->read讀取一幀幀的數據,放到mVideoBuffer中,最後通過 mVideoRenderer->render(mVideoBuffer)把視頻數據發送到顯示模塊。當需要暫停或停止時,調用 cancelPlayerEvents來提交事件用來停止解碼,還可以選擇是否繼續緩衝數據。  

 5、    代碼標記Log    

             依據第4》項StageFright描述的Vide視頻播放流程,作Log標記跟蹤視頻DATA獲取、CODEC 過程。

             從AwesomePlayer.cpp中方法着手,步驟如下:     

                 在修改的/mydroid/frameworks/base/media/libstagefrigh/下,用mm編譯,並調試直到生成相應的.so文件。注:允許單模塊編譯時,需事先在/mydroid下允許. ./build/envsetup.sh文件。     

                在/mydroid/目錄下make進行整體編譯,生成system.img文件。說明:先單模塊編譯,後再整體編譯的好處是,可以縮短調試編譯的時間。     

                將system.img文件copy到/android-sdk-linux/platforms/android-8/下。注意:事先備份原有的system.img。     

                帶sdcard啓動模擬器,在/android-sdk-linux/tools/下運行./adb shell文件,再運行logcat    

                打開Gallery選擇視頻文件運行,並同步查看log。      

              反饋結果如下:

                   I/ActivityManager( 61): Starting: Intent { act=android.intent.action.VIEW dat=content://media/external/video/media/5 typ=video/mp4 cmp=com.cooliris.media/.MovieView } from pid 327 

                  I/RenderView(  327): OnPause RenderView com.cooliris.media.RenderView@4054a3b0

                  E/AwesomePlayer(   34): beginning AwesomePlayer... by jay remarked...

                  E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...

                  E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...

                  E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...

                 E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...

                 E/AwesomePlayer(   34): ending AwesomePlayer... by jay remarked...

                 E/AwesomePlayer(   34): setting video source now... by jay remarked...

                 E/AwesomePlayer(   34): setting Video Type... by jay remarked... 

                 E/AwesomePlayer(   34): returning AwesomeEvent...by jay remarked...

                 E/AwesomePlayer(   34): beginning initVideoDecoder by jay remarked...

                 D/MediaPlayer(  327): getMetadata 

                 I/ActivityManager(   61): Displayed com.cooliris.media/.MovieView: +1s761ms 

                 E/AwesomePlayer(   34): beginning AwesomeLocalRenderer init ...by jay remarked...

                 E/AwesomePlayer(   34): returning open(libstagefrighthw.so) correctly by jay remarked...

                 E/MemoryHeapBase(   34): error opening /dev/pmem_adsp: No such file or directory 

                 I/SoftwareRenderer(   34): Creating physical memory heap failed, reverting to regular heap.

                 E/AwesomePlayer(   34): ending AwesomeLocalRenderer init close ...by jay remarked...

                 E/AwesomePlayer(   34): returning AwesomeLocalRenderer...by jay remarked...

                 I/CacheService(  327): Starting CacheService   

 

http://blog.csdn.net/menguio/archive/2011/04/14/6323954.aspx

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章