一、物理视频流的起点
📷1️⃣ 海康摄像头是怎么输出的?
-
海康工业/监控摄像头通过 RTSP(Real Time Streaming Protocol)输出 H.264 编码的视频流。
-
也就是说,摄像头本身并不是输出原始帧,而是内部 ISP(图像信号处理)模块把 CMOS 采集的 Bayer Raw 做了去噪、白平衡、去畸变(有限)、压缩成 H.264。
-
所以你拉到的 RTSP,本质是个H.264 编码码流,可以直接喂给硬件解码器。
-
总结一句:我理解为RTSP通信协议内部封装H.264格式的编码视频流。
🔗 2️⃣ GStreamer 拉流和解码是干啥?
-
你用
rtspsrc
+rtph264depay
+h264parse
+nvv4l2decoder
:-
rtspsrc
:从摄像头拉取 RTSP。我理解为:通信协议接收。 -
rtph264depay
:把 RTP 包里的 H.264 取出来。我理解为:视频编码格式数据提取。 -
h264parse
:重新整理一下码流(帧界定、SPS/PPS)。我理解为:拆包组包数据解析。 -
nvv4l2decoder
:Jetson 平台的硬件解码,专门把 H.264 码流变成未压缩帧。我理解为:编码视频解压缩并解码。
-
-
这一步的输出是 NV12 或 I420 格式的视频帧,但由于你后面要在 OpenCV 里处理,所以通常做了
videoconvert ! video/x-raw,format=BGR
:-
videoconvert
:颜色格式转换。 -
转成 BGR,是为了直接塞给 OpenCV 的
cv::Mat
,方便用 CUDA、remap、拼接等函数。 -
因为OpenCV 默认处理的是 BGR 格式。
-
🎥 3️⃣ OpenCV 为什么默认是 BGR?
-
OpenCV 设计之初是 C++ 图像处理库,核心是 CPU 上的矩阵运算。
-
绝大多数通用相机、图像文件(JPEG、BMP)都是 BGR 或 RGB。
-
OpenCV 一开始就面向“显示/算法/绘制/检测”场景,BGR/RGB 是最直观的排列顺序。
-
所以
cv::imread
默认读出来就是 BGR,cv::imshow
也默认显示 BGR。 -
这就导致你的中间环节(拼接、畸变校正)最好在 BGR 上做。
⚙️ 二、GPU 去畸变和拼接
-
你用
cv::cuda::remap
对每路帧做鱼眼去畸变:-
输入是 BGR 格式的
cv::cuda::GpuMat
。 -
输出也是 BGR。
-
-
如果要拼接,就把多路去畸变后的帧拿出来,放到更大的画布里(同样是 BGR)。
-
所以,整个环视阶段,你处理的是多路 BGR → 拼成一张大的 BGR pano。
📌 三、为什么要转 I420?
-
到这时,结果是 一张 BGR pano。
-
但是:
-
编码器(H.264)最优处理的输入是 YUV420(I420)。
-
YUV420 相当于把彩色图像拆成亮度(Y)+ 两个色度(U、V)。
-
U、V 是 1/2 宽高采样,压缩率高。
-
-
所以,你需要
cv::cvtColor(BGR → YUV_I420)
:-
CPU 做颜色空间转换。
-
得到连续的 YUV420 平面数据。
-
🚦 四、appsrc 怎么塞给 GStreamer?
-
转完 I420,做成
GstBuffer
。 -
appsrc
是个特殊元素,相当于:-
“我这里有裸帧数据,要送给管道。”
-
-
你设置:
GstCaps* caps = video/x-raw, format=I420, width=1920, height=1080, framerate=30/1
表示:
我后面接收的流就是 I420,分辨率和帧率是这个,别搞错。
-
然后
g_signal_emit_by_name(appsrc, "push-buffer", buffer)
,把数据推进去。
🎞️ 五、x264enc 是怎么编码的?
-
appsrc 的输出是连续裸帧,
-
x264enc 负责把它做帧内/帧间压缩,编码成 H.264 NAL 单元(SPS、PPS、I 帧、P 帧…)。
-
这里用了
speed-preset=ultrafast
tune=zerolatency
:-
不做复杂分析(比如 B 帧)。
-
马上输出,不用缓存 GOP。
-
📦 六、flvmux 为什么必须存在?
-
RTMP 协议要求推送的是 FLV 容器。
-
H.264 本身只是视频帧,缺少时间戳、元信息(音视频多路复用、Metadata)。
-
flvmux
把裸 H.264 封装成 FLV Stream:-
附带
onMetaData
、keyframe index
、时间戳。 -
观众端(ffplay、VLC)才能正常解析。
-
🌐 七、rtmpsink 最终发出去
-
rtmpsink
做 RTMP 协议握手,把 FLV 封装通过 TCP 推给你的流媒体服务器(如 MediaMTX)。 -
观众、ffplay 再从 RTMP 播放时,看到的就是这个流。
✅ 完整主线
[海康摄像头 H.264 RTSP]
→ [GStreamer 硬解码 → BGR]
→ [OpenCV CUDA 去畸变/拼接 → BGR pano]
→ [cv::cvtColor → I420]
→ [appsrc 推送裸帧]
→ [x264enc 压缩]
→ [flvmux 封装 FLV]
→ [rtmpsink 推 RTMP]
→ [MediaMTX/SRS]
→ [观众/播放器]
🔑 核心理念
1️⃣ 解码 → BGR → CUDA 处理
2️⃣ 处理完要给观众,就必须重新编码 → H.264
3️⃣ 编码必须配合 RTMP 协议 → FLV 封装
4️⃣ 所有数据通过 appsrc 塞给 GStreamer → 完成可控推流