標籤:
轉載請把頭部出處連結和尾部二維碼一起轉載,本文出自逆流的魚:http://blog.csdn.net/hejjunlin/article/details/52532085
不知不覺到第九篇了,感覺還有好多好多沒有寫,路漫漫其修遠兮 ,吾將上下而求索,上篇主要介紹了Stagefright架構及AwesomePlayer的資料解析器,最後我們說道,涉及parse及decode部分,將在本篇中介紹,看下今天的Agenda:
- 兩張圖看資料走向
- AwesomePlayer中prepare過程
- AwesomePlayer初始化音視頻解碼器過程
- Stagefright的Decode過程
- Stagefright處理資料過程
- 資料由源到最終解碼後的流程
兩張圖看資料走向
一切從MediaSource中來,一切又從MediaSource中去
Audio:
Video:
AwesomePlayer中prepare過程
首先我們開始看下AwesomePlayer的prepare的執行過程:
以上代碼總結為:prepare程序呼叫了prepareAsync_l函數,在prepareAsync_l中執行new AwesomeEvent,並將AwesomePlayer調用onPrepareAsyncEvent的結果返回AwesomeEvent的構造作為參數。
接著分析AwesomeEvent的過程: 啟動mQueue,作為event handler
上面new AwesomeEvent會執行onPrepareAsyncEvent函數,我們看下該函數的做了什嗎?
以上代碼總結為:會將AV(音視頻)進行分處理,於是有了AwesomePlayer::initVideoDecoder及AwesomePlayer::initAudioDecoder()函數。
本文出自逆流的魚:http://blog.csdn.net/hejjunlin/article/details/52532085
初始化音視頻解碼器過程
我們先看下initVideoDecoder,即初始化視頻解碼器:
接著看下初始化音頻解碼器,看下幾個變數的聲明:
接著看代碼如下:
對上面代碼進行總結:
Stagefright調用AwesomePlayer的prepare後,AwesomePlayer調用自身的prepareAsync進行初始化化音視頻解碼器,這兩個方法裡面都會OMXCodec::Create,接下來看下這個過程。
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:
這樣就得到了OMXCodec。
- AwesomePlayer中得到這個OMXCodec後,接著看initVideoDecoder/initAudioDecoder,這裡看initAudioDecoder方法,是把 mAudioSource = mOmxSource,賦值,接著調用mAudioSource->start()進行初始化。 OMXCodec初始化主要是做兩件事:
- 向OpenMAX發送開始命令。mOMX->sendCommand(mNode, OMX_CommandStateSet, OMX_StateIdle)
- 調用allocateBuffers()分配兩個緩衝區,存放在Vector mPortBuffers[2]中,分別用於輸入和輸出。
- 然後在現個initxxxDecoder方法中會調用(mAudioSource->start()/mVideoSource->start())
觸發MediaSource的子類VideoSource及AudioSource調用start()方法後,它的內部就會開始從資料來源擷取資料並解析,等到緩衝區滿後便停止。在AwesomePlayer裡就可以調用MediaSource的read方法讀取解碼後的資料。
本文出自逆流的魚:http://blog.csdn.net/hejjunlin/article/details/52532085
這個過程就是prepare的過程,重點是解碼把流放到Buffer中
接下來,當java層調用start方法時,通過mediaplayerservice,再傳到StagefrightPlayer中,引用AwesomePlayer,這樣就調到AwesomePlayer的play方法,看代碼:
- 當AwesomePlayer調用play後,通過mVideoSource->read(&mVideoBuffer, &options)讀取資料。mVideoSource->read(&mVideoBuffer, &options)具體是調用OMXCodec.read來讀取資料。而OMXCodec.read主要分兩步來實現資料的讀取:
- (1) 通過調用drainInputBuffers()對mPortBuffers[kPortIndexInput]進行填充,這一步完成 parse。由OpenMAX從資料來源把demux後的資料讀取到輸入緩衝區,作為OpenMAX的輸入。
- (2) 通過fillOutputBuffers()對mPortBuffers[kPortIndexOutput]進行填充,這一步完成 decode。由OpenMAX對輸入緩衝區中的資料進行解碼,然後把解碼後可以顯示的視頻資料輸出到輸出緩衝區。
AwesomePlayer通過mVideoRenderer->render(mVideoBuffer)對經過parse和decode 處理的資料進行渲染。一個mVideoRenderer其實就是一個封裝了IOMXRenderer的AwesomeRemoteRenderer:
Stagefright處理資料過程
Audioplayer為AwesomePlayer的成員,audioplayer通過callback來驅動資料的擷取,awesomeplayer則是通過 videoevent來驅動。二者有個共性,就是資料的擷取都抽象成mSource->read()來完成,且read內部把parse和decode綁在一起。Stagefright AV同步部分,audio完全是callback驅動資料流,注意是video部分在onVideoEvent裡會擷取audio的時間戳記,是傳統的AV時間戳記做同步。
AwesomePlayer的Video主要有以下幾個成員:
- mVideoSource(解碼視頻)
- mVideoTrack(從多媒體檔案中讀取視頻資料)
- mVideoRenderer(對解碼好的視頻進行格式轉換,android使用的格式為RGB565)
- mISurface(重繪圖層)
- mQueue(event事件隊列)
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時間戳記同步的時刻,則延遲到下一輪操作
1、mVideoRenderer為空白,則進行初始化(如果不使用 OMX會將mVideoRenderer設定為AwesomeLocalRenderer)
2、通過mVideoRenderer對象將解析好的視頻buffer轉換成RGB565格式,並發給display模組進行映像繪製
3、將onVideoEvent重新插入event調度器來迴圈
本文出自逆流的魚:http://blog.csdn.net/hejjunlin/article/details/52532085
Stagefright資料由源到最終解碼後的流程
可以對照《Android Multimedia架構總結(八)Stagefright架構之AwesomePlayer及資料解析器》中那個圖,這裡不貼了,
- 設定DataSource,資料來源可以兩種URI和FD。URI可以http://,rtsp://等。FD是一個本地檔案描述符,能過FD,可以找到對應的檔案。
- 由DataSource產生MediaExtractor。通過sp 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來提交事件用來停止解碼,還可以選擇是否繼續緩衝資料。
第一時間獲得部落格更新提醒,以及更多android乾貨,源碼分析,歡迎關注我的公眾號,掃一掃下方二維碼或者長按識別二維碼,即可關注。
如果你覺得好,隨手點贊,也是對筆者的肯定,也可以分享此公眾號給你更多的人,原創不易
Android Multimedia架構總結(九)Stagefright架構之資料處理及到OMXCodec過程