如何将GStreamer缓冲区时间戳与Linux系统时钟关联?
GStreamer-1.0 视频帧时间戳与Linux单调时钟关联问题
我正在使用GStreamer-1.0管道,通过v4l2src元件从摄像头读取实时视频并传入appsink元件,对appsink输出的图像做图像处理生成内容相关数据。同时应用另一部分从加速度计等传感器读取数据,传感器数据用Linux单调时钟时间戳,需要将其和同期处理的视频帧数据关联。
在视频处理线程中,我通过以下代码获取缓冲区时间戳:
... g_signal_emit_by_name(appsink, "pull-sample", &sample); buffer = gst_sample_get_buffer(sample); timestamp = GST_BUFFER_PTS(buffer); ...
但GST时间戳和系统时钟无关,似乎是管道进入PLAYING状态时的GST特定纪元偏移量。我想找更优的方案替代“读取帧时同步取系统时钟计算差值”,比如:
- 获取GST零时间戳对应的Linux单调时钟时间,将偏移量加到每个缓冲区时间戳上
- 用某个未发现的函数直接将缓冲区时间戳转换为系统时间戳
- 配置管道或元件使用系统单调时钟作为缓冲区时间戳
我尝试过几种无效方法:
- 获取系统时钟(
gst_system_clock_obtain)并设置clock-type为MONOTONIC - 调用
gst_pipeline_use_clock将上述时钟设为管道时钟 - 在v4l2src上调用
gst_base_src_set_do_timestamp(尝试过true和false)
请问有什么解决方案?还是只能在从appsink取缓冲区时同步读取系统时钟自行计算差值?
解决方案
方法一:利用GStreamer时钟的时间转换功能
GStreamer内置了管道时间戳到系统时钟的转换接口,无需自行计算偏移:
- 获取管道绑定的时钟:
GstClock *clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline));
- 将缓冲区PTS转换为Linux单调时钟时间(GST时间单位为纳秒):
GstClockTime buffer_pts = GST_BUFFER_PTS(buffer); GstClockTime system_monotonic_time; GstClockReturn ret = gst_clock_convert_time(clock, GST_CLOCK_TIME_NONE, buffer_pts, &system_monotonic_time);
转换后的system_monotonic_time就是对应Linux单调时钟的纳秒级时间戳,可直接与传感器时间关联。
方法二:配置管道使用系统单调时钟生成时间戳
如果想让GST缓冲区直接使用系统单调时钟纪元,需在管道进入PLAYING状态前完成配置:
- 获取并配置系统单调时钟:
GstClock *system_clock = gst_system_clock_obtain(); g_object_set(system_clock, "clock-type", GST_SYSTEM_CLOCK_MONOTONIC, NULL);
- 将该时钟绑定到管道:
gst_pipeline_use_clock(GST_PIPELINE(pipeline), system_clock); gst_object_unref(system_clock);
- 开启
v4l2src的时钟时间戳生成:
gst_base_src_set_do_timestamp(GST_BASE_SRC(v4l2src), TRUE);
此时GST_BUFFER_PTS(buffer)会直接对应Linux单调时钟的纳秒时间戳。之前尝试无效大概率是因为配置时机太晚,必须在管道启动前完成所有时钟设置。
备选方案:记录初始偏移量做转换
如果上述方法仍有问题,可在管道启动时记录GST纪元对应的系统时间,后续用偏移量转换:
- 监听管道状态变化,在进入PLAYING时记录偏移:
static void on_pipeline_state_changed(GstElement *pipeline, GstState old_state, GstState new_state, gpointer user_data) { if (new_state == GST_STATE_PLAYING) { GstClock *clock = gst_pipeline_get_clock(GST_PIPELINE(pipeline)); GstClockTime gst_zero = gst_clock_get_time(clock); struct timespec monotonic_now; clock_gettime(CLOCK_MONOTONIC, &monotonic_now); guint64 system_zero = monotonic_now.tv_sec * 1000000000ULL + monotonic_now.tv_nsec; // 保存偏移量:系统时间 = 缓冲区PTS + (系统零点 - GST零点) *(guint64*)user_data = system_zero - gst_zero; gst_object_unref(clock); } }
- 处理缓冲区时计算系统时间:
guint64 offset = *(guint64*)user_data; guint64 system_time = GST_BUFFER_PTS(buffer) + offset;
内容的提问来源于stack exchange,提问作者David C.
相关产品推荐
相关产品推荐

