OpenCV处理帧后用GStreamer推RTSP流无法连接及自定义流名问题
故障表现
- 测试代码运行无报错,但输出的RTSP流无法正常访问
- VLC播放器无法建立连接,本地执行如下拉流命令时返回连接错误:
gst-launch-1.0 rtspsrc location=rtsp://localhost:8554 latency=100 ! queue ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw,width=640,height=480 ! Xvimagesink
返回错误信息:
Setting pipeline to PAUSED ...
Pipeline is live and does not need PREROLL ...
Progress: (open) Opening Stream
Progress: (connect) Connecting to rtsp://localhost:8554
ERROR: from element /GstPipeline:pipeline0/GstRTSPSrc:rtspsrc0: Could not open resource for reading and writing.
Additional debug info:
gstrtspsrc.c(7469): gst_rtspsrc_retrieve_sdp (): /GstPipeline:pipeline0/GstRTSPSrc:rtspsrc0:
Failed to connect. (Generic error)
ERROR: pipeline doesn't want to preroll.
原有实现流程
- 获取相机帧格式参数
- 创建视频捕获实例
- 获取本机当前IP地址
- 配置RTSP推流管道字符串
- 创建OpenCV VideoWriter实例
- 循环读取相机帧,执行YOLO检测处理
- 本地窗口展示处理结果
- 将处理后的帧写入RTSP流
核心需求
- 修复RTSP流无法连接的问题
- 支持自定义流名称配置,实现形如
rtsp://192.168.1.158:8554/test的带路径RTSP访问地址
原有问题代码
import subprocess as sb import shlex as sh import cv2 import socket import time def get_frame_format(): command = 'v4l2-ctl -d /dev/video0 --list-formats-ext' args = sh.split(command) p = sb.Popen(args, stdout=sb.PIPE, stderr=sb.STDOUT) res = p.communicate()[0].split() fps = float(res[-2].decode('utf-8')[1:]) size = list(map (int , res[19].decode('utf-8').split('x'))) width = size[0] height = size[1] return fps, width, height fps, width, height = get_frame_format() print (f"fps: {fps}, {type(fps)}\n, size: {width} {height}") window_title = "USB Camera" camera_id = "/dev/video0" cam = cv2.VideoCapture(camera_id, cv2.CAP_V4L2) #Get current IP for RTSP s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) ip_address = s.getsockname()[0] pipe_out = 'appsrc is-live=True ! queue ! videoconvert ! video/x-raw, format=RGBA ! nvvidconv\ ! omxh264enc insert-sps-pps=true ! video/x-h264, stream-format=byte-stream! h264parse \ ! rtph264pay name=pay0 pt=96 config-interval=1 ! udpsink port=8554 host=192.168.1.158' rtsp_out = cv2.VideoWriter(pipe_out, fourcc=0, apiPreference=cv2.CAP_GSTREAMER, fps=fps, frameSize=(1980, 1080), isColor=True) time.sleep(2) if not rtsp_out.isOpened() : print("Writer failed") exit() print('Writer opened') if cam.isOpened(): try: window_handle = cv2.namedWindow(window_title, cv2.WINDOW_KEEPRATIO ) while True: ok, frame = cam.read() # Here frame neuro processing by yolo # Showing result on local machine if cv2.getWindowProperty(window_title, cv2.WND_PROP_AUTOSIZE) >=0: frame_out = cv2.resize(frame, (1980, 1080)) cv2.imshow(window_title, frame_out) # result streaming to rtsp rtsp_out.write(frame_out) else: break key = cv2.waitKey(1) & 0xFF if key == 27 or key == ord("q"): break finally: rtsp_out.release() cam.release() cv2.destroyAllWindows() else: print("Camera doesn't open")
问题根因
- 核心逻辑错误:原管道末端使用
udpsink直接发送裸UDP RTP包,没有启动RTSP服务端。RTSP协议需要专门的服务组件处理客户端的握手、SDP协商、流地址映射等流程,直接往端口发UDP流时,客户端发起的RTSP连接请求没有服务响应,必然报连接失败。 - 格式不匹配:管道中指定输入帧格式为
RGBA,但OpenCV通过GStreamer appsrc输出的帧默认是BGR格式,会导致编码异常。 - 参数硬编码:管道中硬编码了主机IP、输出分辨率为1980x1080,和之前动态获取的本机IP、相机参数不匹配,容易出现兼容性问题。
修复方案
前置依赖安装
先安装GStreamer RTSP服务组件,以Ubuntu/Jetson平台为例:
sudo apt update sudo apt install -y gstreamer1.0-rtsp-server gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly python3-gi
如果使用Jetson平台的硬件编码器,确认nvvidconv、omxh264enc插件可用;x86平台可替换为x264enc软编码。
修复后可运行代码
代码中通过GstRtspServer创建RTSP服务,自定义挂载路径为/test,最终访问地址为rtsp://[本机IP]:8554/test,同时修正了帧格式、参数硬编码问题:
import cv2 import socket import gi gi.require_version('Gst', '1.0') gi.require_version('GstRtspServer', '1.0') from gi.repository import Gst, GstRtspServer, GLib import threading # 初始化GStreamer Gst.init(None) # 可自定义配置参数 RTSP_PORT = 8554 STREAM_PATH = "/test" CAMERA_ID = "/dev/video0" OUTPUT_WIDTH = 1980 OUTPUT_HEIGHT = 1080 FPS = 30 # 获取本机IP s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.connect(("8.8.8.8", 80)) LOCAL_IP = s.getsockname()[0] s.close() print(f"RTSP访问地址: rtsp://{LOCAL_IP}:{RTSP_PORT}{STREAM_PATH}") # 构建编码管道,Jetson硬件编码版本,x86可替换nvvidconv/omxh264enc为videoconvert/x264enc PIPELINE = f""" appsrc name=source is-live=true block=true format=GST_FORMAT_TIME caps=video/x-raw,format=BGR,width={OUTPUT_WIDTH},height={OUTPUT_HEIGHT},framerate={FPS}/1 ! queue ! videoconvert ! video/x-raw,format=I420 ! nvvidconv ! omxh264enc insert-sps-pps=true bitrate=4000000 ! h264parse ! rtph264pay name=pay0 pt=96 config-interval=1 """ # 定义RTSP媒体工厂 class CameraFactory(GstRtspServer.RTSPMediaFactory): def __init__(self): super().__init__() self.set_shared(True) # 支持多客户端同时拉流 self.appsrc = None self.set_launch(PIPELINE) self.frame_count = 0 def do_configure(self, rtsp_media): self.appsrc = rtsp_media.get_element().get_by_name("source") def write_frame(self, frame): if not self.appsrc: return data = frame.tobytes() buf = Gst.Buffer.new_wrapped(data) buf.duration = Gst.SECOND // FPS buf.pts = buf.dts = Gst.util_uint64_scale(self.frame_count, Gst.SECOND, FPS) self.appsrc.emit("push-buffer", buf) self.frame_count += 1 # 启动RTSP服务 server = GstRtspServer.RTSPServer() server.set_service(str(RTSP_PORT)) mounts = server.get_mount_points() factory = CameraFactory() mounts.add_factory(STREAM_PATH, factory) server.attach(None) # 启动GLib主循环(后台运行) loop = GLib.MainLoop() threading.Thread(target=loop.run, daemon=True).start() # 打开相机 cam = cv2.VideoCapture(CAMERA_ID, cv2.CAP_V4L2) if not cam.isOpened(): print("相机打开失败") exit() window_title = "Processed Stream" cv2.namedWindow(window_title, cv2.WINDOW_KEEPRATIO) try: while True: ret, frame = cam.read() if not ret: break # 此处插入YOLO检测处理逻辑 frame_out = cv2.resize(frame, (OUTPUT_WIDTH, OUTPUT_HEIGHT)) # 本地显示 cv2.imshow(window_title, frame_out) # 写入RTSP流 factory.write_frame(frame_out) key = cv2.waitKey(1) & 0xFF if key == 27 or key == ord('q'): break finally: cam.release() cv2.destroyAllWindows() loop.quit()
验证方法
代码启动后,用VLC或gst-launch-1.0访问rtsp://[本机IP]:8554/test即可正常拉流,拉流命令示例:
gst-launch-1.0 rtspsrc location=rtsp://127.0.0.1:8554/test latency=100 ! queue ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! autovideosink
内容的提问来源于stack exchange,提问作者Алексей Фадеев

