(Original: http://blog.csdn.net/niehanzi/article/details/6614841)
Gstreamer多路流同步(翻譯自part-synchronisation.txt)
該篇文檔描述了gstreamer多路流的同步技術的概要。
GstPipeline的同步用如下的3個組件完成:
1)GstClock,在GstPipeline中對所有元素來說是全局的。
2)GstBuffer的Timestamps。
3)在發送buffer(數據流)之前的NEW_SEGMENT。
A GstClock
~~~~~~~~~~
GstClock單位是納秒,一個計數器,描述了當前的時間,該值也是絕對時間。
如下的幾個源可以提供時間計數器:
1)系統時間,微妙級的精度,用g_get_current_time函數獲取。
2)音頻設備(基於採樣率)。
3)一個網絡源,例如RTP源,RTP包中帶有時間戳。
......
在Gstreamer中,任何元素提供的GstClock對象都可以用到pipeline中。GstPipeline對象將從時鐘提供者中選擇一個分發給所有其它的元素。
GstClock總是向上遞增的,不一定要從0開始。
Running time
~~~~~~~~~~~~
當pipeline選擇了一個時鐘,它將基於選定的時鐘維護running_time,running_time表示處於PLAYING狀態的總的時間,按照如下方式計算:
1)如果pipeline的狀態是NULL/READY,running_time是未定義的。
2)在PAUSED狀態,running_time保持在暫停的時間。如果pipeline是第一次暫停,running_time是0。
3)在PLAYING狀態,running_time是absolute_time和base_time的差值。當處於PLAYING狀態時, base_time = absolute_time - running_time。
4)在執行了flushing seek,running_time被設置成0,base_time被重新設置,相當於重新開始。
當pipeline從PLAYING -> PAUSED狀態時,保存住running_time;當恢復成PLAYING狀態時,重新開始計算running_time。當pipeline是PAUSED狀態時,不論選定時鐘是否繼續穩步增加,都不會影響到running_time的計算。
時鐘和pipeline用running_time爲所有的元素進行同步。在PLAYING狀態下,running_time能夠在每一個元素中被觀察到,計算公式如下:
C.running_time = absolute_time - base_time
C.running_time也就是running_time,該值以時鐘的速率單調遞增。
Timestamps
~~~~~~~~~~
GstBuffer timestamps和在buffer(數據流)之前的NEW_SEGMENT event定義了一個轉換從buffer timestamps到running_time,計算如下:
定義:
B.running_time:running_time
B: GstBuffer
- B.timestamp:buffer timestamp (GST_BUFFER_TIMESTAMP)
NS: NEWSEGMENT event
- NS.start: start field in the NEWSEGMENT event
- NS.stop: stop field in the NEWSEGMENT event
- NS.rate: rate field of NEWSEGMENT event
- NS.abs_rate: absolute value of rate field of NEWSEGMENT event
- NS.time: time field in the NEWSEGMENT event
- NS.accum: total accumulated time of all previous NEWSEGMENT events. This field is kept in the GstSegment structure.
滿足B.timestamp >= NS.start && B.timestamp <= NS.stop的GstBuffer實例是有效的,在這個範圍之外的其它buffer將被丟棄。
B.timestamp到B.running_time的轉換公式如下:
if (NS.rate > 0.0)
B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
else
B.running_time = (NS.stop - B.timestamp) / NS.abs_rate + NS.accum
由於B.running_time就是running_time,所以running_time是從NEWSEGMENT event和buffer而來的。
所以根據第一個有效地buffer計算出來的running_time爲0,既然B.timestamp == NS.start and NS.accum == 0。
Synchronisation
~~~~~~~~~~~~~~~
我們可以用如下方法得到running_time:
1)用選定的時鐘和元素的base_time:
C.running_time = absolute_time - base_time
2)用buffer timestamp和之前的NEWSEGMENT event(假定播放速率爲正):
B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
running_time的前綴C和B表示兩種不同的計算方法。
同步播放就是要確保:當播放一個時間戳是B.running_time的buffer時,時鐘必須達到了C.running_time,也就是說當:
B.running_time = C.running_time時,
播放buffer裏面的數據。因此可以擴展到如下的公式:
B.running_time = absolute_time - base_time
或者
absolute_time = B.running_time + base_time
記B.sync_time爲buffer應該被播放時的絕對時間,因此得到:
B.sync_time = B.running_time + base_time
對於多路流來說,擁有同樣的running_time的buffers將被同時播放。demuxer元素在它的所有的src pad上發送NEWSEGMENT消息來確保同步的buffer具有同樣的時間戳。
Stream time
~~~~~~~~~~~
Stream time是流的位置,值介於0和媒體文件的長度(時間爲單位)。有如下作用:
1)POSITION查詢
2)seek事件中的position的設置
3)the position used to synchronize controller values
可以按照如下的方式來計算stream time,推導過程如下:
C.running_time = absolute_time - base_time
B.running_time = (B.timestamp - NS.start) / NS.abs_rate + NS.accum
=>
(B.timestamp - NS.start) / NS.abs_rate + NS.accum = absolute_time - base_time;
=>
(B.timestamp - NS.start) / NS.abs_rate = absolute_time - base_time - NS.accum;
=>
(B.timestamp - NS.start) = (absolute_time - base_time - NS.accum) * NS.abs_rate
又因爲
stream_time = (B.timestamp - NS.start) * NS.abs_applied_rate + NS.time
=>
stream_time = (absolute_time - base_time - NS.accum) * NS.abs_rate * NS.abs_applied_rate + NS.time
最後得到的公式常被用來在sink元素中查詢當前播放的位置。
需要注意的是:stream time從來沒有參與多路流的時鐘同步。
-----------------------------------------------------------------------------------------------------------------------------------------
終於翻譯完這篇了。