天天看點

GStreamer如何擷取播放的duration和目前的播放position?GStreamer pipeline的basetime是如何計算出來的?

由于gstreamer在seek時總會出現崩潰或其他bug,這裡把與seek相關的點做一下筆記

www.cnblogs.com/super119/archive/2011/01/03/1924441.html

1. 擷取duration的話,有幾個途徑:

(1)在bus上監聽GST_MESSAGE_DURATION,如果有這個消息到來,用gst_message_parse_duration就可以獲得。但是往往element不會發這樣的message出來

(2)用gst_query_new_duration建立一個duration query,然後用gst_element_query(pipeline, query)查詢即可。這種方法較為常用。一般可以在pipeline所有element都link好了,pipeline狀态是RUNNING的時候,就可以查詢了。 

2. 擷取目前播放的position,也有幾種辦法:

(1)如果pipeline中element支援GST_QUERY_POSITION,那麼可以進行查詢得到。一般來說都是sink element提供position查詢,因為資料從src開始,一路經過demux,decode,這中間都需要消耗時間,是以如果是非sink element提供position的話,那是不準的。

(2)如果不能query,那我現在的做法是:在sink element上add event和buffer probe。在event probe中監聽NEWSEGMENT event,一般剛開始播放的話,NEWSEGMENT中的start都是0。收到該event做一些相應的動作;然後在buffer probe中将第一個buffer的timestamp記錄下來,然後針對後續的buffer,就可以和第一個buffer的timestamp做減法,得到內插補點除以duration,就是目前的position。注意如果播放中途收到NEWSEGMENT event,那麼,第一個buffer的timestamp要重新進行計算,然後最後計算position的時候記得加上NEWSEGMENT中的start,再去除以duration,這才正确。

問題2

GStreamer pipeline的basetime是如何計算出來的?

GstPipeline在從PAUSED轉成PLAYING的時候,會select一個clock并計算出basetime,這兩樣東西都會賦給pipeline中每個element。那這個basetime每次是怎麼計算的呢? 

原來認為這個basetime就是目前的clock time,但是後來發現不對。比如:在播放了2秒的時候PAUSE,等了3秒後再PLAY,此時如果這個basetime是目前時間的話,那就是5秒時刻,此時繼續播放的話,會發現傳過來的buffer中的timestamp是從第3秒開始的,不是從0秒開始的,因為上次暫停的時候是在2秒的時間點上。是以如果此時basetime是5秒的話,那麼sink元件就要到8秒以後再播放這些buffer(因為buffer的timestamp是從3秒開始的)。 

于是回到gst_pipeline_change_state函數研究,發現其實很簡單: 

base_time = start_time - stream_time + delay; 

start_time就是目前clock的時間,stream_time就是已經播放的時間(注意和running_time不一樣,running_time是變成PLAYING狀态後流逝的時間,而stream_time是變成PLAYING狀态後sink實際播放的時間,是以stream_time一般就直接用在POSITION的QUERY上,作為POSITION直接傳回的(當然,這裡還有一些邏輯,不是直接傳回stream time的,詳情參考gst_base_sink_get_position函數)),delay是GstPipeline的一個property,可以設定的。 

這樣一來就對了,還是上面的例子,先播了2秒,是以stream_time是2秒,等了3秒,到第5秒的時候play,是以start_time是5,這樣算下來base_time就是3秒(如果delay設定是0的話,預設值就是0),是以接下來的buffer timestamp是3秒開始的,這樣就正好接上播放了。 

如果是第一次播放,之前沒有PAUSE過,那麼上面的公式很清楚,此時basetime就正好是start time了。

繼續閱讀