GStreamer 多媒体开发
1. 概述
1.1 功能简介
GStreamer 是 Linux 平台上用得最多的开源多媒体框架。它的思路是:把播放、录制、转码这类任务拆成一个个小处理单元(Element),再用数据流把它们串成一条流水线(Pipeline),数据从源头一路流到出口,中途完成解码、格式转换、输出等环节。在 RK3588 上,GStreamer 通过 Rockchip MPP(Media Process Platform,瑞芯微的媒体处理框架)插件调用硬件编解码器,再配合 V4L2(Linux 的视频采集接口)摄像头采集、DRM/KMS(内核显示子系统)显示输出、ALSA(Linux 音频子系统)音频处理,就构成一条完整的硬件加速多媒体链路。RK3588 的 8 核 Cortex-A76/A55 处理器,加上 Mali-G610 MP4 GPU 和专用 RGA 2D 加速器,可以支撑 8K 视频解码、4K 视频编码这样的高性能多媒体应用。
1.2 应用场景
- 视频播放:本地/网络视频文件播放(H.264/H.265/VP9 硬解码)
- 视频监控:多路摄像头实时采集、预览与录像
- 视频编码录制:摄像头采集后硬件编码为 H.264/H.265 文件
- 流媒体服务:RTSP/RTMP/HLS 推流与拉流
- 视频会议:多路音视频采集、编解码、显示
- 数字标牌:多屏异显播放、视频墙
- 视频转码:格式转换、分辨率缩放、码率调整
1.3 工作原理
GStreamer 采用流水线模型:多个处理单元(Element)通过数据流连接,形成完整的处理链路。以文件播放为例:filesrc 从文件读数据,decodebin 解码,videoconvert 转换格式,最后交给显示输出;音频走一条类似的支线。
┌─────────────────────────────────────────────────────────────┐
│ GStreamer Pipeline │
│ │
│ Source ──► Decode/Filter ──► Processing ──► Sink │
│ │
│ Example: 文件播放流水线 │
│ filesrc ! decodebin ! videoconvert ! video sink │
│ audioconvert ! audio sink │
└─────────────────────────────────────────────────────────────┘
RK3588 硬件加速架构:
┌──────────────────────────────────────────────────────┐
│ GStreamer 应用层 │
│ filesrc / v4l2src / alsasrc / rtspsrc │
├──────────────────────────────────────────────────────┤
│ GStreamer 插件层 │
│ rockchipmpp (MPP 硬件编解码) │
│ v4l2 (V4L2 摄像头/视频设备) │
│ rga (RGA 2D 图形加速) │
│ drm/drmkms (DRM/KMS 显示输出) │
│ alsa (ALSA 音频) │
├──────────────────────────────────────────────────────┤
│ 硬件抽象层 (HAL) │
│ MPP (Media Process Platform) │
│ DRM/KMS (显示管理) │
│ V4L2 (视频设备) │
│ ALSA (音频设备) │
├──────────────────────────────────────────────────────┤
│ RK3588 硬件 │
│ VPU (视频编解码) / RGA (2D 加速) │
│ VOP (显示控制器) / HDMI TX (hdmi@fe0a0000) │
│ I2S/TDM (音频接口) │
│ MIPI-CSI (摄像头接口) │
└──────────────────────────────────────────────────────┘RK3588 关键多媒体硬件地址如下表。排查显示、摄像头、编解码问题时,可以拿设备树节点和寄存器地址对照设备树与 dmesg 输出定位:
| 模块 | 设备树节点 | 寄存器基地址 | 功能 |
|---|---|---|---|
| 显示控制器 | vop@fe040000 | 0xFE040000 | 视频合成与显示输出 |
| HDMI 输出 | hdmi@fe0a0000 | 0xFE0A0000 | HDMI 2.1 输出 |
| RGA 2D 加速 | rga@ff680000 | 0xFF680000 | 2D 图形加速 |
| MPP VPU | rkvdec2@fe610000 | 0xFE610000 | 视频解码器 |
| MPP VPU | rkvenc@fe640000 | 0xFE640000 | 视频编码器 |
1.4 运行环境
下面的结果取自实机验证(系统 linaro-alip,内核 6.1.141,Debian 12):
| 组件 | 情况 |
|---|---|
| gstreamer1.0 | 1.22.9 已预装(核心 + alsa / gl / clutter 等插件) |
| gst-launch-1.0 | 可用 |
| gst-inspect-1.0 | 可用,能列出 949 个插件 |
| v4l2-ctl | v4l-utils 已预装 |
Camera (/dev/video*) | video0~19 + video-camera0/enc0/dec0 多个 V4L2 节点(rkisp / cif) |
| HDMI 音频 | card1=rockchiphdmi1(playback) |
| ALSA ES8388 | card0=rockchipes8388(capture + playback) |
多数 GStreamer + 摄像头 / HDMI 的场景可以直接在板上跑起来,不需要在 PC 上编译;遇到缺少的复杂插件(例如 NN inference 相关),按需 apt install gstreamer1.0-* 装上即可。
下面是当时实际跑过的验证命令和输出:
# 实际跑过的验证
$ gst-inspect-1.0 --version | head -2
gst-inspect-1.0 version 1.22.9
GStreamer 1.22.9
$ gst-inspect-1.0 | wc -l
949
# 1) 测试 pipeline(不需要摄像头/显示器) — 10 帧,0.013s
$ timeout 4 gst-launch-1.0 -e videotestsrc num-buffers=10 ! fakesink 2>&1 | tail -3
Execution ended after 0:00:00.012882787
释放管道资源 ...
RC=0
# 2) 音频测试 — audiotestsrc → HDMI 输出
$ timeout 3 gst-launch-1.0 -e audiotestsrc freq=440 num-buffers=10 \
! audioconvert ! alsasink device=plughw:1,0 2>&1 | tail -3
Execution ended after 0:00:00.236079190
释放管道资源 ...
RC=0
# 3) 声卡清单
$ arecord -l && aplay -l
card 0: rockchipes8388 [rockchip-es8388], device 0: dailink-multicodecs ES8323 HiFi-0
card 1: rockchiphdmi1 [rockchip-hdmi1], device 0: rockchip-hdmi1 i2s-hifi-0
# 4) V4L2 设备树(节选)
$ v4l2-ctl --list-devices
rkisp-statistics (platform: rkisp):
/dev/video18
/dev/video19
rkcif-mipi-lvds2 (platform:rkcif-mipi-lvds2):
/dev/media0
rkisp_mainpath (platform:rkisp0-vir0):
/dev/video112. 硬件准备
2.1 硬件清单
按下表准备硬件。摄像头、音频线、U 盘是可选项,只在验证对应功能时才需要:
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3588LQ 核心板 + SHIMETAPI-M5-R2A 底板 | 1 | 运行 Debian 系统 |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| HDMI 显示器 | 支持 1080p 或更高分辨率 | 1 | 用于视频输出验证 |
| 摄像头模块(可选) | MIPI-CSI 摄像头(IMX415/SC5235 等) | 1 | 用于摄像头采集验证 |
| 音频模块(可选) | 3.5mm 音频线或 HDMI 音频 | 1 | 用于音频输出验证 |
| 网线 | 标准 RJ45 | 1 | 用于网络和 SSH 连接 |
| USB 存储(可选) | U 盘或移动硬盘 | 1 | 存放测试视频文件 |
2.2 硬件连接
接线方式如下:
SHIMETAPI-M5-R2A 底板连接:
├── HDMI 输出 ──► HDMI 显示器
├── MIPI-CSI 接口 ──► 摄像头模块(可选)
├── 3.5mm 音频口 ──► 音频输出设备(可选)
├── RJ45 网口 ──► 网络交换机/路由器
├── USB 调试口 ──► PC(串口调试)
└── DC 12V 电源 ──► 电源适配器2.3 硬件确认
上电进系统后,逐条执行下面的命令,对照注释里的期望输出:架构是 aarch64、GStreamer 可用、MPP 插件在、V4L2 和 DRM 设备节点齐全。全部对得上,说明多媒体环境就绪:
# 确认系统架构
uname -m
# 输出:aarch64
# 确认 GStreamer 版本
gst-inspect-1.0 --version
# 输出:GStreamer 1.18.x
# 确认 Rockchip MPP 插件是否可用
gst-inspect-1.0 | grep rockchip
# 输出:rockchipmpp: mpph264dec
# rockchipmpp: mpph265dec
# rockchipmpp: mppvp9dec
# ...
# 确认 V4L2 设备
ls /dev/video*
# 输出:/dev/video0 /dev/video1 ...
# 确认 DRM 显示设备
ls /dev/dri/
# 输出:card0 renderD1283. 软件准备
3.1 内核配置
自己重编内核时,需要确认下面这些选项已打开(预装的系统一般已经配好,只有重编内核时才需要逐项核对):
# Rockchip MPP 硬件编解码
CONFIG_ROCKCHIP_MPP=y
CONFIG_ROCKCHIP_MPP_SERVICE=y
CONFIG_ROCKCHIP_MPP_IEP2=y
# V4L2 视频设备
CONFIG_V4L2_MEM2KM=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_MEDIA_CAMERA_SUPPORT=y
# DRM/KMS 显示
CONFIG_DRM_ROCKCHIP=y
CONFIG_ROCKCHIP_VOP2=y
CONFIG_ROCKCHIP_DW_HDMI=y
# ALSA 音频
CONFIG_SND_SOC_ROCKCHIP=y
CONFIG_SND_SOC_RK817=y3.2 设备树配置
要接 MIPI-CSI 摄像头(例如 IMX415),需要在设备树里把摄像头挂到 CSI DPHY 接口上。下面的配置中,摄像头一侧和 DPHY 一侧的 endpoint 通过 remote-endpoint 互相指向,数据链路就接通了:
/* MIPI-CSI 摄像头接口(以 IMX415 为例) */
&csi2_dphy0 {
status = "okay";
ports {
port@0 {
reg = <0>;
csi2_dphy0_in: endpoint {
remote-endpoint = <&imx415_out>;
data-lanes = <1 2 3 4>;
};
};
};
};
&i2c3 {
status = "okay";
imx415: camera@1a {
compatible = "sony,imx415";
reg = <0x1a>;
clocks = <&cru CLK_MIPI_CAMARAOUT_M3>;
clock-frequency = <24000000>;
reset-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_LOW>;
port {
imx415_out: endpoint {
remote-endpoint = <&csi2_dphy0_in>;
data-lanes = <1 2 3 4>;
clock-lanes = <0>;
};
};
};
};3.3 GStreamer 插件安装
Debian 下用 apt 装插件包,按用途分几组:基础插件、Rockchip MPP 插件(SDK 自带)、ALSA 音频插件、Python 绑定、开发头文件,按需安装:
# Debian 系统 GStreamer 基础包
sudo apt-get update
sudo apt-get install -y \
gstreamer1.0-tools \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-plugins-ugly \
gstreamer1.0-libav
# Rockchip MPP GStreamer 插件(SDK 自带)
# 插件路径:/usr/lib/gstreamer-1.0/
ls /usr/lib/gstreamer-1.0/libgstrockchipmpp.so
# ALSA 音频插件
sudo apt-get install -y \
gstreamer1.0-alsa \
libasound2-dev
# Python GStreamer 绑定
sudo apt-get install -y \
python3-gi \
gir1.2-gstreamer-1.0 \
gir1.2-gst-plugins-base-1.0
# 开发头文件(编译自定义 GStreamer 应用时需要)
sudo apt-get install -y \
libgstreamer1.0-dev \
libgstreamer-plugins-base1.0-dev4. 操作步骤
4.1 GStreamer 命令行基础
先认识两个最常用的工具。gst-inspect-1.0 负责查:看系统装了哪些插件、每个元素支持什么参数。gst-launch-1.0 负责跑:在命令行直接搭流水线,元素之间用 ! 连接,写法类似 shell 里的管道,比如 filesrc ! decodebin ! autovideosink 的意思就是"从文件读数据、自动解码、送显示"。流水线正常结束时打印 Execution ended 一类信息,画面或文件符合预期就算成功:
# 查看所有可用插件
gst-inspect-1.0
# 查看特定插件详情
gst-inspect-1.0 rockchipmpp
# 查看元素(Element)详情
gst-inspect-1.0 mpph264dec
# 播放本地视频文件(自动选择最佳输出)
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! autovideosink
# 播放带音频的视频文件
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin name=demux \
demux. ! queue ! videoconvert ! autovideosink \
demux. ! queue ! audioconvert ! autoaudiosink4.2 视频解码流水线(H.264/H.265/VP9)
解码的关键是选对解码器:decodebin 会按码流格式自动挑;想明确走硬件,就直接写 mpp 系列(mpph264dec / mpph265dec / mppvp9dec)。另外,播放 MP4 文件要先用 qtdemux 把容器拆开,裸流则交给对应的 parse 元素整理后再解码。
H.264 硬件解码
下面三种写法效果相近:第一种最省事,后两种把每一步写明白,方便排查:
# 方法一:使用 decodebin 自动选择 MPP 解码器
gst-launch-1.0 filesrc location=/root/test_h264.mp4 ! decodebin ! \
videoconvert ! videoscale ! video/x-raw,width=1920,height=1080 ! \
autovideosink
# 方法二:显式指定 MPP H.264 解码器
gst-launch-1.0 filesrc location=/root/test.h264 ! h264parse ! \
mpph264dec ! videoconvert ! autovideosink
# 方法三:MP4 文件解码
gst-launch-1.0 filesrc location=/root/test.mp4 ! qtdemux ! h264parse ! \
mpph264dec ! videoconvert ! autovideosinkH.265 硬件解码
把元素换成 h265parse 和 mpph265dec 即可:
# H.265/HEVC 硬件解码
gst-launch-1.0 filesrc location=/root/test.h265 ! h265parse ! \
mpph265dec ! videoconvert ! autovideosink
# MP4 容器中的 H.265 解码
gst-launch-1.0 filesrc location=/root/test.mp4 ! qtdemux ! h265parse ! \
mpph265dec ! videoconvert ! autovideosinkVP9 硬件解码
VP9 裸流用 vp9parse;WebM 容器要先过 matroskademux 拆包:
# VP9 硬件解码
gst-launch-1.0 filesrc location=/root/test.vp9 ! vp9parse ! \
mppvp9dec ! videoconvert ! autovideosink
# WebM 容器中的 VP9 解码
gst-launch-1.0 filesrc location=/root/test.webm ! matroskademux ! \
vp9parse ! mppvp9dec ! videoconvert ! autovideosink4K/8K 视频解码(零拷贝模式)
RK3588 支持 8K@30fps 解码。分辨率越高越要减少不必要的转换,4K 示例里用 videoscale 一步缩放到目标分辨率输出:
# 4K H.265 解码,使用 DMA-BUF 零拷贝
gst-launch-1.0 filesrc location=/root/test_4k.mp4 ! qtdemux ! h265parse ! \
mpph265dec ! videoconvert ! videoscale ! \
video/x-raw,width=3840,height=2160 ! autovideosink
# 8K H.265 解码(RK3588 支持 8K@30fps 解码)
gst-launch-1.0 filesrc location=/root/test_8k.mp4 ! qtdemux ! h265parse ! \
mpph265dec ! videoconvert ! autovideosink4.3 视频编码流水线
编码用 mpph264enc / mpph265enc。编码器后面接 h264parse 保证 SPS/PPS 信息完整(少了这一步,生成的文件可能播不了,见 6.5 节),再用 mp4mux 封装成 MP4。摄像头采集编码的组合写法见 4.4 节。
H.264 硬件编码
# 从文件编码 H.264
gst-launch-1.0 filesrc location=/root/raw_video.yuv ! \
video/x-raw,format=I420,width=1920,height=1080,framerate=30/1 ! \
mpph264enc ! h264parse ! mp4mux ! filesink location=/root/output.mp4
# 摄像头采集编码(见 4.4 节组合流水线)H.265 硬件编码
# H.265/HEVC 硬件编码
gst-launch-1.0 filesrc location=/root/raw_video.yuv ! \
video/x-raw,format=I420,width=3840,height=2160,framerate=30/1 ! \
mpph265enc ! h265parse ! mp4mux ! filesink location=/root/output_4k.mp44.4 摄像头采集流水线(V4L2)
摄像头通过 V4L2 接口采集,对应元素是 v4l2src:device 参数指定 /dev/video 节点,video/x-raw,... 一串参数(caps)约束格式、分辨率和帧率。跑之前先按 6.3 节的方法确认设备没被占用。预览时显示器上出现画面、录制时生成可播放的文件,就说明流水线通了。
摄像头预览
# V4L2 摄像头实时预览
gst-launch-1.0 v4l2src device=/dev/video0 ! \
video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1 ! \
videoconvert ! autovideosink
# MIPI-CSI 摄像头预览(YUY2 格式)
gst-launch-1.0 v4l2src device=/dev/video0 ! \
video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1 ! \
videoconvert ! videoscale ! video/x-raw,width=1280,height=720 ! \
autovideosink摄像头采集并编码保存
num-buffers=300 限制采集帧数,采满后流水线自动结束:
# 摄像头采集 + H.264 硬件编码 + MP4 封装
gst-launch-1.0 v4l2src device=/dev/video0 num-buffers=300 ! \
video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1 ! \
videoconvert ! mpph264enc ! h264parse ! mp4mux ! \
filesink location=/root/capture.mp4
# 摄像头采集 + H.265 硬件编码
gst-launch-1.0 v4l2src device=/dev/video0 num-buffers=300 ! \
video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1 ! \
videoconvert ! mpph265enc ! h265parse ! mp4mux ! \
filesink location=/root/capture_h265.mp4多路摄像头采集
RK3588 支持多路 V4L2 设备,两路摄像头可以同时预览:
# 两路摄像头同时预览(RK3588 支持多路 V4L2 设备)
gst-launch-1.0 \
v4l2src device=/dev/video0 ! video/x-raw,width=1920,height=1080 ! \
videoconvert ! autovideosink name=sink1 \
v4l2src device=/dev/video2 ! video/x-raw,width=1280,height=720 ! \
videoconvert ! autovideosink name=sink24.5 音频流水线(ALSA)
音频分播放和录制两类。alsasrc / alsasink 对应 ALSA 设备,device 参数写成 hw:卡号,设备号 或 plughw:卡号,设备号,卡号用 arecord -l / aplay -l 查(对照 1.4 节的输出:card 0 是 ES8388,card 1 是 HDMI):
# ALSA 音频播放(WAV 文件)
gst-launch-1.0 filesrc location=/root/test.wav ! wavparse ! \
audioconvert ! autoaudiosink
# ALSA 音频录制(WAV 文件)
gst-launch-1.0 alsasrc device=hw:0,0 ! \
audio/x-raw,format=S16LE,rate=44100,channels=2 ! \
wavenc ! filesink location=/root/record.wav
# ALSA 音频播放 MP3
gst-launch-1.0 filesrc location=/root/test.mp3 ! mpegaudioparse ! \
mpg123audiodec ! audioconvert ! autoaudiosink
# 音频重采样
gst-launch-1.0 filesrc location=/root/test.wav ! wavparse ! \
audioconvert ! audio/x-raw,rate=48000 ! autoaudiosink4.6 显示输出(DRM/KMS Sink)
autovideosink 走窗口系统输出;kmssink 直接通过 DRM/KMS 把画面送上屏幕,路径更短。connector-id 指定用哪个输出接口,取值用 modetest 查(见 6.2 节)。VOP 支持多图层,两条流水线都开 enable-overlay 就能做画中画:
# DRM/KMS 直接输出到显示器(零拷贝显示)
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
videoconvert ! video/x-raw,format=NV12 ! \
kmssink connector-id=0
# 指定显示模式和分辨率
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
videoconvert ! video/x-raw,format=NV12 ! \
kmssink connector-id=0 driver-name=rockchip
# DRM 直接输出(通过 drmvideosink)
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
videoconvert ! drmvideosink
# DRM/KMS 视频叠加层(Overlay)
# RK3588 VOP 支持多图层,可实现画中画
gst-launch-1.0 \
filesrc location=/root/bg.mp4 ! decodebin ! videoconvert ! \
kmssink connector-id=0 enable-overlay=true name=bg \
filesrc location=/root/pip.mp4 ! decodebin ! videoconvert ! \
kmssink connector-id=0 enable-overlay=true name=pip4.7 完整多媒体应用流水线
视频播放器(带音频同步)
qtdemux 拆出的视频、音频两路数据各走一条支线,每条支线前放一个 queue 做缓冲,两条支线互不阻塞、并行推进:
# 完整的视频播放流水线
gst-launch-1.0 \
filesrc location=/root/movie.mp4 ! qtdemux name=demux \
demux. ! queue ! h264parse ! mpph264dec ! videoconvert ! \
videoscale ! video/x-raw,width=1920,height=1080 ! autovideosink \
demux. ! queue ! aacparse ! faad ! audioconvert ! \
audio/x-raw,rate=48000,channels=2 ! autoaudiosinkRTSP 流媒体接收
rtspsrc 从网络拉流,latency 参数控制缓冲延迟。拉流播放和拉流录像各一条命令:
# 接收 RTSP 流并播放
gst-launch-1.0 rtspsrc location=rtsp://192.168.49.100/stream latency=300 ! \
rtph264depay ! h264parse ! mpph264dec ! videoconvert ! autovideosink
# 接收 RTSP 流并录制
gst-launch-1.0 rtspsrc location=rtsp://192.168.49.100/stream latency=300 ! \
rtph264depay ! h264parse ! mp4mux ! filesink location=/root/record.mp45. 代码详解
5.1 GStreamer Pipeline 架构
写代码前先弄清三个词。Element(元素)是流水线上的处理单元,按角色分五种:产生数据的 Source、加工数据的 Filter、负责解码的 Decoder、负责编码的 Encoder、消费数据的 Sink。Pad(端口)是元素身上的数据进出口,分输出用的 Src Pad 和输入用的 Sink Pad。Caps(能力)描述端口上流动的数据格式,比如分辨率、帧率,上下游靠它协商出一致的格式:
Element(元素):
├── Source Element(源元素):产生数据
│ filesrc, v4l2src, alsasrc, rtspsrc
├── Filter Element(过滤元素):处理数据
│ videoconvert, videoscale, audioconvert
├── Decoder Element(解码元素):硬件解码
│ mpph264dec, mpph265dec, mppvp9dec
├── Encoder Element(编码元素):硬件编码
│ mpph264enc, mpph265enc
└── Sink Element(输出元素):消费数据
autovideosink, autoaudiosink, kmssink, filesink
Pad(端口):
├── Src Pad(源端口):数据输出
└── Sink Pad(接收端口):数据输入
Caps(能力协商):
定义数据格式、分辨率、帧率等参数
video/x-raw,format=NV12,width=1920,height=10805.2 Rockchip MPP 插件工作原理
MPP 插件做的事,就是把 GStreamer 的数据搬进搬出 VPU 硬件。以解码为例,从解析码流头到取出解码帧的完整流程、每一步调用的 MPP API 如下图,图末汇总了 RK3588 VPU 的硬件能力:
MPP GStreamer 插件数据流:
编码数据 (H.264/H.265/VP9 码流)
│
▼
mpph264dec / mpph265dec / mppvp9dec
│
├── 1. 解析码流头部,获取分辨率/帧率信息
├── 2. 调用 MPP API 分配解码缓冲区
│ mpp_create() → mpp_init() → mpp_open()
├── 3. 将码流数据送入 VPU 解码
│ mpp_packet_init() → mpp_dec_send()
├── 4. VPU 硬件解码完成
│ mpp_frame_get_info_change() → mpp_dec_get_frame()
├── 5. 输出 NV12/YUV420 帧数据
│ mpp_frame_get_buffer() → DMA-BUF / 内存映射
└── 6. 通过 GStreamer buffer 传递给下游元素
RK3588 VPU 硬件能力:
├── 解码:H.264 8K@30fps, H.265 8K@30fps, VP9 8K@30fps
├── 编码:H.264 4K@60fps, H.265 4K@60fps
└── 并发:支持多路同时编解码(受 VPU 通道数限制)5.3 RGA 2D 加速
RGA 是 RK3588 内置的 2D 图形加速器,缩放、旋转、格式转换这类活可以交给它,把 CPU 腾出来。在流水线里把 videoconvert 换成 rga 即可:
# 使用 RGA 进行图像缩放和格式转换
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
videoconvert ! rga video/x-raw,format=NV12,width=1280,height=720 ! \
autovideosink
# RGA 支持的操作:
# ├── 图像缩放(Resize)
# ├── 色彩空间转换(Color Space Conversion)
# ├── 图像旋转(Rotation: 0/90/180/270 度)
# ├── 图像裁剪(Crop)
# └── Alpha 混合(Blending)5.4 DMA-BUF 零拷贝机制
DMA-BUF 是 Linux 的跨设备共享内存机制。不走它,解码数据从 VPU 到显示要搬好几次;走它,解码输出的 buffer 直接交给 DRM 显示,中间零拷贝。两条路径的对比和零拷贝的成立条件如下:
传统数据流(多次内存拷贝):
VPU 解码 → 内存拷贝 → GStreamer Buffer → 内存拷贝 → DRM 显示
↑ ↑
└──── 至少 2 次内存拷贝 ────────────────────────────────┘
DMA-BUF 零拷贝(RK3588 优化路径):
VPU 解码 → DMA-BUF(共享内存)→ DRM 显示
↑ ↑
└──── 零拷贝,直接共享 buffer ──────┘
零拷贝实现条件:
1. MPP 解码输出使用 DMA-BUF allocator
2. DRM/KMS sink 接受 DMA-BUF import
3. 内核 IOMMU 支持 DMA-BUF 共享
4. GStreamer buffer 使用 GstDmaBufAllocator5.5 Python GStreamer 绑定
Python 通过 GObject Introspection(python3-gi 包)调用 GStreamer。下面三个例子的套路一致:拼出流水线描述字符串,用 Gst.parse_launch() 创建流水线,监听总线(bus)消息,收到 EOS(播放结束)或 ERROR 再退出。第一个是视频播放器,第二个是摄像头采集录像,第三个是视频转码:
#!/usr/bin/env python3
"""RK3588 GStreamer Python 示例 — 视频播放器"""
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstVideo', '1.0')
from gi.repository import Gst, GstVideo, GLib
# 初始化 GStreamer
Gst.init(None)
def on_message(bus, message, loop):
"""处理 GStreamer 消息"""
mtype = message.type
if mtype == Gst.MessageType.EOS:
print("播放结束")
loop.quit()
elif mtype == Gst.MessageType.ERROR:
err, debug = message.parse_error()
print(f"错误: {err.message}")
if debug:
print(f"调试: {debug}")
loop.quit()
elif mtype == Gst.MessageType.STATE_CHANGED:
if message.src == pipeline:
old, new, pending = message.parse_state_changed()
print(f"状态变更: {old.value_nick} -> {new.value_nick}")
return True
def play_video(file_path):
"""播放视频文件"""
global pipeline
# 构建流水线(使用 MPP H.264 解码器)
pipeline_str = (
f'filesrc location={file_path} ! qtdemux name=demux '
f'demux. ! queue ! h264parse ! mpph264dec ! videoconvert ! '
f'autovideosink '
f'demux. ! queue ! audioconvert ! autoaudiosink'
)
print(f"流水线: {pipeline_str}")
pipeline = Gst.parse_launch(pipeline_str)
# 获取消息总线
bus = pipeline.get_bus()
bus.add_signal_watch()
# 创建主循环
loop = GLib.MainLoop()
# 连接消息处理
bus.connect("message", on_message, loop)
# 启动播放
pipeline.set_state(Gst.State.PLAYING)
print("开始播放...")
try:
loop.run()
except KeyboardInterrupt:
print("用户中断")
# 停止流水线
pipeline.set_state(Gst.State.NULL)
if __name__ == "__main__":
import sys
if len(sys.argv) < 2:
print(f"用法: {sys.argv[0]} <视频文件路径>")
sys.exit(1)
play_video(sys.argv[1])#!/usr/bin/env python3
"""RK3588 GStreamer Python 示例 — 摄像头采集"""
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GLib
Gst.init(None)
def camera_capture():
"""摄像头采集并保存为 MP4 文件"""
# 摄像头采集 + H.264 硬件编码
pipeline_str = (
'v4l2src device=/dev/video0 ! '
'video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1 ! '
'videoconvert ! mpph264enc ! h264parse ! mp4mux ! '
'filesink location=/root/capture.mp4'
)
pipeline = Gst.parse_launch(pipeline_str)
bus = pipeline.get_bus()
bus.add_signal_watch()
loop = GLib.MainLoop()
def on_message(bus, message):
mtype = message.type
if mtype == Gst.MessageType.EOS:
print("录制完成")
loop.quit()
elif mtype == Gst.MessageType.ERROR:
err, debug = message.parse_error()
print(f"错误: {err.message}")
loop.quit()
bus.connect("message", on_message)
pipeline.set_state(Gst.State.PLAYING)
print("开始录制...按 Ctrl+C 停止")
try:
loop.run()
except KeyboardInterrupt:
print("停止录制...")
pipeline.send_event(Gst.Event.new_eos())
bus.connec.loop.run()
pipeline.set_state(Gst.State.NULL)
if __name__ == "__main__":
camera_capture()#!/usr/bin/env python3
"""RK3588 GStreamer Python 示例 — 带 RGA 加速的视频处理"""
import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gst, GLib
Gst.init(None)
def process_video_with_rga(input_path, output_path):
"""使用 RGA 硬件加速进行视频缩放和格式转换"""
# 读取 → 解码 → RGA 缩放 → 编码 → 输出
pipeline_str = (
f'filesrc location={input_path} ! qtdemux ! h264parse ! '
f'mpph264dec ! videoconvert ! '
f'video/x-raw,format=NV12,width=1280,height=720 ! '
f'mpph264enc ! h264parse ! mp4mux ! '
f'filesink location={output_path}'
)
pipeline = Gst.parse_launch(pipeline_str)
bus = pipeline.get_bus()
bus.add_signal_watch()
loop = GLib.MainLoop()
def on_message(bus, message):
if message.type == Gst.MessageType.EOS:
print("处理完成")
loop.quit()
elif message.type == Gst.MessageType.ERROR:
err, _ = message.parse_error()
print(f"错误: {err.message}")
loop.quit()
elif message.type == Gst.MessageType.PROGRESS:
_, percent = message.parse_progress()
print(f"进度: {percent}%", end='\r')
bus.connect("message", on_message)
pipeline.set_state(Gst.State.PLAYING)
try:
loop.run()
except KeyboardInterrupt:
pipeline.send_event(Gst.Event.new_eos())
loop.run()
pipeline.set_state(Gst.State.NULL)
if __name__ == "__main__":
process_video_with_rga("/root/input_1080p.mp4", "/root/output_720p.mp4")5.6 内核设备节点与 GStreamer 对应关系
排查问题时先看内核设备节点在不在,再看 GStreamer 有没有对应元素。常用设备的对应关系如下:
RK3588 多媒体设备节点映射:
/dev/video0, /dev/video1 ──► v4l2src element
│
└── MIPI-CSI 摄像头接口
├── /dev/media0 (media controller)
└── /dev/v4l-subdev0 (sub-device)
/dev/dri/card0 ──► kmssink element
│
├── vop@fe040000 (显示控制器)
│ ├── CRTC 0: HDMI 输出
│ └── CRTC 1: MIPI DSI 输出
└── hdmi@fe0a0000 (HDMI 2.1 TX)
/dev/snd/ ──► alsasrc / autoaudiosink
├── pcmC0D0p (playback)
├── pcmC0D0c (capture)
└── hw:0,0 / hw:0,1 (ALSA 设备)
/dev/mpp_service ──► mpph264dec / mpph265dec / mppvp9dec / mpph264enc
│
└── MPP (Media Process Platform)
├── rkvdec2@fe610000 (H.264/H.265/VP8/VP9 解码)
├── rkvenc@fe640000 (H.264/H.265 编码)
└── vdpp@fe630000 (后处理:缩放、去噪)6. 常见问题
6.1 MPP 解码插件未找到
现象:gst-inspect-1.0 rockchipmpp 无输出,或运行时报 "no element mpph264dec"。
按顺序排查:先看插件文件在不在,再看 /dev/mpp_service 设备和内核模块,最后检查 MPP 服务是否在运行:
# 检查 MPP 插件是否存在
ls -la /usr/lib/gstreamer-1.0/libgstrockchipmpp.so
# 检查 MPP 驱动是否加载
ls /dev/mpp_service
# 检查内核模块
lsmod | grep mpp
# 手动加载 MPP 模块
sudo modprobe rockchip_mpp
# 检查 MPP 服务
sudo systemctl status mpp-daemon
# 或直接运行
sudo /usr/bin/mpp-daemon &6.2 视频播放黑屏无输出
现象:流水线正常运行,但屏幕上没有画面。
从显示链路末端往回查:DRM 设备、HDMI 连接状态、VOP 控制器,最后是 kmssink 的 connector-id 是否指定正确:
# 检查 DRM 设备
ls -la /dev/dri/
# 检查显示连接器状态
cat /sys/class/drm/card0-HDMI-A-1/status
# 应输出:connected
# 检查 VOP 显示控制器
cat /sys/kernel/debug/dri/0/state | grep -A5 vop
# 使用 DRM sink 时指定 connector-id
gst-launch-1.0 filesrc location=test.mp4 ! decodebin ! \
videoconvert ! kmssink connector-id=42
# 查看可用的 connector-id
cat /sys/class/drm/card0-HDMI-A-1/status
modetest -M rockchip -c6.3 V4L2 摄像头无法打开
现象:v4l2src device=/dev/video0 报错 "Cannot identify device"。
先确认设备节点存在、设备能力正常,再确认没被其他程序占用、设备树里 CSI 状态是 okay:
# 检查 V4L2 设备
ls -la /dev/video*
# 检查设备能力
v4l2-ctl --device=/dev/video0 --all
# 检查摄像头是否被占用
fuser /dev/video0
# 检查 MIPI-CSI 设备树状态
cat /proc/device-tree/csi2-dphy0/status
# 应输出:okay
# 查看 media 设备拓扑
media-ctl -d /dev/media0 -p6.4 音频无声
现象:视频画面正常,但没有声音。
先用 aplay 直接放一个 WAV 文件,把问题定位到 GStreamer 还是 ALSA 本身,再检查音频插件和 ALSA 设备号的指定:
# 检查 ALSA 设备
arecord -l # 列出录音设备
aplay -l # 列出播放设备
# 测试 ALSA 直接播放
aplay /root/test.wav
# 检查音频设备节点
ls /dev/snd/
# 检查 GStreamer 音频插件
gst-inspect-1.0 | grep alsa
# 指定 ALSA 设备
gst-launch-1.0 filesrc location=test.mp3 ! mpegaudioparse ! \
mpg123audiodec ! audioconvert ! alsasink device=hw:0,06.5 编码输出文件无法播放
现象:编码生成的文件在播放器里打不开。
常见原因是编码器后面少了 h264parse,SPS/PPS 信息不完整;也可能是指定的编码参数不合适。用 ffprobe 看输出文件能快速定位:
# 检查编码输出格式
gst-launch-1.0 -v filesrc location=/root/raw.yuv ! \
video/x-raw,format=I420,width=1920,height=1080,framerate=30/1 ! \
mpph264enc ! h264parse ! mp4mux ! filesink location=/root/out.mp4
# 使用 h264parse 确保 SPS/PPS 信息正确
# 在 mpph264enc 后添加 h264parse
# 检查编码参数
gst-inspect-1.0 mpph264enc
# 确认 bitrate、key-int-max 等参数
# 用 ffprobe 检查输出文件
ffprobe /root/out.mp46.6 性能瓶颈 — CPU 占用过高
现象:视频播放时 CPU 占用率超过 80%。
多半是 CPU 在干硬件该干的活。依次尝试:零拷贝输出、用 RGA 替代 videoconvert 做格式转换、加 queue 让流水线各环节并行:
# 使用零拷贝模式(DMA-BUF)
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
videoconvert ! video/x-raw,format=NV12 ! \
kmssink connector-id=0
# 使用 RGA 替代 videoconvert(硬件加速色彩转换)
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
rga video/x-raw,format=NV12,width=1920,height=1080 ! \
autovideosink
# 调整 GStreamer 线程数
export GST_DEBUG=2
export GST_PLUGIN_PATH=/usr/lib/gstreamer-1.0
# 使用 queue 元素实现流水线并行
gst-launch-1.0 filesrc location=/root/test.mp4 ! decodebin ! \
queue ! videoconvert ! autovideosink \
queue ! audioconvert ! autoaudiosink相关文档
- 01_C-C++应用开发入门 -- C/C++ 开发基础
- ch02 Python 应用开发 -- Python 开发基础
- 11_音频驱动 -- ALSA 音频驱动
- 12_摄像头Camera驱动 -- V4L2 摄像头驱动
- 14_显示DRM驱动 -- DRM/KMS 显示驱动
- peripheral-drivers/ch15 HDMI 显示驱动 -- HDMI 输出驱动
- 02_内核配置与裁剪 -- 内核配置
