原帖地址:http://blog.csdn.net/coroutines/article/details/7472743
VLC版本2.0.1
最近研究IP-STB音視頻同步問題,發現方案自帶的自動STC在網絡延時過大時,不能成功同步音視頻。在參考了VLC的串流播放機制后,以為適當緩沖可以解決此問題,可惜最終結果是稍有緩解,並不能從根本上解決。但這種緩沖時間的計算,對於基於網絡這種音視頻數據的注入有一定的參考意義。
1、最初的線索:
使用 -vvv 參數 啟動VLC時,提示:
命令行啟動vlc: ./vlc.exe -vvv
[0xb73005f0] main input debug: Buffering 0%
[0xb73005f0] main input debug: Buffering 3%
[0xb73005f0] main input debug: Buffering 6%
[0xb73005f0] main input debug: Buffering 9%
[0xb73005f0] main input debug: Buffering 12%
[0xb73005f0] main input debug: Buffering 15%
[0xb73005f0] main input debug: Buffering 18%
[0xb73005f0] main input debug: Buffering 21%
[0xb73005f0] main input debug: Buffering 24%
[0xb73005f0] main input debug: Buffering 27%
可以查找到這個緩沖過程的打印是從src/input/es_out.c:658行輸出的。函數EsOutDecodersStopBuffering的作用是判斷當前緩沖流的長度i_stream_duration是否達到預置的i_buffering_duation長度,是則停止緩沖,否則返回等待。
接下來是查i_stream_duration和i_buffering_duation如何計算。
2、i_stream_duration的計算:
在EsOutDecodersStopBuffering中,i_stream_duration是通過input_clock_GetState來獲取。而input_clock_GetState是取一個類型為input_clock_t對象的倆個clock_point_t對象成員last和ref的i_stream的差值作為已緩沖數據的長度。
繼續查找i_stream值的計算,可以發現i_stream只有在clock_point_Create中進行的設置,而last和ref對象的i_stream值是在input_clock_Update中做了更新,而用於更新i_stream的數據由i_ck_stream傳入。input_clock_Update函數的調用位於es_out.c:2324行,該段代碼用於設置input_clock_t對象的pcr值,上層傳入的命令是ES_OUT_SET_GROUP_PCR,傳入此命令的函數只有一個ts.c:2173行的PCRHandle。
查PCRHandle的代碼可知,pcr的計算是直接從ts包中獲取adaption字段的pcr數據,按照90kHz的時鍾轉換為秒,並乘以1000000換算成微秒,傳遞給input_clock_Update,到此完成了i_stream_duration的計算。
3、i_buffering_duration的計算:
在EsOutDecodersStopBuffering中,i_buffering_duration是一個es_out_sys_t對象的i_pts_delay屬性再加上另外幾個參數來確定。這里我只研究了i_pts_delay的獲取,在我的測試中,其余幾個值均為0。
es_out_sys_t對象是EsOutDecodersStopBuffering的es_out_t型參數out傳入的,回溯代碼的調用過程,EsOutDecodersStopBuffering <-EsOutControlLocked <-EsOutControl <-es_out_vaControl <-es_out_control <-CmdExecuteControl,在這里out換成了另一個es_out_t型參數p_out,繼續回溯,CmdExecutedControl <-ControlLocked < -Control <-es_out_vaControl <-es_out_Control <-PCRHandle,這里的p_out是demux_t型參數p_demux的成員,PCRHandle <-GatherPES <-Demux <-demux_Demux <-MainLoopDemux,這里的p_demux是input_thread_t型對象p_input成員,再回溯,MainLoopDemux <-MainLoop <-Run,p_input在input.c:550行通過Init函數初始化。
再讀Init部分代碼,可以發現在input.c:1258行InitSourceInit函數后,i_pts_delay的值發生變化,往下深入,會發現在input.c:2644行,有一個ACCESS_GET_PTS_DELAY操作,最終對應到我的測試中是udp.c:173行,VLC獲取了一個叫做:network-caching參數的值,這個值配置的是1000,我猜測應該是ms,返回時又乘以了1000,轉換為微秒,准備與i_stream_duration值進行比較。
總結:
這次測試只做了基於udp的組播播放,其它協議可能還有許多細節與udp不同,等到遇到時再進行分析。
讀了許久的TS協議,在實際應用中才發現許多東西跟想像的都不同,脫離實際的空想終究是不能成事的。
VLC這個基於PCR的緩沖長度的機制對於我解決不同步的問題,雖沒有大的幫助,但應用到播放不同碼率的網絡串流方面,我想會有一些作用。假設我們開固定大小的緩沖,由於數據傳輸效率上的問題,經常會遇到解碼器緩沖區上溢或下溢的問題,但如果我采用基於時間的緩沖,應該會有一定的改善,只是不知這種分析實時PCR值的方法是否引入了又一個瓶頸。