GStreamer如何獲取播放的duration和當前的播放position?
由於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
原來認為這個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了。