Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Multimedia框架總結(十)Stagefright框架之音視頻輸出過程

Android Multimedia框架總結(十)Stagefright框架之音視頻輸出過程

編輯:關於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把音視頻進行分開,那音頻呢?誰來讓他們保持同步的呢?

音頻數據到Buffer過程

無論是音頻也好,還是視頻,都是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在AwesomePlayer運行過程

下面看AudioPlayer構造函數

這裡寫圖片描述

主要是進行初始化,並將傳入的mAudioSink存在成員mAudioSink中
再回到上面的start函數中
總結如下:

調用mSource->read 啟動解碼,解碼第一幀相當於啟動了解碼循環 獲取音頻參數:采樣率、聲道數、以及量化位數(這裡只支持PCM_16_BIT) 啟動輸出:這裡若mAudioSink非空,則啟動mAudioSink進行輸出,否則構造一個AudioTrack進行音頻輸出,這裡AudioTrack是比較底層的接口 AudioOut是AudioTrack的封裝。 在start方法中主要是調用mAudioSink進行工作,主要代碼如下:

這裡寫圖片描述

剛介紹過mAudioSink是AudioOut對象,看下實際的實現(代碼在mediaplayerservice.cpp中)

首先mAudioSink->open 需要注意的是傳入的參數中有個函數指針 AudioPlayer::AudioSinkCallback ,其主要作用就是audioout播放pcm的時候會定期調用此回調函數填充數據,具體實現如下

這裡寫圖片描述

以上代碼總結為:

1、處理傳入的參數,回調函數保存在mCallback中, cookie代表的是AudioPlayer對象指針類型,接下來是根據采樣率聲道數等計算 frameCount。 2、構造AudioTrack對象,並且賦值給t 3、將audiotrack對象存儲在mTrack成員中
當以上過程完成後,繼續分析AudioPlayer.start函數時,最後都會實例化一個AudioTrack對象,然後獲取幀大小,比特等信息,然後調用mAudioTrack.start,最後到達mediaplayerservice音頻輸出start函數。

這裡寫圖片描述

調用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,
即將AudioPlayer作為參考時鐘, 上述代碼中成員變量mSeekTimeUs是由如下語句獲得:CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs)); realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs; 當顯示畫面是第一幀時,表示當前的audio的播放時間與第一幀video的時間差值 其中變量是通過mAudioPlayer->getMediaTimeMapping( int64_t *realtime_us, int64_t *mediatime_us) {
Mutex::Autolock autoLock(mLock)得到

這裡寫圖片描述

二者的差值表示這一包pcm數據已經播放了多少。Stagefright中的video便依據從AudioPlayer得出來之兩個時間戳的差值,作為播放的依據

音視頻輸出

最後回到本文開頭的onVideoEvent方法中,

這裡寫圖片描述

這樣最終音視頻數據通過渲染器就到Surface顯示畫面,就可看到視頻和聽到聲音了。
本文出自逆流的魚yuiop:http://blog.csdn.net/hejjunlin/article/details/52560012

一張圖看音視頻輸出

這裡寫圖片描述

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved