使用GStreamer通过RTSP转发视频流仅显示首帧的问题求助
问题分析与解决方案
你的代码只显示第一帧的核心问题是**start_camera回调函数在推送第一帧后就直接return退出了**,导致后续帧无法继续推送。另外还有VideoCapture初始化位置不当、帧率控制缺失等问题,以下是具体修改方案:
关键问题点
start_camera里的return True写在了while循环内部,推送一帧就终止函数,不再处理后续帧cv2.VideoCapture在回调里初始化,可能被多次触发重复创建- 没有控制帧推送的间隔,可能导致帧堆积或不同步
修改后的完整代码
import cv2 import gi import time gi.require_version('Gst', '1.0') gi.require_version('GstRtspServer', '1.0') from gi.repository import Gst, GstRtspServer, GLib Gst.init(None) class Camera: def __init__(self): self.camera_url = 'RTSP link/video path' # 初始化VideoCapture,放在__init__避免重复创建 self.cap = cv2.VideoCapture(self.camera_url) if not self.cap.isOpened(): raise Exception(f"无法打开视频源: {self.camera_url}") # 获取视频实际帧率,避免硬编码(可选,更灵活) self.fps = self.cap.get(cv2.CAP_PROP_FPS) or 12 self.duration = 1 / self.fps * Gst.SECOND self.number_frames = 0 # 调整appsrc参数,block=false更适合实时流 self.pipe = "appsrc name=source is-live=true block=false format=GST_FORMAT_TIME " \ " caps=video/x-raw,format=BGR,width=1280,height=720,framerate={}/1 " \ "! videoconvert ! video/x-raw,format=I420 ! x264enc speed-preset=ultrafast tune=zerolatency " \ "! rtph264pay config-interval=1 name=pay0 pt=96 " \ "! udpsink host=127.0.0.1 port=5400 sync=false async=false".format(self.fps) self.pipeline = Gst.parse_launch(self.pipe) self.loop = None appsrc = self.pipeline.get_by_name('source') # 改用need-data回调控制推送 appsrc.connect('need-data', self.push_frame) # 添加enough-data回调暂停推送,避免堆积 appsrc.connect('enough-data', self.pause_push) self.is_pushing = False def create_rtsp_server(self): server = GstRtspServer.RTSPServer.new() server.set_property("service", "8554") server.attach(None) factory = GstRtspServer.RTSPMediaFactory.new() # 修正udpsrc的caps,确保和发送端匹配 pipeline = "( udpsrc port=5400 name=pay0 caps=\"application/x-rtp, media=video, clock-rate=90000, encoding-name=H264, payload=96 \" )" factory.set_launch(pipeline) factory.set_shared(True) server.get_mount_points().add_factory("/stream", factory) def run(self): self.pipeline.set_state(Gst.State.PLAYING) self.create_rtsp_server() self.loop = GLib.MainLoop() self.loop.run() def push_frame(self, src, length): if self.is_pushing: return self.is_pushing = True try: while self.is_pushing: ret, frame = self.cap.read() if not ret: # 视频结束或出错,发送EOS src.emit('end-of-stream') break # 控制帧率,匹配视频实际速度 time.sleep(1/self.fps) data = frame.tobytes() buf = Gst.Buffer.new_allocate(None, len(data), None) buf.fill(0, data) buf.duration = self.duration timestamp = self.number_frames * self.duration buf.pts = buf.dts = int(timestamp) buf.offset = timestamp self.number_frames += 1 retval = src.emit('push-buffer', buf) if retval != Gst.FlowReturn.OK: print(f"推送失败: {retval}") self.is_pushing = False except Exception as e: print(f"推送帧出错: {e}") self.is_pushing = False def pause_push(self, src): self.is_pushing = False def __del__(self): # 释放资源 if hasattr(self, 'cap'): self.cap.release() if hasattr(self, 'pipeline'): self.pipeline.set_state(Gst.State.NULL) if __name__ == "__main__": cam = Camera() cam.run()
核心修改说明
- 调整VideoCapture初始化位置:移到
__init__方法,避免在回调里重复创建,同时提前检查是否能打开视频源。 - 修复回调函数的退出逻辑:移除循环内的
return,改用is_pushing标志控制推送状态,配合enough-data回调暂停推送,避免帧堆积。 - 添加帧率控制:用
time.sleep(1/self.fps)匹配视频实际帧率,避免推送过快。 - 调整GStreamer管线参数:将
udpsink的sync=true改为sync=false,减少延迟;appsrc的block=false更适合实时流场景。 - 添加资源释放:在类的
__del__方法里释放VideoCapture和GStreamer管线资源。
内容的提问来源于stack exchange,提问作者VivaFidel
相关产品推荐
相关产品推荐

