編輯:關於Android編程
前言:上篇文中最後介紹了數據解碼放到Buffer過程,今天分析的是stagefright框架中音視頻輸出過程:
先看下今天的Agenda:
一張圖回顧數據處理過程 視頻渲染器構建過程 音頻數據到Buffer過程 AudioPlayer在AwesomePlayer運行過程 音視頻同步 音視頻輸出 一張圖看音視頻輸出在構造時,new AweSomeEvent時,就開始把AwesomePlayer把onVideoEvent注入進去。
以上代碼最會調用initRenderer_l函數
從上面代碼來看:AwesomeRemoteRenderer的本質由OMX::createRenderer會先建立一個hardware renderer就是:mVideoRenderer =
new AwesomeNativeWindowRenderer(mNativeWindow, rotationDegrees);若失敗,則建立new AwesomeLocalRenderer(mNativeWindow, format);
接下來看下:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxpbWcgYWx0PQ=="這裡寫圖片描述" src="/uploadfile/Collfiles/20160918/20160918092107315.png" title="\" />
而另一個AwesomeLocalRenderer在構造時new SoftwareRenderer(nativeWindow)
AwesomeLocalRender的本質上是由OMX:createRenderer,createRenderer會建立一個渲染器。如果video decoder是software component,則建立一個AwesomeLocalRenderer作為mVideoRenderer
AwesomeLocalRenderer的constructor會呼叫本身的init函數,其所做的事和OMX::createRenderer一模一樣。可以理解為把read的數據顯示在渲染器中。
渲染器渲染出畫面後,我們可能會想,MediaExtractor把音視頻進行分開,那音頻呢?誰來讓他們保持同步的呢?
無論是音頻也好,還是視頻,都是bufferdata,音頻或視頻總有一個來維持時間線的流。舉個例子:我們看過雙簧,一個人說話,一個人演示動作,動作快了不行,話說快,動作跟不上也不行。中間在聯系台詞時,自然有一些停頓或暗號。在OpenCore中,設置了一個主clock,而audio和video就分別以此作為輸出的依據。而在Stagefright中,audio的輸出是透過callback函式來驅動,video則根據audio的timestamp來做同步。在這之前,我們得了解下音頻相關playback過程:
Stagefright框架中,audio的部分是交由AudioPlayer來處理,它是在AwesomePlayer::play_l中被建立的。貼一段以前分析過的代碼:只不過當時沒有向AudioPlayer方向向下看
創建AudioPlayer
再接著看下startAudioPlayer_l函數,
接下來看下音頻mAudioPlayer->start(true)的操作,上面的過程都是在AwesomePlayer中,接下來變到AudioPlayer.cpp類中:
這裡首先要介紹一下mAudioSink ,當mAudioSink不為NULL的時候,AudioPlayer會將其傳入構造函數。
而且AudioPlayer中的播放操作都會依考mAudioSink來完成。
此處mAudioSink是從MediaPlayerService注冊而來的AudioOut對象。具體代碼在MediaPlayerservice中
間接地調用到stagefrightplayer->setAudioSink,最終到awesomeplayer中,如下:
而構造AudioPlayer時用到的就是mAudioSink成員,因此後面分析傳入的mAudioSink的操作時,記住實際的對象為AudioOut對象,在MediaPlayerService定義。
本文出自逆流的魚yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012
下面看AudioPlayer構造函數
主要是進行初始化,並將傳入的mAudioSink存在成員mAudioSink中
再回到上面的start函數中
總結如下:
剛介紹過mAudioSink是AudioOut對象,看下實際的實現(代碼在mediaplayerservice.cpp中)
首先mAudioSink->open 需要注意的是傳入的參數中有個函數指針 AudioPlayer::AudioSinkCallback ,其主要作用就是audioout播放pcm的時候會定期調用此回調函數填充數據,具體實現如下
以上代碼總結為:
1、處理傳入的參數,回調函數保存在mCallback中, cookie代表的是AudioPlayer對象指針類型,接下來是根據采樣率聲道數等計算 frameCount。 2、構造AudioTrack對象,並且賦值給t 3、將audiotrack對象存儲在mTrack成員中調用mTrack->start,audiotrack啟動後就會周期性的調用 回調函數從解碼器獲取數據.
本文出自逆流的魚yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012
回到我們前面的問題:音視頻如何同步?通過fillBuffer,不斷填充buffer。
代碼如下:
以上代碼總結為:當callback函數回調AudioPlayer讀取解碼後的數據時,AudioPlayer會取得兩個時間戳:mPositionTimeMediaUs和mPositionTimeRealUs,mPositionTimeMediaUs是數據裡面所持有的時間戳(timestamp);mPositionTimeRealUs則是播放此數據的實際時間(依據frame number及sample rate得出)。
以上代碼總結為:
在構造audioplayer的時候會執行mTimeSource = mAudioPlayer,二者的差值表示這一包pcm數據已經播放了多少。Stagefright中的video便依據從AudioPlayer得出來之兩個時間戳的差值,作為播放的依據
最後回到本文開頭的onVideoEvent方法中,
這樣最終音視頻數據通過渲染器就到Surface顯示畫面,就可看到視頻和聽到聲音了。
本文出自逆流的魚yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012
程序運行效果圖: 程序代碼: /** * 獲取所有軟件信息 * 1.通過異步的方式顯示系統中所有軟件 * 2.單擊打開指定軟件 * 3.將所有軟件的包名
今天給大家帶來2017年的第一篇文章,這裡先祝大家新年好。本篇文章的主題是ConstraintLayout。其實ConstraintLayout是AndroidStudi
[html]復制代碼 代碼如下:/** * 畫一個圓角圖 * 
記得在2013年12月的時候,有系列文章是介紹怎麼開發一個智能手表的App,讓用戶可以在足球比賽中記錄停表時間。隨著Android Wear的問世,在可穿戴設備中開發一款