StageFright架構流程解讀

來源:互聯網
上載者:User

標籤:android   http   java   color   使用   檔案   

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 [email protected]
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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.