从GStreamer获取的PTS与保存的TS文件中PTS不匹配问题排查
问题背景
我用Rust实现了一个GStreamer管道,拉取摄像头RTSP流并做AI目标检测,简化后的管道如下:
rtspsrc latency=100 location=rtsp://<rtspurl> protocols=0x4 name=basesrc basesrc. ! rtph264depay ! tee name=t t. ! queue ! vaapidecodebin name=video_decoder ! tee name=decode_t decode_t. ! queue ! videorate ! video/x-raw,framerate=15/1 ! vaapipostproc format=nv12 ! video/x-raw,width=2880,height=1620 ! vaapih265enc ! h265parse config-interval=-1 ! hlssink2 playlist-length=5 max-files=0 target-duration=10 send-keyframe-requests=true program-date-time=true playlist-location=manifest.m3u8 location=video/%t.ts decode_t. ! queue ! videorate ! video/x-raw,framerate=3/1 ! vaapipostproc format=nv12 ! video/x-raw,width=640,height=384 ! videoconvert ! objectdetector ! fakesink async=false
其中objectdetector是我开发的自定义插件,在transform_frame_ip()中通过gst_video::VideoFrameRef<&mut gst::BufferRef>获取PTS:
let pts = frame.buffer().pts().expect("Buffer must have a PTS value.");
但用ffprobe查看hlssink生成的TS分段文件时,得到的PTS和插件中获取的完全不同,ffprobe命令:
ffprobe -select_streams v -show_frames -of csv -show_entries frame=coded_picture_number,key_frame,pict_type,pts,pts_time -i <>
需要匹配两者的PTS以对应实际播放时间,方便在浏览器播放manifest时绘制目标检测框,请问问题出在哪?
核心原因与解决方法
1. PTS时间基准不统一
GStreamer中每个Pad/Element的PTS基于自身的时间基准(Clock Time Base),而ffprobe显示的TS文件PTS基于TS容器的标准时间基准(通常为90kHz,即1/90000秒),两者刻度不同,直接数值对比无意义。
解决方式:
- 在自定义插件中,将PTS转换为秒级绝对时间,或转换为与HLSSink输出一致的时间基准,再与ffprobe输出的
pts_time字段对比。
2. 转换PTS为统一时间单位
在objectdetector插件中,获取当前Pad的时间基准,将PTS转换为秒级时间:
// 获取当前Buffer所属Pad的时间基准 let time_base = frame.buffer().pad().unwrap().current_time_base(); // 将PTS转换为秒级浮点数 let pts_seconds = pts.to_nseconds() as f64 / time_base.denom() as f64;
此时得到的pts_seconds可以直接和ffprobe输出的pts_time字段匹配,用于对应播放时间。
3. 对齐HLSSink的墙钟时间
由于你设置了program-date-time=true,HLSSink会将流时间戳转换为实际墙钟时间写入播放列表和TS文件。你可以在插件中同样将PTS转换为墙钟时间,和HLSSink输出对齐:
// 获取管道时钟 let clock = element.clock().unwrap(); // 将流PTS转换为系统绝对时间 let absolute_time = clock.convert_stream_time_to_system_time(time_base, pts); // 转换为RFC3339格式,和HLSSink的program-date-time输出格式一致 let time_str = absolute_time.to_rfc3339();
这个时间字符串可以直接对应浏览器播放时的实际时间点,用于绘制检测框。
4. 同步分支时钟避免偏移
你的检测分支使用了fakesink async=false,会导致该分支脱离管道同步时钟,可能造成时间戳和编码分支偏移。建议:
- 移除
async=false,让检测分支与主编码分支共享管道时钟,确保时间基准一致。 - 若必须保留
async=false,需手动同步两个分支的时间基准,比如通过总线消息传递时间基准参数。
内容的提问来源于stack exchange,提问作者Gatothgaj

