基于树莓派的视频推流方案

random-pic-api

简介

因为项目上有很多设计到监控视频的工作, 所以想通过树莓派来研究一下流媒体相关的技术, 所以总结了一些在树莓派上实现视频推流的方案.

硬件:

20250212192842_OAFqjO2i.webp

摄像头

一个摄像头使用了官方的 RPi Camera V2, 使用了索尼 IMX219 800 万像素传感器, 另一个使用了 IMX519, 具备 1600W 像素.

RPi Camera V2 插上就能识别, 但是 IMX519 费了点功夫, 找到了几篇相关的讨论:

关于型号

感光芯片型号支持的树莓派主板型号支持的驱动类型
OV5647所有树莓派主板libcamera / Raspicam
OV9281所有树莓派主板libcamera
IMX219 (树莓派官方)所有树莓派主板libcamera / Raspicam
IMX219 (第三方)树莓派计算模块libcamera
IMX290/ IMX327所有树莓派主板libcamera
IMX378所有树莓派主板libcamera
IMX477 (树莓派官方)所有树莓派主板libcamera / Raspicam
IMX477 (第三方)树莓派计算模块libcamera
IMX519树莓派主板libcamera(另装驱动)
IMX708 (树莓派 Camera Module 3)所有树莓派主板libcamera
IMX296(树莓派 Global Camera)所有树莓派主板libcamera
IMX500(树莓派 AI Camera)所有树莓派主板libcamera

下面是具体的实施步骤.

修改固件配置

1
2
3
4
5
6
7
8
sudo nano /boot/config.txt
# 如果是 bookworm系统
sudo nano /boot/firmware/config.txt

# 添加下面这段配置
camera_auto_detect=0
dtoverlay=imx219,cam0
dtoverlay=imx519,cam1

修改摄像头配置

libcamera/src/ipa/rpi/pisp/data at arducam · ArduCAM/libcamera · GitHub 下载最新的 imx519.json 文件, 替换掉原来的配置: /usr/share/libcamera/ipa/rpi/pisp/imx519.json, 然后重启即可.

驱动检查

1
2
3
4
5
6
7
8
9
dmesg | grep imx

[ 0.532486] platform 1f00110000.csi: Fixed dependency cycle(s) with /axi/pcie@120000/rp1/i2c@88000/imx219@10
[ 0.532607] platform 1f00128000.csi: Fixed dependency cycle(s) with /axi/pcie@120000/rp1/i2c@80000/imx519@1a
[ 3.298575] imx519 4-001a: Device found is imx519
[ 3.311363] rp1-cfe 1f00110000.csi: found subdevice /axi/pcie@120000/rp1/i2c@88000/imx219@10
[ 3.312865] rp1-cfe 1f00128000.csi: found subdevice /axi/pcie@120000/rp1/i2c@80000/imx519@1a
[ 3.312891] rp1-cfe 1f00128000.csi: Using sensor imx519 4-001a for capture
[ 3.598311] rp1-cfe 1f00110000.csi: Using sensor imx219 6-0010 for capture

可以看到 imx519 的驱动成功加载了.

检查摄像头

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
v4l2-ctl --list-devices

pispbe (platform:1000880000.pisp_be):
/dev/video20
/dev/video21
/dev/video22
/dev/video23
/dev/video24
/dev/video25
/dev/video26
/dev/video27
/dev/video28
/dev/video29
/dev/video30
/dev/video31
/dev/video32
/dev/video33
/dev/video34
/dev/video35
/dev/video36
/dev/video37
/dev/media2
/dev/media3

rp1-cfe (platform:1f00110000.csi):
/dev/video8
/dev/video9
/dev/video10
/dev/video11
/dev/video12
/dev/video13
/dev/video14
/dev/video15
/dev/media0

rp1-cfe (platform:1f00128000.csi):
/dev/video0
/dev/video1
/dev/video2
/dev/video3
/dev/video4
/dev/video5
/dev/video6
/dev/video7
/dev/media1

rpivid (platform:rpivid):
/dev/video19
/dev/media4

主要关注 rp1-cfe 信息, 这个设备通常指的是连接到 Raspberry Pi 的 CSI (Camera Serial Interface) 设备, 可查看具体设备的详细信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
➜  ~ v4l2-ctl -d /dev/video0 --all
Driver Info:
Driver name : rp1-cfe
Card type : rp1-cfe
Bus info : platform:1f00128000.csi
Driver version : 6.6.31
Capabilities : 0xaca00001
Video Capture
Metadata Capture
Metadata Output
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x24a00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Media Driver Info:
Driver name : rp1-cfe
Model : rp1-cfe
Serial :
Bus info : platform:1f00128000.csi
Media version : 6.6.31
Hardware revision: 0x00114666 (1132134)
Driver version : 6.6.31
Interface Info:
ID : 0x03000017
Type : V4L Video
Entity Info:
ID : 0x00000015 (21)
Name : rp1-cfe-csi2_ch0
Function : V4L2 I/O
Pad 0x01000016 : 0: Sink, Must Connect
Link 0x02000037: from remote pad 0x1000006 of entity 'csi2' (Video Interface Bridge): Data
Priority: 2
Video input : 0 (rp1-cfe-csi2_ch0: ok)
Format Video Capture:
Width/Height : 640/480
Pixel Format : 'pRAA' (10-bit Bayer RGRG/GBGB Packed)
Field : None
Bytes per Line : 800
Size Image : 384000
Colorspace : Raw
Transfer Function : None
YCbCr/HSV Encoding: ITU-R 601
Quantization : Full Range
Flags :
Format Metadata Capture:
Sample Format : 'SENS' (Sensor Ancillary Metadata)
Buffer Size : 16384

测试摄像头

自动对焦并拍摄照片

1
libcamera-still -t 10000 -n --autofocus-mode auto -o test1.jpg --camera 1

20250212130816_rj2SNreE.webp

Raspberry Pi Camera Autofocus: Complete Guide (V1, V2 & HQ)

IMX219 直接使用 rpicam-jpeg 来拍摄一张图片:

1
rpicam-jpeg -o test.jpg --camera 0

20250212130730_Jods28xH.webp

通过 WebUI 控制摄像头

picamera2-WebUI 是 Raspberry Pi 相机模块的轻量级 Web 界面,基于 Picamera2 Python 库并使用 Flask 构建。此项目提供了一个用户界面,用于配置相机设置、拍摄照片以及在基本图库中管理图像。

部署

1
2
3
git clone git@github.com:monkeymademe/picamera2-WebUI.git
cd picamera2-WebUI
python app.py

一切正常的话, 会在 8080 端口开启一个 Web 服务:

20250212131525_cfMOZfTX.webp

2 个摄像头都成功识别到了.

还能够控制摄像头的分辨率:

20250212131652_SJ6e76J2.webp

摄像头控制

20250212192857_K2bz1Poh.webp

查看视频流

左边:IMX219: 1080P
右边: IMX519 4K

流媒体服务器

基于树莓派的流媒体服务器非常多, 下面 shi 一些常见的方案

调研结果

  1. mjpg-stream - web 可以直接访问,但是帧率太低,参考

  2. vlc - 延迟高,大约 2s-5s

  3. raspivid - 延迟 170ms 左右,并支持 h264 硬件编码,好像可以 参考这里

    该工具已经默认集成到了树莓派之中

    1
    raspivid -t 0 -w 1280 -h 720 -fps 20 -o - | nc -k -l 8090

    -t 表示延时;-o 表示輸出;-fps 表示帧率;端口号为 8090

    -w 表示图像宽度;,-h 表示图像高度,此处设置的分辨率为 1280720;我们可以修改 -w 1920 -h 1080 将分辨率设置为 19201080

    该命令执行玩后不会出现任何打印信息即可

    在局域网内的 linux 主机上安装 mplayer 工具(sudo apt-get install mplayer),然后执行命令

    1
    mplayer -fps 200 -demuxer h264es ffmpeg://tcp://192.168.31.166:8090

    即会弹出一个显示树莓派实时视频流的窗口,而且延迟尚可,大概在 200ms 左右,基本上可以满足实时性的要求了。

  4. pistreaming - 性能不错,延迟低

    1. 安装依赖

      1
      2
      3
      4
      5
      6
      7
      8
      # 安装python3-picamera
      sudo apt-get install python3-picamera
      # 安装pip3
      sudo apt-get install python3-pip
      # 安装ws4py
      sudo pip3 install ws4py
      # 安装ffmpeg
      sudo apt-get install ffmpeg
    2. 下载源码

      1
      git clone https://github.com/waveform80/pistreaming.git
    3. 测试效果

      1
      2
      3
      4
      # 进入源码目录
      cd pistreaming
      # 运行程序
      python3 server.py

      浏览器访问查看效果

      1
      http://pi_ip:8082/index.html
  5. motion - 卡顿很严重,延迟在 30s+

  6. fswebcam - 采集摄像头数据保存为图片,用来做视频监控的话,性能和延迟都达不到要求

  7. Camkit - 支持硬件编解码,比较小众,缺少维护

  8. ffmpeg 硬解码推流 - 支持硬件编解码,但是延迟很高(不知道是推流原因还是播放原因),画质很差

    流程比较复杂,整理成脚本保存在 ffmpeg 目录中

    1. 安装 x264 硬件编码 install_x264.sh

    2. 安装 ffmpeg install_ffmpeg.sh

    3. 安装运行 nginx install_nginx.sh

    4. 启动推流(注意 192.168.0.111 替换为你的 ip 地址)

      1
      /usr/local/bin/ffmpeg -ss 0 -pix_fmt yuv420p -i /dev/video0 -c:v h264_omx -f flv rtmp://192.168.0.111:1935/live/camera
    5. potplayer 播放 url rtmp://192.168.0.111:1935/live/camera

  9. webrtc - 看视频貌似效果很好,工作量太大,后期验证

最终方案

油管上找到一个 测试树莓派 5 最佳直播方式的视频, 对比了 WebRTC, TCP, UDP 和 RTSP 的延迟:

20250212192857_p7dMvHQp.webp

看着 MediaMTX 延迟表现非常好, 决定直接使用它来作为媒体服务器使用, 另外一点是它内置了树莓派支持.

部署 MediaMTX

1
2
3
4
5
wget https://github.com/bluenviron/mediamtx/releases/download/v1.9.3/mediamtx_v1.9.3_linux_arm64v8.tar.gz
tar -zxvf mediamtx_v1.9.3_linux_arm64v8.tar.gz -C mediamtx_v1.9.3_linux_arm64v8
cd mediamtx_v1.9.3_linux_arm64v8
# 使用默认配置启动
./mediamtx

配置 MediaMTX

内置支持

MediaMTX 自带了树莓派摄像头支持, 配置如下 (mediamtx-rpiCamera.yml):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

... # 前面的配置未做任何修改
paths:
cam0:
# http://192.168.21.21:8889/cam0/
source: rpiCamera
rpiCameraCamID: 0
rpiCameraWidth: 1920
rpiCameraHeight: 1080
rpiCameraHFlip: True
rpiCameraVFlip: True
cam1:
# http://192.168.21.21:8889/cam1/
source: rpiCamera
rpiCameraCamID: 1
rpiCameraWidth: 2560
rpiCameraHeight: 1440
rpiCameraHFlip: True
rpiCameraVFlip: True
rpiCameraBrightness: 0.0
rpiCameraContrast: 1
rpiCameraSaturation: 1
rpiCameraSharpness: 0
rpiCameraExposure: normal
rpiCameraAWB: auto
rpiCameraDenoise: "cdn_off"
rpiCameraShutter: 0
rpiCameraMetering: centre
rpiCameraGain: 0
rpiCameraEV: 0
rpiCameraHDR: false
rpiCameraFPS: 30
rpiCameraIDRPeriod: 60
rpiCameraBitrate: 2000000
rpiCameraProfile: main
rpiCameraLevel: "4.1"
rpiCameraAfMode: continuous
rpiCameraAfRange: normal
rpiCameraAfSpeed: normal
rpiCameraLensPosition: 0

启动方式:

1
2
# 导入 ldconfig, 它在 /usr/sbin 目录下, 不知道为啥找不到
export PATH=$PATH:/usr/sbin && nohup ./mediamtx mediamtx-rpiCamera.yml &

20250212135804_gljWVL4z.webp

RTMP 方式

配置(mediamtx-rtmp.yml)

1
2
3
4
5
6
7
paths:
cam0:
runOnInit: bash -c 'rpicam-vid --hflip --vflip -t 0 --camera 0 --nopreview --codec yuv420 --width 1920 --height 1080 --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 1920x1080 -i /dev/stdin -c:v libx264 -preset ultrafast -tune zerolatency -f flv rtmp://192.168.21.7/live/pi5a-camera0'
runOnInitRestart: yes
cam1:
runOnInit: bash -c 'rpicam-vid --hflip --vflip -t 0 --autofocus-mode auto --camera 1 --nopreview --codec yuv420 --width 2540 --height 1440 --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 2560x1440 -i /dev/stdin -c:v libx264 -preset ultrafast -tune zerolatency -f flv rtmp://192.168.21.7/live/pi5a-camera1'
runOnInitRestart: yes

将视频流推送到 192.168.21.7 服务器, 这个是使用 WVP 搭建的另一个流媒体服务器:

启动

1
nohup ./mediamtx mediamtx-rtmp.yml &
1
2
3
4
5
6
7
8
9
10
11
12
...
Output #0, rtsp, to 'rtsp://localhost:8554/cam0':
Metadata:
encoder : Lavf59.27.100
Stream #0:0: Video: h264, yuv420p, 1920x1080, q=2-31, 25 fps, 90k tbn
Metadata:
encoder : Lavc59.37.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
frame= 0 fps=0.0 q=0.0 Lsize=N/A time=00:00:00.00 bitrate=N/A speed= 0x
video:0kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
frame= 1863 fps= 18 q=18.0 size=N/A time=00:01:14.48 bitrate=N/A speed=0.721x

20250212142052_f8wpDOW9.webp

RTMP 明显要比 WebRTC 延迟高, 基本上在 4 秒以上.

RTSP 方式

配置(mediamtx-rtsp.yml)

1
2
3
4
5
6
7
paths:
cam0:
runOnInit: bash -c 'rpicam-vid --hflip --vflip -t 0 --camera 0 --nopreview --codec yuv420 --width 1920 --height 1080 --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 1920x1080 -i /dev/stdin -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH'
runOnInitRestart: yes
cam1:
runOnInit: bash -c 'rpicam-vid --hflip --vflip -t 0 --camera 1 --nopreview --codec yuv420 --width 2560 --height 1440 --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v 2560x1440 -i /dev/stdin -c:v libx264 -preset ultrafast -tune zerolatency -f rtsp rtsp://localhost:$RTSP_PORT/$MTX_PATH'
runOnInitRestart: yes

启动

1
nohup ./mediamtx mediamtx-rtsp.yml &
1
2
3
4
5
6
7
8
9
Output #0, rtsp, to 'rtsp://localhost:8554/cam1':
Metadata:
encoder : Lavf59.27.100
Stream #0:0: Video: h264, yuv420p(progressive), 2560x1440, q=2-31, 25 fps, 90k tbn
Metadata:
encoder : Lavc59.37.100 libx264
Side data:
cpb: bitrate max/min/avg: 0/0/0 buffer size: 0 vbv_delay: N/A
frame= 1245 fps= 18 q=19.0 size=N/A time=00:00:49.76 bitrate=N/A speed=0.72x

使用 WVP 的拉流代理:

20250212142815_ndsfrSEN.webp

延迟有高出一大截, 毕竟是经过 2 个流媒体服务器多次转码的:

20250212142639_WjSecgaP.webp

延迟对比

20250212192910_CbVi4Iol.webp

启动脚本

为了方便测试, 写了一个启动脚本, 支持多种协议以及分辨率:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/bin/bash

# 检查是否有 -h 参数
if [[ "$1" == "-h" ]]; then
echo "使用方法: $0 [协议] [摄像头编号] [宽度] [高度] [URL地址]"
echo "示例: $0 rtmp 0 1920 1080 192.168.21.7/pi5b, 最终URL: rtmp://192.168.21.7/pi5b/0"
echo
echo "参数说明:"
echo " 协议 : rtmp 或 rtsp (用于选择流媒体传输协议)"
echo " 摄像头编号 : 摄像头编号 (例如 0 或 1)"
echo " 宽度 : 分辨率宽度 (例如 1920)"
echo " 高度 : 分辨率高度 (例如 1080)"
echo " URL地址 : 基础的 URL 地址 (例如 192.168.21.7/pi5b)"
echo
echo "RTMP 推流说明:"
echo " 192.168.21.7:1935/pi5b --> rtmp://192.168.21.7:1935/pi5b/0 推流至 m920x 的 zlm 服务, 默认端口 1935, 会出现在 WVP 的推流列表中"
echo " 192.168.21.7:41935/pi5b --> rtmp://192.168.21.7:41935/pi5b/0 推流至 m920x 的 mediamtx 服务, 使用 mediamtx 服务的 WebRTC 访问: http://192.168.21.7:48889/pi5b/{CAMERA}"
echo " 127.0.0.1:1935/pi5b --> 本地推流至 mediamtx 服务, 使用 WebRTC 访问: http://ip:8889/pi5b/{CAMERA}"
echo " ===================================================="
echo " ./stream.sh rtmp 0 1920 1080 192.168.21.7/pi5a"
echo " ./stream.sh rtmp 0 2560 1440 192.168.21.7:41935/pi5a"
echo " ./stream.sh rtmp 0 3840 2160 192.168.21.7/pi5a"
echo " ./stream.sh rtmp 0 3840 2160 127.0.0.1/pi5a"
echo
echo "RTSP 推流说明:"
echo " 192.168.21.7:554/pi5b --> rtsp://192.168.21.7:554/pi5a/0 推流至 m920x 的 zlm 服务, 默认端口 554, 会出现在 WVP 的推流列表中"
echo " 192.168.21.7:48554/pi5b --> rtsp://192.168.21.7:48554/pi5b/0 推流至 m920x 的 mediamtx 服务, 使用 WebRTC 访问: http://192.168.21.7:48889/pi5b/{CAMERA}"
echo " 127.0.0.1:8554/pi5b --> 本地推流至 mediamtx 服务, 使用 WebRTC 访问: http://ip:8889/pi5b/{CAMERA}"
echo " ===================================================="
echo " ./stream.sh rtsp 1 1920 1080 192.168.21.7/pi5b"
echo " ./stream.sh rtsp 1 2560 1440 192.168.21.7:48554/pi5a"
echo " ./stream.sh rtsp 1 3840 2160 192.168.21.7:8554/pi5a"
echo " ./stream.sh rtsp 1 3840 2160 127.0.0.1:8554/pi5a"
exit 0
fi

# 参数赋值
PROTOCOL=$1 # 第一个参数为协议 (rtmp 或 rtsp)
CAMERA=$2 # 第二个参数为摄像头编号
WIDTH=$3 # 第三个参数为宽度 1920x1080 2560x1440 3840x2160
HEIGHT=$4 # 第四个参数为高度
URL=$5 # 第五个参数为基础的 URL 地址

# 根据协议动态设置输出流地址和格式
if [ "$PROTOCOL" == "rtmp" ]; then
OUTPUT_URL="rtmp://${URL}/${CAMERA}"
FFMPEG_FORMAT="flv"
elif [ "$PROTOCOL" == "rtsp" ]; then
OUTPUT_URL="rtsp://${URL}/${CAMERA}"
FFMPEG_FORMAT="rtsp"
else
echo "不支持的协议: $PROTOCOL"
exit 1
fi

# 运行命令
nohup bash -c "rpicam-vid --hflip --vflip -t 0 --camera $CAMERA --nopreview --codec yuv420 --width $WIDTH --height $HEIGHT --inline --listen -o - | ffmpeg -f rawvideo -pix_fmt yuv420p -s:v ${WIDTH}x${HEIGHT} -i /dev/stdin -c:v libx264 -preset ultrafast -tune zerolatency -f $FFMPEG_FORMAT $OUTPUT_URL" > ${PROTOCOL}-cam${CAMERA}.log 2>&1 &

相关资源

高阶玩法

V4L2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ sudo apt-get install v4l-utils
$ v4l2-ctl --list-devices
bcm2835-codec-decode (platform:bcm2835-codec):
/dev/video10
/dev/video11
/dev/video12
/dev/media1
bcm2835-isp (platform:bcm2835-isp):
/dev/video13
/dev/video14
/dev/video15
/dev/video16
/dev/media0
mmal service 16.1 (platform:bcm2835-v4l2):
/dev/video0

驱动程序

V4L2 驱动程序提供用于访问摄像头和编解码器功能的标准 Linux 接口。通常,Linux 会在启动期间自动加载驱动程序。但在某些情况下,可能需要 明确加载摄像头驱动程序

使用时的设备节点 libcamera

/dev/videoX默认操作
video0第一个 CSI-2 接收器的 Unicam 驱动程序
video1用于第二个 CSI-2 接收器的 Unicam 驱动程序
video10视频解码
video11视频编码
video12简单的 ISP,除了 Bayer 到 RGB/YUV 的转换外,还可以执行 RGB/YUV 格式之间的转换和调整大小
video13输入至完全可编程 ISP
video14完全可编程 ISP 的高分辨率输出
video15完全可编程 ISP 的结果输出较低
video16来自完全可编程 ISP 的图像统计数据
video19HEVC 解码

使用 V4L2 驱动程序

参阅 V4L2 文档