Skip to content

About

A hardware-driven, zero-copy GStreamer pipeline on Raspberry Pi 5 that streams CSI/UVC frames exclusively via dma-buf to v4l2h264enc for hardware encoding, recording, and RTSP preview without any CPU memory copying.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

Repository files navigation

vm_camera_dma_buf

一个跑在树莓派 5 + Ubuntu24 + IMX219 上的小项目,用来把 GStreamer 端到端的 dma-buf 零拷贝通路彻底跑对。上游从 CSI 或 UVC 拿到帧,一路 dma-buf 送到 v4l2h264enc 硬编,落盘 + 可选 RTSP 预览;也可以走另一条支线:直接把 dma-buf 交给 kmssink,让 DRM primary plane 做 scanout,在 Pi 5 没有 H.264 硬编时得到一条真正 CPU 零参与的显示链路。在直显形态下还提供两个 KMS overlay plane 的合成层:右上角时间戳水印(CPU 光栅化 8x16 点阵,一秒一次),以及 libpag 动画 overlay(GPU 直接画进 dma-buf backed FBO,再以 DRM fb 绑到 overlay plane)。整条链路不下地、不 memcpy,运行时能看到编码器 / 显示器入口 caps 上确实带着 memory:DMABuf,overlay plane 在 vc4 HVS 里与主视频同帧硬件合成。

项目定位

在真板子上把 CSI camera → dma-buf → HW encoder → RTSP 这条链路彻底跑对,并且可复现地验证零拷贝没被打断。 编码分支不可用(例如 Pi 5 上没有 H.264 硬编)时,提供 kmssink 直显作为对照组,以理解"零拷贝"和"是否经过编码"是两件事。直显形态下再叠一层业务需求常见的 overlay(时间戳水印 / PAG 动画),验证 "GPU/CPU 生成内容 → dma-buf → KMS overlay plane → HVS 硬件合成" 这条 c-side 通路的可行性,同时把 EGL/GBM、DRM master fd 借用、DRM plane 竞争等边角都真跑一遍。

硬件:Raspberry Pi 5 (8G) + Sony IMX219(CSI)或任意 UVC。系统:Ubuntu 24.04。

快速开始

sudo apt install -y \
    libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev \
    gstreamer1.0-tools gstreamer1.0-plugins-{base,good,bad,ugly} \
    gstreamer1.0-libav gstreamer1.0-libcamera \
    libgstrtspserver-1.0-dev gstreamer1.0-rtsp \
    libdrm-dev libgbm-dev libegl-dev libgles-dev \
    libspdlog-dev libyaml-cpp-dev cmake ninja-build g++

cmake -S . -B cmake-build-raspi -G Ninja \
      -DCMAKE_BUILD_TYPE=Release -DTARGET_PLATFORM=rpi
cmake --build cmake-build-raspi -j

# 默认在当前目录自动搜索 config/default.yaml
./cmake-build-raspi/vm_camera_dma_buf

# 也可以显式指定配置,或用 CLI 覆盖单个字段
./cmake-build-raspi/vm_camera_dma_buf --config config/default.yaml
./cmake-build-raspi/vm_camera_dma_buf --device /dev/video2

libpag(PAG overlay 支线)由第三方源码在 third_party/libpag 下随项目一起构建,默认由 CMake 开关 ENABLE_PAG_OVERLAY=ON 打开。若不需要 PAG overlay,可以 -DENABLE_PAG_OVERLAY=OFF 关掉,此时 src/overlay/pag/ 下三个 .cpp 不参与编译、也不链接 libpag。

关于 libcamera 手动编译

Ubuntu 24.04 for Raspberry Pi 官方源里的 libcamera / gstreamer1.0-libcamera 版本与 Pi 5 的 IMX219 CSI 通路对不上,apt 装完之后 rpicam-hello --list-cameras 直接空列表,libcamerasrc 也起不来。所以真板子上这一步是绕不开的:需要从源码手动编译 libcamera(以及可选的 rpicam-apps 生态)。

具体流程直接参考这篇踩坑记录:

目录结构

vm_camera_dma_buf/
├── CMakeLists.txt                 # 构建入口
├── config/                        # 运行时 YAML 配置 (config/default.yaml)
├── assets/                        # 运行期资源 (PAG 素材等)
│   └── pag/PAG_LOGO.pag
├── docs/                          # 分模块的详细设计文档
│   ├── dma_buf_lifecycle.md
│   ├── drm_api_reference.md
│   ├── hdmi_display_pipeline.md
│   ├── timestamp_overlay_pipeline.md
│   └── pag_overlay_pipeline.md
├── scripts/                       # 环境自检 / 辅助脚本
├── src/                           # C++ 源码主目录
│   ├── main.cpp / app.{h,cpp}     # 程序入口 + 全局装配 (build_and_start)
│   ├── util/                      # 配置、日志、dma-buf 探测、caps 工具等
│   ├── buffer/                    # dma-buf allocator / pool 预留位
│   ├── pipeline/                  # GStreamer pipeline 组装
│   │   ├── rpi_pipeline.cpp             # CSI / UVC dma-buf / UVC mmap 三分支
│   │   ├── encoder_branch.{h,cpp}       # tee + v4l2h264enc + filesink (+ 可选 rtsp-tap)
│   │   ├── display_branch.{h,cpp}       # queue + kmssink 直显 (与 encoder 互斥)
│   │   ├── pipeline_builder.h
│   │   └── pipeline_factory.cpp
│   ├── sink/                      # appsink 回调、抓帧、RTSP server 封装
│   └── overlay/                   # 显示形态下叠加在 kmssink 之上的 DRM overlay
│       ├── timestamp_overlay.{h,cpp}    # 8x16 点阵水印 → dumb-buffer plane
│       ├── drm/                          # DRM plane 通用件 (session/picker/dumb_fb)
│       └── pag/                          # libpag 动画 overlay 支线
│           ├── egl_gbm_context.{h,cpp}  # /dev/dri/renderD128 + EGL/GBM
│           ├── dmabuf_fb_ring.{h,cpp}   # dma-buf ↔ GL FBO ↔ DRM fb 三位一体 ring
│           └── pag_overlay.{h,cpp}      # libpag → FBO 渲染线程 + KMS 显示线程
├── third_party/                   # 第三方组件源码 (libpag、mediamtx 等)
└── web/                           # 浏览器预览壳(配合 RTSP 分支使用)

Pipeline 形态

三种形态互斥/叠加规则:encoder.enable 与 display.enable 互斥(编码链路里最后一级是 filesink,直显链路里最后一级是 kmssink,共享同一个源 tee 的下游需要用配置在两者中二选一);display.enable=true 之后,display.timestamp.enable / display.pag_overlay.enable 可以各自独立打开,两者与主视频在 KMS 侧属于不同 plane,由 vc4 HVS 硬件合成。

形态 A:rtsp 预览

最完整的形态,实际生成的图接近:

libcamerasrc
  ! video/x-raw(memory:DMABuf), NV12, 1920x1080@30
  ! tee name=t
      t. ! queue leaky=downstream max-size-buffers=2
         ! appsink                                            # 主干,业务侧
      t. ! queue leaky=downstream max-size-buffers=4
         ! v4l2h264enc extra-controls="..."
         ! h264parse config-interval=1
         ! tee name=t2
             t2. ! queue ! filesink location=/tmp/gst_dump/out.h264
             t2. ! queue ! appsink name=rtsp-tap             # 供 RTSP 拉

rtsp-tap 侧的 sample 会被 pump 线程读出来,跨 pipeline 送到 RTSP factory 内部的 appsrc,由 rtph264pay 打包发出:

appsrc (do-timestamp=true, format=time)
  ! video/x-h264, byte-stream, au
  ! queue leaky=downstream max-size-buffers=8
  ! rtph264pay config-interval=1

形态 B:kmssink 直显

必须 encoder.enable=false display.enable=true,走 kmssink 直显。这是 Pi 5 上"没有 H.264 硬编、但仍要看画面"的落地形态:

libcamerasrc
  ! video/x-raw(memory:DMABuf), NV12, 1920x1080@30
  ! queue leaky=downstream max-size-buffers=2
  ! kmssink force-modesetting=true can-scale=true sync=false

形态 C:形态 B 之上再叠 overlay 合成层(libpag + timestamp)

display.timestamp.enable / display.pag_overlay.enable 打开后,我们会 借用 kmssink 的 DRM master fd(dup(kmssink.fd))另开独立生命线,各自 drmModeSetPlane() 挂到一个 overlay plane 上。GStreamer 侧的 pipeline 不改,vc4 HVS 在 scanout 时把三层合到一起:

[主视频]  libcamerasrc → dma-buf → kmssink → DRM primary plane   ─┐
                                                                   │
[时间戳]  CPU 8x16 点阵 (每秒一次) → dumb-buffer → overlay plane  ─┤─→ HVS 硬件合成 → HDMI
                                                                   │
[PAG 动画] libpag GLES2 → dma-buf FBO (30/60fps) → overlay plane  ─┘
                                                    ↑
                                          (GPU 直接写 dma-buf,无 CPU 拷贝)

About

A hardware-driven, zero-copy GStreamer pipeline on Raspberry Pi 5 that streams CSI/UVC frames exclusively via dma-buf to v4l2h264enc for hardware encoding, recording, and RTSP preview without any CPU memory copying.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages