由于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是如何计算出来的?
原来认为这个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了。