RK3588 MIPI 摄像头采集与 WebRTC 低延迟推流实战
在嵌入式开发中,打通从 MIPI 摄像头采集、硬件 ISP 处理、AI 推理到 WebRTC 低延迟推流的完整链路往往比想象中复杂。本文基于 RK3588 平台,分享一套经过验证的实战方案,涵盖环境搭建、核心脚本编写及常见坑点规避。
整体架构思路
本方案主要使用 Python 实现,流程如下:
- 画面采集:利用 GStreamer 对接底层驱动,通过 RK3588 的硬件 ISP 将 MIPI RAW 数据转换为彩色图像。
- 图像处理:OpenCV 获取图像后,可在此处接入 AI 推理或常规图像处理逻辑。
- 硬件编码推流:调用
mpph264enc(瑞芯微硬件编码器)压缩视频流,推送至本地流媒体服务器。 - 终端分发:使用轻量级
MediaMTX作为流媒体服务器,客户端通过 WebRTC 协议实现网页端秒开与超低延迟观看。
第一步:环境准备
首先确认摄像头节点(例如单摄时通常为 /dev/video11)。确保系统安装了支持 GStreamer 的 OpenCV 及相关插件。注意 PyPI 上的默认 OpenCV 可能不支持 GStreamer,建议直接使用系统环境包或手动编译。
sudo apt-get update
sudo apt-get install python3-opencv
sudo apt-get install gstreamer1.0-plugins-good gstreamer1.0-plugins-bad gstreamer1.0-plugins-ugly gstreamer1.0-rtsp
第二步:部署轻量级流媒体服务器 (MediaMTX)
此服务器并非传统网站服务器,建议在板子上直接运行,这样客户端无需额外配置。下载 ARM64 版本并启动:
wget https://github.com/bluenviron/mediamtx/releases/download/v1.16.1/mediamtx_v1.16.1_linux_arm64.tar.gz
tar -zxvf mediamtx_v1.16.1_linux_arm64.tar.gz
./mediamtx
启动成功后应看到类似输出,表明 RTSP、WebRTC 等端口已监听:
INF MediaMTX v1.16.1, linux, arm64
INF [RTSP] listener opened on :8554
INF [WebRTC] listener opened on :8889
保持该终端运行,后续操作可在新终端进行。
第三步:核心 Python 脚本
新建脚本 stream.py。代码中包含了降分辨率、防缓存延迟和调用 MPP 硬件编码的核心参数。
import cv2
import time
class FPSCounter:
"""FPS 计数器类"""
def __init__(self, buffer_size=20):
self.timestamps = []
self.buffer_size = buffer_size
def update(self):
"""更新 FPS 计数"""
current_time = time.time()
self.timestamps.append(current_time)
if len(self.timestamps) > self.buffer_size:
self.timestamps.pop(0)
def get_fps(self):
"""计算当前 FPS"""
if len(self.timestamps) < 2:
return 0.0
time_span = self.timestamps[-1] - self.timestamps[0]
if time_span <= 0:
return 0.0
return (len(self.timestamps) - 1) / time_span
# 1. 配置读取摄像头的 GStreamer Pipeline
# 根据你的 MIPI 摄像头节点调整,这里假设是 /dev/video11
# 强制设定分辨率和帧率,并转换为 BGR 格式给 OpenCV 处理
cap_pipeline = (
"v4l2src device=/dev/video11 io-mode=2 ! "
"video/x-raw, width=640, height=480, framerate=30/1, format=NV12 ! "
"videoconvert ! video/x-raw, format=BGR ! "
"appsink max-buffers=1 drop=true"
)
# 2. 配置推流的 GStreamer Pipeline
# appsrc 接收 OpenCV 的 BGR 图像,转为 NV12,交给 mpph264enc 硬编,然后推流给 MediaMTX
push_pipeline = (
"appsrc is-live=true ! "
"video/x-raw, format=BGR, width=640, height=480, framerate=30/1 ! "
"videoconvert ! video/x-raw, format=NV12 ! "
"mpph264enc bps=4000000 rc-mode=vbr ! "
"h264parse ! "
"rtspclientsink location=rtsp://127.0.0.1:8554/live protocols=tcp"
)
# 初始化视频读取
cap = cv2.VideoCapture(cap_pipeline, cv2.CAP_GSTREAMER)
if not cap.isOpened():
print("错误:无法打开 MIPI 摄像头。请检查节点和 ISP 状态。")
exit()
# 初始化视频推流器
out = cv2.VideoWriter(push_pipeline, cv2.CAP_GSTREAMER, 0, 30, (640, 480))
if not out.isOpened():
print("错误:无法初始化 GStreamer 推流器。")
exit()
print("成功启动采集和推流,按 'Ctrl+C' 停止。")
try:
fps_counter = FPSCounter()
while True:
ret, frame = cap.read()
if not ret:
print("未能读取到画面")
break
# ==========================================
# ★ 在这里加入你的 AI 推理代码 ★
# ==========================================
# 例如:results = rknn_model.inference(frame)
# frame = draw_bounding_boxes(frame, results)
fps_counter.update()
fps = fps_counter.get_fps()
# 作为演示,我们在这里加上时间戳和 fps
cv2.putText(frame, f"Time: {time.ctime()}", (30, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1)
cv2.putText(frame, f"Fps: {fps}", (30, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 1)
# 将画好框的处理后图像,写入推流管道(由 mpp 硬件编码)
out.write(frame)
except KeyboardInterrupt:
print("停止推流...")
finally:
cap.release()
out.release()
运行脚本:python3 stream.py
第四步:如何在客户端观看与保存?
此时,你的开发板已经变成了一个 IP 摄像头。假设你的板子局域网 IP 是 <设备 IP>。
1. 低延迟观看(网页直开)
直接在电脑或手机浏览器输入:http://<设备 IP>:8889/live(端口号参考 MediaMTX 启动日志)。利用 WebRTC 协议,延迟基本在百毫秒以内。
2. 在客户端无损录像
想要在电脑上把监控视频存下来?只需一行命令,直接将底层编好的 H.264 流封装成文件,完全不消耗客户端算力:
ffmpeg -rtsp_transport tcp -i rtsp://<设备 IP>:8554/live -c copy -f mp4 record_video.mp4
注意这里的端口号选的是 8554(RTSP)。
避坑指南
分享几个过程中最容易卡住的问题:
- 使用 GStreamer 无法打开摄像头
- 原因:RK3588 的 V4L2 驱动在处理 MIPI 摄像头和 ISP 内存时,默认的 mmap 内存映射方式有时会在 GStreamer 中引发异常。
- 解法:给
v4l2src加上io-mode=4(使用 DMABUF)或io-mode=2(USERPTR)往往能瞬间解决问题。
- MediaMTX 报错
invalid rtmp version (71)- 原因:进错端口了!用 HTTP 浏览器去访问了 1935 (RTMP) 或 8554 (RTSP) 端口。
- 解法:浏览器观看必须要访问
8889端口。
- 推流成功了,但是画面全是'纯绿色'
- 原因:OpenCV 推入的画面分辨率和推流器配置的分辨率不匹配;或者忘记启动
rkaiqISP 服务导致画面全是空数据。 - 解法:严格检查
cap_pipeline、cv2.VideoWriter和push_pipeline三个地方的分辨率参数是否完全一致。
- 原因:OpenCV 推入的画面分辨率和推流器配置的分辨率不匹配;或者忘记启动
- 延迟越来越大,跑久了像看回放
- 原因:加入 AI 推理后处理速度跟不上采集速度,OpenCV 把旧画面全缓存在了内存里。
- 解法:务必在读取管道末尾加上
appsink max-buffers=1 drop=true。
小贴士
如果单纯想流畅地观看调试图像而不需要 WebRTC,可以直接用 OpenCV 自带的编码函数传 JPEG 到 PC 端,虽然性能不如硬编推流,但简单直接方便:
success, encoded_img = cv2.imencode(".jpg", img)
之后可通过 Flask 等框架进行网络传输。
