You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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()

核心修改说明

  1. 调整VideoCapture初始化位置:移到__init__方法,避免在回调里重复创建,同时提前检查是否能打开视频源。
  2. 修复回调函数的退出逻辑:移除循环内的return,改用is_pushing标志控制推送状态,配合enough-data回调暂停推送,避免帧堆积。
  3. 添加帧率控制:用time.sleep(1/self.fps)匹配视频实际帧率,避免推送过快。
  4. 调整GStreamer管线参数:将udpsink的sync=true改为sync=false,减少延迟;appsrc的block=false更适合实时流场景。
  5. 添加资源释放:在类的__del__方法里释放VideoCapture和GStreamer管线资源。

内容的提问来源于stack exchange,提问作者VivaFidel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 23:47:48