RK 音视频开发:从硬件到软件的完整链路
本文以 Rockchip(RK)平台为背景,讲清音视频数据从真实世界进入芯片、被处理和编码、最终通过网络播放的完整过程。
贯穿示例:CSI 摄像头与麦克风接入 RK 开发板,向 VLC 等客户端提供带声音的 RTSP 实时流。
不同 RK SoC(如 RK356x、RK3588、RV1106)与 SDK 的具体设备名、结构体字段、API 名称可能略有差异;但媒体链路和模块职责一致。
1. 核心认知:一条实时数据流水线
真实世界
│
├─ 摄像头光信号 ─→ Sensor ─→ MIPI-CSI ─→ VICAP ─→ ISP ─→ NV12/YUV
│ │
│ ├─ RGA:缩放/旋转/叠字
│ └─ VENC:H.264/H.265 编码
│ │
│ └─ RTSP / MP4 / WebRTC / 屏幕显示
│
└─ 麦克风声信号 ─→ Audio Codec ─→ I2S ─→ ALSA ─→ AI ─→ AENC
│
└─ AAC / G.711 / Opus → 网络或文件
音视频开发不是简单地“读取摄像头、发送网络包”,而是搭建一条尽量由硬件完成、尽量少经过 CPU 拷贝的高吞吐流水线。
2. RK 芯片内的主要硬件模块
| 硬件块 | 作用 | 常见软件接口 |
|---|---|---|
| CSI / DPHY | 接收 MIPI 摄像头高速数据 | Device Tree、V4L2 |
| VICAP | 将摄像头帧采集进内存缓冲区 | /dev/video*、VI |
| ISP | 去噪、曝光、白平衡、HDR、颜色处理 | rkaiq、ISP 参数 |
| RGA | 缩放、裁剪、旋转、颜色转换、OSD | RGA API |
| VENC | H.264/H.265/MJPEG 硬件编码 | MPP、RKMedia VENC |
| VDEC | H.264/H.265 等硬件解码 | MPP、RKMedia VDEC |
| I2S | 音频数字总线 | Device Tree、ALSA |
| Audio Codec | ADC/DAC,模拟与数字音频转换 | ALSA mixer |
| VO / DRM | 把图像送到 HDMI、MIPI 屏等显示设备 | DRM/KMS、VO |
几个容易混淆的事实:
- 摄像头通常先输出 RAW Bayer 原始像素,并非 H.264 视频。
- ISP 将 RAW 转换为可供处理、显示或编码的 YUV/NV12 图像。
- VENC 将逐帧的 NV12/YUV 图像压缩为 H.264/H.265 码流。
- RTSP 仅负责传输已经编码的码流,不负责编码。
3. 一帧视频图像经历了什么
以 1920×1080、30fps 的摄像头为例:
Sensor
↓ 输出 RAW10 Bayer 数据
MIPI CSI-2
↓
VICAP
↓ 将帧送入内存缓冲区
ISP
↓ 自动曝光、白平衡、去马赛克、降噪、颜色校正
NV12,1920×1080,30fps
↓
VENC
↓ H.264/H.265 压缩
H.264 NALU:SPS/PPS/IDR/P 帧
↓
RTSP Server
↓ RTP 打包并经 TCP/UDP 发送
VLC / NVR / Web 前端
一帧 1080P NV12 图像约占:
1920 × 1080 × 1.5 ≈ 3 MB
30fps 时,未压缩图像数据量约为:
3 MB × 30 = 90 MB/s
若 H.264 编码码率为 4 Mbps,网络侧实际数据量约为:
4 Mbps ÷ 8 = 0.5 MB/s
这解释了硬件编码器的价值:把巨大的原始图像流压缩成适合存储和网络传输的码流,同时避免 CPU 被软件编码占满。
4. 软件分层
应用程序
├─ RTSP / MP4 / WebRTC 业务逻辑
├─ RKMedia 或 MPP
└─ ALSA / V4L2 / DRM
↓
RK 内核媒体驱动
├─ rkcif / vicap
├─ rkisp
├─ rkvenc / rkvdec
├─ rga
├─ i2s / codec
└─ drm
↓
Device Tree
├─ 摄像头型号、I2C 地址
├─ MIPI lane、时钟、reset/pwdn GPIO
├─ ISP/VICAP 连接关系
└─ I2S、Codec、功放等
↓
RK 芯片与板级硬件
各层职责:
- Device Tree:描述开发板上有哪些硬件、硬件之间如何连接。
- 内核驱动:使硬件成为 Linux 可使用的设备节点。
- V4L2 / ALSA:Linux 通用视频和音频接口。
- MPP:Rockchip 较底层的媒体处理接口。
- RKMedia:对 VI、VENC、AI、AENC、RGA 等模块的较高层封装。
- rkaiq:调节 ISP 的自动曝光、白平衡、HDR、降噪等画质能力。
5. Demo 架构:Camera + Mic → RTSP
目标链路:
CSI 摄像头 ─→ VI ─→ VENC(H.264) ─→ RTSP video track
I2S 麦克风 ─→ AI ─→ AENC(G.711/AAC) ─→ RTSP audio track
RKMedia 中的关键思想是模块绑定:
VI channel 0 ── bind ──> VENC channel 0
AI device ── bind ──> AENC channel 0
绑定后:
- VI 产生 NV12 帧;
- 帧通过 DMA Buffer 直接传给 VENC;
- VENC 输出 H.264 码流;
- 应用只需要从 VENC 获取编码后码流,发送给 RTSP 客户端。
这种链路通常称为 zero-copy(零拷贝) 或 少拷贝。实际含义是:图像数据尽量不从硬件缓冲区复制到 CPU 私有内存后再复制回来。
6. Demo 的初始化与退出顺序
启动顺序:
1. 初始化系统
2. 启动 ISP(摄像头需要 ISP 时)
3. 配置并启动 VI
4. 配置并创建 VENC
5. Bind:VI → VENC
6. 配置 AI 与 AENC
7. Bind:AI → AENC
8. 创建 RTSP Server
9. 循环获取 VENC/AENC 码流并发送
退出时按相反方向执行:先停止 RTSP 和取流线程,解除 Bind,销毁编码器,关闭输入,再反初始化系统。
7. 视频核心代码骨架
以下示例以 RKMedia 风格 API 展示核心流程。请按实际 SDK 调整头文件、设备节点和字段名。
#include "rkmedia_api.h"
#include "rtsp_demo.h"
int main(void) {
RK_MPI_SYS_Init();
/* 1. 摄像头输入:VI */
VI_CHN_ATTR_S vi_attr;
memset(&vi_attr, 0, sizeof(vi_attr));
vi_attr.pcVideoNode = "rkispp_scale0";
vi_attr.u32BufCnt = 3;
vi_attr.u32Width = 1920;
vi_attr.u32Height = 1080;
vi_attr.enPixFmt = IMAGE_TYPE_NV12;
vi_attr.enWorkMode = VI_WORK_MODE_NORMAL;
RK_MPI_VI_SetChnAttr(0, 0, &vi_attr);
RK_MPI_VI_EnableChn(0, 0);
/* 2. 硬件编码器:VENC */
VENC_CHN_ATTR_S venc_attr;
memset(&venc_attr, 0, sizeof(venc_attr));
venc_attr.stVencAttr.enType = VIDEO_ID_AVC;
venc_attr.stVencAttr.imageType = IMAGE_TYPE_NV12;
venc_attr.stVencAttr.u32PicWidth = 1920;
venc_attr.stVencAttr.u32PicHeight = 1080;
venc_attr.stVencAttr.u32VirWidth = 1920;
venc_attr.stVencAttr.u32VirHeight = 1080;
venc_attr.stRcAttr.enRcMode = VENC_RC_MODE_H264CBR;
venc_attr.stRcAttr.stH264Cbr.u32BitRate = 4 * 1024;
venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateNum = 30;
venc_attr.stRcAttr.stH264Cbr.fr32DstFrameRateDen = 1;
venc_attr.stRcAttr.stH264Cbr.u32Gop = 60;
RK_MPI_VENC_CreateChn(0, &venc_attr);
/* 3. 建立零拷贝绑定:VI → VENC */
MPP_CHN_S vi_chn = {
.enModId = RK_ID_VI,
.s32DevId = 0,
.s32ChnId = 0
};
MPP_CHN_S venc_chn = {
.enModId = RK_ID_VENC,
.s32DevId = 0,
.s32ChnId = 0
};
RK_MPI_SYS_Bind(&vi_chn, &venc_chn);
/* 4. 创建 RTSP 服务 */
rtsp_demo_handle rtsp = create_rtsp_demo(554);
rtsp_session_handle session = rtsp_new_session(rtsp, "/live");
rtsp_set_video(session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
/* 5. 取得 H.264 码流并发送 */
while (1) {
MEDIA_BUFFER mb = RK_MPI_SYS_GetMediaBuffer(RK_ID_VENC, 0, -1);
if (mb) {
void *data = RK_MPI_MB_GetPtr(mb);
size_t size = RK_MPI_MB_GetSize(mb);
int64_t pts = RK_MPI_MB_GetTimestamp(mb);
rtsp_tx_video(session, data, size, pts);
rtsp_do_event(rtsp);
RK_MPI_MB_ReleaseBuffer(mb);
}
}
}
启动服务后,通常可通过 VLC 打开:
rtsp://<开发板IP>/live
8. Bind 背后的意义
下面这句是高性能链路的关键:
RK_MPI_SYS_Bind(&vi_chn, &venc_chn);
它表达的是:
VI 产生的 NV12 Buffer
↓
尽量不经过应用层 memcpy
↓
直接交给 VENC 硬件编码
如果业务需要在中间自行处理帧,例如送 NPU 推理、做算法或调试,可以不用 Bind,而由应用手动转发:
MEDIA_BUFFER frame = RK_MPI_SYS_GetMediaBuffer(RK_ID_VI, 0, -1);
RK_MPI_SYS_SendMediaBuffer(RK_ID_VENC, 0, frame);
RK_MPI_MB_ReleaseBuffer(frame);
手动转发灵活,但需要自行处理帧率节奏、缓存积压、超时与 Buffer 生命周期。
9. 音频链路
音频模块和视频模块是一一对应的:
AI = Audio Input,采集 PCM
AENC = Audio Encoder,将 PCM 编码为 G.711/AAC 等
监控场景常见参数:
采样率:16000 Hz
位宽:16 bit
声道:1
编码:G.711A
原始 PCM 数据量:
16000 samples/s × 16 bit × 1 channel = 256 kbps
G.711A 后约为:
128 kbps
音视频同步依赖 PTS:
- 视频 PTS 通常来自编码器;
- 音频 PTS 应按采样率和采样数递增;
- 不要将“收到数据的系统时间”随意混作媒体时间戳。
如果声音越来越不同步,检查音视频 PTS、丢帧、缓存积压,以及 RTSP 客户端是否正确识别音频编码。
10. ISP:决定画质的关键模块
ISP 不是编码器,但它决定图像质量。典型能力包括:
- AE:自动曝光;
- AWB:自动白平衡;
- AF:自动对焦,需要镜头马达支持;
- NR:暗光降噪;
- HDR:同时保留亮部和暗部细节;
- 锐化、去雾、畸变校正等。
推荐调试顺序:
确认 Sensor 被识别
→ 确认 MIPI/CSI 稳定收帧
→ 确认 ISP 输出图像正确
→ 再调编码码率、GOP 与低延迟
如果原始画面已经花屏、偏色、闪烁或曝光异常,优先检查 Sensor、MIPI、时钟、设备树与 ISP 参数,而不是先怀疑 H.264 编码器。
11. 常用排障路径
没有视频设备节点
dmesg | grep -Ei "isp|cif|csi|sensor|mipi"
media-ctl -p
v4l2-ctl --list-devices
重点检查:
- Sensor 驱动是否 probe 成功;
- I2C 地址是否正确;
- reset/pwdn GPIO 是否正确;
- MIPI lane 数、lane 顺序及时钟是否匹配;
- Device Tree endpoint 是否正确连接。
有画面但花屏或偏色
常见原因:
- RAW10/RAW12 等输入格式配置不一致;
- MIPI lane 或时钟质量问题;
- Sensor 寄存器初始化错误;
- ISP IQ 文件或参数不匹配;
- NV12 与 NV21 等像素格式混淆。
RTSP 可打开但黑屏
逐段验证:
v4l2-ctl 验证摄像头
→ 保存一帧 NV12/JPEG 验证 ISP
→ 将 H.264 码流写为 .h264 文件
→ 用 VLC 本地播放 .h264
→ 最后检查 RTSP
- 裸
.h264无法播放:问题多在 VI、ISP 或 VENC; - 裸
.h264可播放但 RTSP 失败:重点检查 SPS/PPS、时间戳、RTP/RTSP 封装和网络。
延迟过高
检查以下项目:
- GOP 是否过大;
- 是否使用 B 帧;
- 编码或网络缓冲是否积压;
- RTSP 是否走 TCP;
- 客户端网络缓存是否过大;
- 应用层是否发生多次
memcpy; - 是否把高分辨率图像拉到 CPU 做软件缩放。
低延迟监控的常见设置:
H.264 CBR
30 fps
GOP = 30 或 60
关闭 B 帧
使用硬件 RGA
VI → VENC Bind
客户端缩小网络缓存
12. 用“媒体图”理解整个系统
不要把 RK 音视频理解为一段孤立的摄像头编码代码。它是一张可以按业务拼接的媒体图:
Camera → VI → ISP → RGA → VENC → RTSP
├→ JPEG → 抓拍
├→ NPU → AI 检测
└→ VO → 本地屏幕
Mic → AI → AENC → RTSP / MP4
加入 AI 检测时,一个常见架构为:
VI → RGA(缩小到 640×640) → NPU
VI → VENC → RTSP
NPU 检测结果 → RGA/OSD → VENC
主码流持续以高分辨率编码,NPU 只处理缩小后的图像,检测结果以框或文字叠加回视频。这是智能摄像机常见的实现方式。
13. 推荐学习顺序
- Linux 基础:设备树、驱动日志、
dmesg、media-ctl、v4l2-ctl、ALSA。 - 摄像头链路:Sensor → MIPI → CSI → ISP → NV12。
- 硬件编解码:NV12 → H.264/H.265;理解码率、帧率、GOP、I/P/B 帧。
- RKMedia / MPP:VI、VENC、AI、AENC、RGA、Bind、
MEDIA_BUFFER。 - 协议与封装:H.264 裸流、MP4、RTSP、RTP、WebRTC。
- 系统优化:DMA Buffer、零拷贝、缓冲深度、延迟与内存带宽。
- 进阶:ISP 调优、OSD、录像、双码流、NPU 推理、WebRTC。
总结
硬件负责高吞吐处理;内核驱动负责把硬件暴露为 Linux 设备;RKMedia/MPP 负责连接媒体模块;应用负责业务和协议输出。
真正的工程能力,是能够沿着 Sensor → ISP → Buffer → 编码器 → 网络 → 播放端 逐段验证、定位和优化问题。