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

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

  1. HomeLab
  2. 2024.11.06
  3. 17 min read

简介

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

硬件:

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

下面是具体的实施步骤.

修改固件配置

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, 然后重启即可.

驱动检查

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 的驱动成功加载了.

检查摄像头

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) 设备, 可查看具体设备的详细信息:

  ~ 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

测试摄像头

自动对焦并拍摄照片

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 来拍摄一张图片:

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

20250212130730_Jods28xH.webp

通过 WebUI 控制摄像头

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

部署

git clone [email protected]:monkeymademe/picamera2-WebUI.git
cd picamera2-WebUI
python app.py

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

20250212131525_cfMOZfTX.webp

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

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

20250212131652_SJ6e76J2.webp

摄像头控制

20250212192857_K2bz1Poh.webp

查看视频流

{% videos, 2 %} {% video https://cdn.dong4j.site/source/image/stream1.mp4 %} {% video https://cdn.dong4j.site/source/image/stream2.mp4 %} {% endvideos %}

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

流媒体服务器

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

调研结果

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

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

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

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

    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),然后执行命令

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

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

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

    1. 安装依赖

      # 安装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. 下载源码

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

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

      浏览器访问查看效果

      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 地址)

      /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

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):


... # 前面的配置未做任何修改
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

启动方式:

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

20250212135804_gljWVL4z.webp

RTMP 方式

配置(mediamtx-rtmp.yml)

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 搭建的另一个流媒体服务器:

启动

nohup ./mediamtx mediamtx-rtmp.yml &
...
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)

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

启动

nohup ./mediamtx mediamtx-rtsp.yml &
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

启动脚本

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

#!/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

$ 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 文档

Homelab 自动化运维 学习笔记