如何通过GStreamer在appsrc推送过慢时复制帧维持30fps?
问题:GStreamer管道如何补帧维持30fps RTMP流?
应用流程
- 接收RTSP流转OpenCV Mats的GStreamer管道:
rtspsrc location=rtsp://localhost:8554/looping-sample latency=500 protocols=udp buffer-mode=auto ! rtph264depay ! h264parse ! avdec_h264 max-threads=2 ! videoconvert ! video/x-raw,format=BGR ! appsink max-buffers=0 drop=true sync=false - 对Mats进行深度学习推理与绘制,耗时约100ms,导致输出帧率仅7-10fps
- 推送处理后帧到RTMP的GStreamer管道(OpenCV VideoWriter配置为30fps):
appsrc ! videoconvert ! nvh264enc bitrate=3000 ! h264parse ! flvmux ! rtmpsink location=test
当前问题
OpenCV VideoWriter设为30fps,但appsrc实际推送帧率仅7-10fps,导致媒体服务器无法识别为有效流。此前用OpenCV+FFMPEG时,VideoWriter会自动复制最后一帧维持帧率,虽卡顿但能正常工作。尝试添加videorate元素未生效,需实现自适应补帧维持30fps的功能。
可行解决方案
1. 正确配置videorate元素(需配合appsrc时间戳)
之前videorate未生效的核心原因是appsrc输出的帧没有携带正确的时间戳,GStreamer无法判断需要补多少帧。需做两步修改:
- 修改GStreamer输出管道,在appsrc后添加
videorate并明确帧率约束:appsrc ! videoconvert ! videorate ! video/x-raw,framerate=30/1 ! nvh264enc bitrate=3000 ! h264parse ! flvmux ! rtmpsink location=test - 在OpenCV中为appsrc设置时间戳:每次推送Mat时,计算当前帧的PTS(显示时间戳),确保时间戳严格按照30fps的间隔递增(每帧间隔约33.33ms),让videorate能根据时间戳自动补全缺失的帧。
2. 手动在OpenCV端补帧
如果不想依赖GStreamer的videorate,可在推理完成后手动计算需要补充的帧数:
- 记录上一帧推送的时间戳,计算当前帧与上一帧的时间差
- 根据30fps的要求,算出这段时间内应该推送的总帧数,减去当前帧,得到需要复制的帧数
- 循环复制当前Mat并推送到VideoWriter,直到达到预设帧率的帧数量
- 示例逻辑(伪代码):
long lastFrameTime = System.currentTimeMillis(); // 推理得到当前mat long currentTime = System.currentTimeMillis(); long timeDiff = currentTime - lastFrameTime; int expectedFrames = (int)(timeDiff / (1000.0/30)); // 推送当前帧 videoWriter.write(mat); // 补帧 for(int i=1; i<expectedFrames; i++){ videoWriter.write(mat); } lastFrameTime = currentTime;
3. 优化flvmux参数提升兼容性
部分媒体服务器对RTMP流的稳定性要求较高,可给flvmux添加streamable=true参数,降低流被判定为无效的概率:
appsrc ! videoconvert ! videorate ! video/x-raw,framerate=30/1 ! nvh264enc bitrate=3000 ! h264parse ! flvmux streamable=true ! rtmpsink location=test
内容的提问来源于stack exchange,提问作者Jan
相关产品推荐
相关产品推荐

