1. 程式人生 > >Directshow中的Sample時間戳(Time Stamp)

Directshow中的Sample時間戳(Time Stamp)

每個Sample上可以設定兩種時間戳:IMediaSample::SetTime和IMediaSample::SetMediaTime。

我們通常講到時間戳,一般是指前者,它又叫Presentation time,Renderer正是根據這個時間戳來控制播放;而後者對於Filter來說不是必須的,Media time有沒有用取決於你的實現,比如你給每個發出去的Sample依次打上遞增的序號,在後面的Filter接收時就可以判斷傳輸的過程中是否有Sample丟失。

我們再看一下IMediaSample::SetTime的引數,兩個引數型別都是REFERENCE_TIME,千萬不要誤解這裡的時間是Reference time,其實它們用的是Stream time。還有一點,就是並不是所有的Sample都要求打上時間戳。對於一些壓縮資料,時間戳是很難打的,而且意義也不是很大(不過壓縮資料經過Decoder出來之後到達Renderer之前,一般都會打好時間戳了)。

時間戳包括兩個時間,開始時間和結束時間。當Renderer接收到一個Sample時,一般會將Sample的開始時間和當前的Stream time作比較,如果Sample來晚了或者沒有時間戳,則馬上播放這個Sample;如果Sample來得早了,則通過呼叫參考時鐘的IReferenceClock::AdviseTime等待Sample的開始時間到達後再將這個Sample播放。

Sample上的時間戳一般由Source Filter或Parser Filter來設定,設定的方法有如下幾種情況:

1. 檔案回放(File playback):第一個Sample的時間戳從0開始打起,後面Sample的時間戳根據Sample有效資料的長度和回放速率來定。

2. 音視訊捕捉(Video and audio capture):原則上,採集到的每一個Sample的開始時間都打上採集時刻的Stream time。對於視訊幀,Preview pin出來的Sample是個例外,因為如果按上述方法打時間戳的話,每個Sample通過Filter鏈路傳輸,最後到達Video Renderer的時候都將是遲到的;Video Renderer通過Quality Control反饋給Source Filter,會導致Source Filter丟幀。所以,Preview pin出來的Sample都不打時間戳。對於音訊採集,需要注意的是,Audio Capture Filter與音效卡驅動

程式兩者各自使用了不同的快取,採集的資料是定時從驅動程式快取拷貝到Filter的快取的,這裡面有一定時間的消耗。

  3. 合成(Mux Filters):取決於Mux後輸出的資料型別,可以打時間戳,也可以不打時間戳。

  大家可以看到,Sample的時間戳對於保證音視訊同步是很重要的。Video Renderer和Audio Renderer作為音視訊同步的最終執行者,需要做很多工作。我們或許要開發其它各種型別的Filter,但一般這兩個Filter是不用再開發的。一是因為Renderer Filter本身的複雜性,二是因為微軟會對這兩個Filter不斷升級,整合DirectX中其它模組的最新技術(如DirectSound、DirectDraw、Direct3D等)。

  最後,我們再來仔細看一下Live Source的情況。Live Source又叫Push source,包括Video /Audio Capture Filter、網路廣播接收器等。Filter Graph Manager是如何知道一個Filter是Live Source的呢?通過如下任何一個條件判斷:

  1. 呼叫Filter上的IAMFilterMiscFlags::GetMiscFlags返回有AM_FILTER_MISC_FLAGS_IS_SOURCE標記,並且至少有一個Output pin實現了IAMPushSource介面。

  2. Filter實現了IKsPropertySet介面,並且有一個Capture output pin(Pin的型別為PIN_CATEGORY_CAPTURE)。

  Live Source對於音視訊同步的影響主要是以下兩個方面:Latency和Rate Matching。Latency是指Filter處理一個Sample花費的時間,對於Live Source來說,主要取決於使用快取的大小,比如採集30fps的視訊一般採集完一幀後才將資料以一個Sample傳送出去,則這個Filter的Latency為33ms,而Audio一般快取500ms後才傳送一個Sample,則它的Latency就為500ms。這樣的話,Audio與Video到達Renderer就會偏差470ms,造成音視訊的不同步。預設情況下,Filter Graph Manager是不會對這種情況進行調整的。當然,應用程式可以通過IAMPushSource介面來進行Latency的補償,方法是呼叫IAMGraphStreams::SyncUsingStreamOffset函式。Filter Graph Manager的實現如下:對所有實現IAMPushSource介面的Filter呼叫IAMLatency::GetLatency得到各個Source的Latency值,記下所有Latency值中的最大值,然後呼叫IAMPushSource::SetStreamOffset對各個Source設定偏移值。

  這樣,在Source Filter產生Sample時,打的時間戳就會加上這個偏移量。Rate Matching問題的引入,主要是由於Renderer Filter和Source Filter使用的是不同的參考時鐘。這種情況下,Renderer對資料的播放要麼太快,要麼太慢。另外,一般Live Source不能控制輸出資料的速率,所以必須在Renderer上進行播放速率的匹配。因為人的聽覺敏感度要大於視覺敏感度,所以微軟目前只在Audio Renderer上實現了Rate Matching。實現Rate Matching的演算法是比較複雜的,這裡就不再贅述。

  看到這裡,大家應該對DirectShow是如何解決音視訊同步問題的方案有一點眉目了吧。深層次的研究,還需要更多的測試、Base class原始碼閱讀,以及DirectShow相關控制機制的理解,比如Quality Control Management等