RGA 图像处理
目标平台: RK3576 | NPU 算力: 6 TOPS | 文档版本: V1.0
适用板型: C-3576BQ 核心板 + M4-R2 底板
最后更新: 2026-10-23
1. 概述
1.1 功能简介
RGA(Raster Graphic Acceleration,光栅图形加速器)是 RK3576 芯片内置的独立二维图形硬件加速单元,基地址以实际发布的 RK3576 SDK 为准(待板上确认)。与 GPU 承担的 3D 渲染不同,RGA 专门用于 2D 图像操作的硬件加速,包括图像缩放(Resize)、旋转(Rotation)、翻转(Flip)、格式转换(Format Conversion)、色彩空间转换(Color Space Conversion)以及 Alpha 混合(Alpha Blending)等。
M4-R2 规格书标注 RK3576 采用 ARM G52 MC3 GPU,内嵌高性能 2D 加速硬件,RGA 即用于 2D 图像操作的硬件加速单元。
librga 是瑞芯微提供的用户空间 RGA 操作库,封装了 RGA 内核驱动的 ioctl 调用,提供简洁的 C 语言 API 接口。通过 librga,开发者无需直接操作寄存器即可完成各类 2D 图像处理任务。
1.2 应用场景
- 视频后处理:视频解码后的格式转换(NV12→RGB)、分辨率缩放、画面旋转
- UI 合成:多图层 Alpha 混合、半透明效果、窗口叠加
- 摄像头预览:摄像头采集的 YUV 数据实时转换为 RGB 用于显示输出
- 数字标牌:多屏异显内容的缩放和旋转适配
- 图像预处理:AI 推理前的图像缩放、裁剪、色彩空间转换
- 屏幕镜像:多屏同显时的画面缩放与镜像处理
- 嵌入式 GUI:LVGL/Qt 等 GUI 框架的 2D 加速支持
1.3 工作原理
RGA 作为独立的硬件加速引擎,通过内核驱动接收用户空间提交的 2D 操作请求,在硬件层面执行像素级处理,结果写入目标 buffer。整个数据流如下:
┌─────────────────────────────────────────────────────────────┐
│ 用户空间应用层 │
│ │
│ 应用程序 ──► librga (API 封装) ──► ioctl 系统调用 │
│ │
├─────────────────────────────────────────────────────────────┤
│ 内核驱动层 │
│ │
│ /dev/rga ──► RGA 驱动 ──► DMA-BUF 管理 ──► 硬件调度 │
│ │
├─────────────────────────────────────────────────────────────┤
│ RGA 硬件引擎 │
│ │
│ 源图像 Buffer ──► RGA 处理单元 ──► 目标图像 Buffer │
│ │
│ 处理能力: │
│ ├── 格式转换:NV12/NV21/YUV420/RGB888/RGBA8888 互转 │
│ ├── 缩放:任意比例缩放(支持放大/缩小) │
│ ├── 旋转:0/90/180/270 度 │
│ ├── 翻转:水平翻转、垂直翻转 │
│ └── Alpha 混合:支持多种混合模式 │
│ │
├─────────────────────────────────────────────────────────────┤
│ RK3576 硬件 │
│ │
│ RGA 2D 加速器(基地址以实际 SDK 为准) │
│ DMA 控制器 (内存搬运) │
└─────────────────────────────────────────────────────────────┘RGA 的核心处理流程:
- 应用程序通过 librga 构造
rga_context上下文,配置源/目标 buffer 的地址、格式、尺寸等信息。 - 调用
RGA_runBlit()等 API 提交操作请求。 - librga 通过 ioctl 将请求传递给内核 RGA 驱动(
/dev/rga)。 - RGA 驱动将请求排队到硬件命令队列,由 RGA 引擎执行。
- RGA 硬件从源 buffer 读取像素数据,执行指定操作(缩放/旋转/格式转换等),将结果写入目标 buffer。
- 操作完成后,驱动通知用户空间,应用可使用目标 buffer 进行后续处理或显示输出。
2. 硬件准备
2.1 硬件清单
| 组件 | 型号/规格 | 数量 | 备注 |
|---|---|---|---|
| 主控板 | C-3576BQ 核心板 + ShimetaPi M4-R2 V10 底板 | 1 | 运行 Debian/Buildroot 系统 |
| 调试串口线 | USB 转 TTL(3.3V) | 1 | 波特率 1500000 |
| HDMI 显示器 | 支持 1080p 或更高分辨率 | 1 | 用于显示输出验证 |
| 网线 | 标准 RJ45 | 1 | 用于网络和 SSH 连接 |
| 电源 | 5V/2A(Type-C 或 6pin) | 1 | M4-R2 供电 |
2.2 硬件连接
ShimetaPi M4-R2 V10 底板连接:
├── HDMI 输出 ──► HDMI 显示器
├── RJ45 网口 ──► 网络交换机/路由器
├── USB 调试口 ──► PC(串口调试)
└── 电源 ──► 5V/2A 电源适配器2.3 硬件确认
# 确认系统架构
uname -m
# 输出:aarch64
# 确认内核版本(应为 6.1)
uname -r
# 输出:6.1.x-rockchip
# 确认 RGA 设备节点
ls -la /dev/rga
# 输出:crw-rw---- 1 root video 10, 123 ...
# 确认 RGA 设备树节点
cat /proc/device-tree/rga/status
# 输出:okay
# 确认 librga 库是否安装
ldconfig -p | grep librga
# 输出:librga.so.2 => /usr/lib/librga.so.23. 软件准备
3.1 内核配置
RGA 硬件加速需要以下内核配置项支持(配置项名称以实际发布的 RK3576 SDK 内核为准):
# RGA 驱动(必须)
CONFIG_ROCKCHIP_RGA=y
# DRM 支持(RGA 通过 DRM 获取 DMA-BUF)
CONFIG_DRM_ROCKCHIP=y
# DMA-BUF 支持
CONFIG_DMA_SHARED_BUFFER=y
CONFIG_DMABUF=y
# 编译内核模块验证
lsmod | grep rga
# 输出:rockchip_rga3.2 设备树配置
RK3576 RGA 设备树节点(通常已由官方 DTS 默认启用,节点名与地址以实际发布的 RK3576 SDK 为准):
/* RK3576 RGA 设备树节点(示例) */
rga: rga@<base_addr> {
compatible = "rockchip,rk3576-rga";
reg = <0x0 0x<base_addr> 0x0 0x1000>;
interrupts = <GIC_SPI xxx IRQ_TYPE_LEVEL_HIGH>;
clocks = <&cru ACLK_RGA>, <&cru HCLK_RGA>,
<&cru DCLK_RGA>;
clock-names = "aclk", "hclk", "dclk";
resets = <&cru SRST_A_RGA>, <&cru SRST_H_RGA>,
<&cru SRST_D_RGA>;
reset-names = "axi", "ahb", "dsys";
status = "okay";
};以上设备树节点仅为平台通用结构示例,RK3576 实际的 RGA 节点名、寄存器基地址、中断号与时钟 ID 以实际发布的 RK3576 SDK 设备树源码为准(待板上确认)。
验证设备树状态:
# 确认 RGA 设备树节点状态
cat /proc/device-tree/rga/status
# 输出:okay
# 查看 RGA 时钟信息
cat /sys/kernel/debug/clk/clk_summary | grep rga3.3 librga 安装
librga 是 Rockchip 提供的用户空间 RGA 操作库,源码位于 SDK 的 external/linux-rga 目录。
# ===== 方法一:Debian 系统安装预编译包 =====
sudo apt-get update
sudo apt-get install -y librga-dev
# 验证安装
pkg-config --modversion librga
dpkg -l | grep librga
# ===== 方法二:从源码编译安装 =====
# 获取 librga 源码(从 Rockchip SDK)
cd /opt
git clone https://github.com/airockchip/librga.git
cd librga
# 编译
make clean
make -j$(nproc)
# 安装
sudo make install
sudo ldconfig
# 验证安装
ls -la /usr/lib/librga*
# 输出:/usr/lib/librga.so -> librga.so.2
# /usr/lib/librga.so.2
# /usr/lib/librga.a
ls -la /usr/include/rga/
# 输出:RockchipRga.h rga.h rgaType.h ...
# ===== 方法三:通过 GStreamer RGA 插件 =====
# 如果需要在 GStreamer 流水线中使用 RGA
sudo apt-get install -y libgstreamer-plugins-bad1.0-dev
# GStreamer 内置 rga element
gst-inspect-1.0 | grep rga4. 操作步骤
4.1 RGA 基础测试
# 检查 RGA 设备节点
ls -la /dev/rga
# 查看 RGA 驱动信息
dmesg | grep -i rga
# 测试 RGA 基本 Blit 操作
cat > /tmp/test_rga.sh << 'EOF'
#!/bin/bash
echo "=== RGA 基础功能测试 ==="
# 检查 RGA 设备
if [ ! -c /dev/rga ]; then
echo "错误:/dev/rga 设备不存在"
exit 1
fi
echo "RGA 设备节点:OK"
# 检查 librga
if ldconfig -p | grep -q librga; then
echo "librga 库:已安装"
else
echo "错误:librga 未安装"
exit 1
fi
echo "=== 基础测试完成 ==="
EOF
chmod +x /tmp/test_rga.sh
/tmp/test_rga.sh4.2 格式转换
RGA 支持多种像素格式之间的硬件加速转换,包括 YUV(NV12、NV21、YUV420)与 RGB(RGB888、RGBA8888、ARGB8888)之间的互转。
# 使用 GStreamer rga element 进行 NV12 → RGB 格式转换
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=RGB888 ! \
filesink location=/root/output_rgb.raw
# YUV420 → RGBA8888 转换
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=I420,width=1920,height=1080 ! \
rga video/x-raw,format=RGBA8888 ! \
filesink location=/root/output_rgba.raw
# NV21 → NV12 格式转换(色度平面交换)
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV21,width=1920,height=1080 ! \
rga video/x-raw,format=NV12 ! \
filesink location=/root/output_nv12.raw4.3 图像缩放
# 1080p 缩小到 720p
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=NV12,width=1280,height=720 ! \
filesink location=/root/output_720p.nv12
# 720p 放大到 1080p
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1280,height=720 ! \
rga video/x-raw,format=NV12,width=1920,height=1080 ! \
filesink location=/root/output_1080p.nv12
# 从视频文件读取并缩放
gst-launch-1.0 filesrc location=/root/input.mp4 ! \
decodebin ! videoconvert ! \
rga video/x-raw,format=NV12,width=640,height=480 ! \
filesink location=/root/output_480p.nv12最大缩放分辨率取决于 RK3576 RGA 硬件规格,需板端实测(待板上验证)。
4.4 旋转翻转
# 水平翻转(左右镜像)
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=NV12,flip-method=horizontal ! \
autovideosink
# 垂直翻转(上下镜像)
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=NV12,flip-method=vertical ! \
autovideosink
# 180 度旋转
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=NV12,rotation=180 ! \
autovideosink
# 90 度旋转
gst-launch-1.0 videotestsrc num-buffers=100 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=NV12,rotation=90 ! \
autovideosink4.5 Alpha 混合
# 两个视频流的 Alpha 混合
gst-launch-1.0 \
videotestsrc num-buffers=300 ! video/x-raw,format=RGBA,width=1920,height=1080 ! \
mix. videotestsrc num-buffers=300 pattern=ball ! \
video/x-raw,format=RGBA,width=1920,height=1080 ! \
mix. videomixer name=mix sink_0::alpha=0.7 sink_1::alpha=0.3 ! \
autovideosink
# 使用 rga element 做前景/背景混合
gst-launch-1.0 \
filesrc location=/root/background.mp4 ! decodebin ! videoconvert ! \
mix.sink_0 videotestsrc num-buffers=300 pattern=ball ! videoconvert ! \
mix.sink_1 videomixer name=mix ! autovideosink4.6 性能测试
# 测试 RGA 处理帧率
# 方法一:使用 GStreamer 统计信息
gst-launch-1.0 videotestsrc num-buffers=600 ! \
video/x-raw,format=NV12,width=1920,height=1080,framerate=60/1 ! \
rga video/x-raw,format=NV12,width=1920,height=1080 ! \
fakesink -v 2>&1 | grep -E "frames|fps"
# 方法二:对比 RGA 与软件转换性能
# 软件转换(CPU)
time gst-launch-1.0 videotestsrc num-buffers=300 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
videoconvert ! fakesink
# RGA 硬件转换
time gst-launch-1.0 videotestsrc num-buffers=300 ! \
video/x-raw,format=NV12,width=1920,height=1080 ! \
rga video/x-raw,format=RGB888 ! fakesink
# 方法三:使用 librga 直接测试吞吐
cat > /tmp/rga_perf_test.c << 'CEOF'
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "rga/RockchipRga.h"
#define TEST_WIDTH 1920
#define TEST_HEIGHT 1080
#define TEST_COUNT 100
static double get_time_ms(void) {
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0;
}
int main() {
rga_context rga_ctx;
rga_info_t src, dst;
int ret;
ret = rga_init(&rga_ctx);
if (ret != 0) {
printf("RGA 初始化失败\n");
return -1;
}
memset(&src, 0, sizeof(rga_info_t));
memset(&dst, 0, sizeof(rga_info_t));
src.fd = -1;
src.width = TEST_WIDTH;
src.height = TEST_HEIGHT;
src.format = RK_FORMAT_YCbCr_420_SP;
dst.fd = -1;
dst.width = TEST_WIDTH;
dst.height = TEST_HEIGHT;
dst.format = RK_FORMAT_RGB_888;
double start = get_time_ms();
for (int i = 0; i < TEST_COUNT; i++) {
rga_set_rect(&src.rect, 0, 0, TEST_WIDTH, TEST_HEIGHT);
rga_set_rect(&dst.rect, 0, 0, TEST_WIDTH, TEST_HEIGHT);
ret = RGA_runBlit(&rga_ctx, &src, &dst, NULL);
if (ret != 0) {
printf("RGA Blit 失败: %d\n", ret);
break;
}
}
double end = get_time_ms();
double elapsed = end - start;
double fps = TEST_COUNT / (elapsed / 1000.0);
printf("RGA 性能测试结果 (%dx%d NV12->RGB):\n", TEST_WIDTH, TEST_HEIGHT);
printf(" 总帧数: %d\n", TEST_COUNT);
printf(" 总耗时: %.2f ms\n", elapsed);
printf(" 平均帧耗时: %.2f ms\n", elapsed / TEST_COUNT);
printf(" 吞吐帧率: %.2f fps\n", fps);
rga_deinit(&rga_ctx);
return 0;
}
CEOF
# 编译并运行(需要板上环境)
# gcc -o /tmp/rga_perf_test /tmp/rga_perf_test.c -lrga
# /tmp/rga_perf_test5. 代码详解
5.1 RGA API 详解
librga 的核心 API 位于 RockchipRga.h,主要接口如下:
#include <rga/RockchipRga.h>
/* 1. 初始化 RGA 上下文 */
rga_context rga_ctx;
int ret = rga_init(&rga_ctx);
// 返回 0 表示成功
/* 2. 配置源图像信息 */
rga_info_t src;
memset(&src, 0, sizeof(rga_info_t));
src.fd = src_fd; // DMA-BUF 文件描述符(-1 表示使用内存地址)
src.width = 1920; // 源图像宽度
src.height = 1080; // 源图像高度
src.format = RK_FORMAT_YCbCr_420_SP; // NV12 格式
src.buf = src_buffer; // 内存地址(当 fd=-1 时使用)
src.handles[0] = 0; // GEM handle(如使用 DMA-BUF 则为 0)
// 源图像裁剪区域
rga_set_rect(&src.rect, 0, 0, 1920, 1080);
/* 3. 配置目标图像信息 */
rga_info_t dst;
memset(&dst, 0, sizeof(rga_info_t));
dst.fd = dst_fd;
dst.width = 1280;
dst.height = 720;
dst.format = RK_FORMAT_RGB_888;
dst.buf = dst_buffer;
// 目标图像放置区域
rga_set_rect(&dst.rect, 0, 0, 1280, 720);
/* 4. 配置旋转/翻转(可选) */
src.rotation = HAL_TRANSFORM_ROT_90; // 旋转 90 度
// 其他选项:HAL_TRANSFORM_FLIP_H(水平翻转)
// HAL_TRANSFORM_FLIP_V(垂直翻转)
// HAL_TRANSFORM_ROT_180(180 度旋转)
// HAL_TRANSFORM_ROT_270(270 度旋转)
/* 5. 执行 Blit 操作 */
ret = RGA_runBlit(&rga_ctx, &src, &dst, NULL);
/* 6. 释放资源 */
rga_deinit(&rga_ctx);支持的像素格式常量:
| 常量 | 说明 | 位深 |
|---|---|---|
RK_FORMAT_RGB_888 | RGB888 | 24bit |
RK_FORMAT_RGBA_8888 | RGBA8888 | 32bit |
RK_FORMAT_ARGB_8888 | ARGB8888 | 32bit |
RK_FORMAT_YCbCr_420_SP | NV12 | 12bit |
RK_FORMAT_YCrCb_420_SP | NV21 | 12bit |
RK_FORMAT_YCbCr_420_P | I420 (YUV420P) | 12bit |
RK_FORMAT_YUYV_422 | YUYV (YUV422) | 16bit |
RK_FORMAT_RGB_565 | RGB565 | 16bit |
5.2 DRM RGA 使用
在 DRM/KMS 显示流水线中使用 RGA 进行格式转换和缩放:
#include <xf86drm.h>
#include <libdrm/drm.h>
#include "rga/RockchipRga.h"
/*
* DRM + RGA 零拷贝处理流程:
* 1. 从 VPU/摄像头获取 DMA-BUF fd
* 2. 通过 RGA 进行格式转换/缩放/旋转
* 3. 将结果 DMA-BUF fd 提交给 DRM 进行显示
*/
int drm_rga_display_example(int src_buf_fd, int src_w, int src_h,
int dst_w, int dst_h) {
rga_context rga_ctx;
rga_info_t src, dst;
// 初始化 RGA
if (rga_init(&rga_ctx) != 0) {
fprintf(stderr, "RGA 初始化失败\n");
return -1;
}
// 配置源:DMA-BUF fd(来自 VPU 解码输出或摄像头采集)
memset(&src, 0, sizeof(rga_info_t));
src.fd = src_buf_fd;
src.width = src_w;
src.height = src_h;
src.format = RK_FORMAT_YCbCr_420_SP; // NV12
rga_set_rect(&src.rect, 0, 0, src_w, src_h);
// 配置目标:DMA-BUF fd(DRM 分配的帧缓冲)
memset(&dst, 0, sizeof(rga_info_t));
dst.fd = drm_framebuffer_fd; // DRM framebuffer 的 DMA-BUF fd
dst.width = dst_w;
dst.height = dst_h;
dst.format = RK_FORMAT_XRGB_8888; // DRM 显示格式
rga_set_rect(&dst.rect, 0, 0, dst_w, dst_h);
// 执行 Blit(NV12 → RGB + 缩放)
int ret = RGA_runBlit(&rga_ctx, &src, &dst, NULL);
if (ret != 0) {
fprintf(stderr, "RGA Blit 失败: %d\n", ret);
rga_deinit(&rga_ctx);
return -1;
}
// 提交 DRM 显示
drmModePageFlip(fd, crtc_id, dst_fb_id, DRM_MODE_PAGE_FLIP_EVENT, data);
rga_deinit(&rga_ctx);
return 0;
}5.3 常用操作示例
示例一:摄像头画面旋转显示
/*
* 场景:竖屏显示摄像头画面(摄像头横置,需要旋转 90 度后显示)
* 摄像头输出 NV12 1920x1080 → RGA 旋转 90° → DRM 显示 1080x1920
*/
#include "rga/RockchipRga.h"
int camera_rotate_display(int camera_buf_fd) {
rga_context rga_ctx;
rga_info_t src, dst;
rga_init(&rga_ctx);
// 源:摄像头 NV12 1920x1080
memset(&src, 0, sizeof(rga_info_t));
src.fd = camera_buf_fd;
src.width = 1920;
src.height = 1080;
src.format = RK_FORMAT_YCbCr_420_SP;
src.rotation = HAL_TRANSFORM_ROT_90; // 旋转 90 度
rga_set_rect(&src.rect, 0, 0, 1920, 1080);
// 目标:旋转后 1080x1920,NV12 格式
memset(&dst, 0, sizeof(rga_info_t));
dst.fd = display_buf_fd;
dst.width = 1080;
dst.height = 1920;
dst.format = RK_FORMAT_YCbCr_420_SP;
rga_set_rect(&dst.rect, 0, 0, 1080, 1920);
RGA_runBlit(&rga_ctx, &src, &dst, NULL);
rga_deinit(&rga_ctx);
return 0;
}示例二:画中画(PiP)叠加
/*
* 场景:大画面中叠加一个小窗口(画中画)
* 主画面 1920x1080 + 小窗口 480x270 叠加到右下角
*/
#include "rga/RockchipRga.h"
int pip_overlay(int main_buf_fd, int pip_buf_fd) {
rga_context rga_ctx;
rga_info_t src_main, src_pip, dst;
rga_init(&rga_ctx);
// 第一步:将主画面 Blit 到目标
memset(&src_main, 0, sizeof(rga_info_t));
src_main.fd = main_buf_fd;
src_main.width = 1920;
src_main.height = 1080;
src_main.format = RK_FORMAT_YCbCr_420_SP;
rga_set_rect(&src_main.rect, 0, 0, 1920, 1080);
memset(&dst, 0, sizeof(rga_info_t));
dst.fd = output_buf_fd;
dst.width = 1920;
dst.height = 1080;
dst.format = RK_FORMAT_YCbCr_420_SP;
rga_set_rect(&dst.rect, 0, 0, 1920, 1080);
RGA_runBlit(&rga_ctx, &src_main, &dst, NULL);
// 第二步:将小画面缩放并叠加到右下角
memset(&src_pip, 0, sizeof(rga_info_t));
src_pip.fd = pip_buf_fd;
src_pip.width = 1920;
src_pip.height = 1080;
src_pip.format = RK_FORMAT_YCbCr_420_SP;
rga_set_rect(&src_pip.rect, 0, 0, 1920, 1080);
// 目标区域:右下角 480x270
memset(&dst, 0, sizeof(rga_info_t));
dst.fd = output_buf_fd;
dst.width = 1920;
dst.height = 1080;
dst.format = RK_FORMAT_YCbCr_420_SP;
rga_set_rect(&dst.rect, 1440, 810, 480, 270); // 右下角偏移
RGA_runBlit(&rga_ctx, &src_pip, &dst, NULL);
rga_deinit(&rga_ctx);
return 0;
}示例三:图像格式批量转换(YUV → RGB 用于 AI 推理)
/*
* 场景:将摄像头采集的 YUV 帧批量转换为 RGB,供 AI 推理使用
* 这是 RK3576 在 AI 视觉应用中的典型场景
*/
#include <stdio.h>
#include "rga/RockchipRga.h"
void batch_yuv_to_rgb(int *input_fds, int *output_fds,
int count, int width, int height) {
rga_context rga_ctx;
rga_init(&rga_ctx);
for (int i = 0; i < count; i++) {
rga_info_t src, dst;
memset(&src, 0, sizeof(rga_info_t));
src.fd = input_fds[i];
src.width = width;
src.height = height;
src.format = RK_FORMAT_YCbCr_420_SP; // NV12
rga_set_rect(&src.rect, 0, 0, width, height);
memset(&dst, 0, sizeof(rga_info_t));
dst.fd = output_fds[i];
dst.width = width;
dst.height = height;
dst.format = RK_FORMAT_RGB_888; // RGB888
rga_set_rect(&dst.rect, 0, 0, width, height);
RGA_runBlit(&rga_ctx, &src, &dst, NULL);
}
rga_deinit(&rga_ctx);
}5.4 librga 与 GStreamer rga element 的区别
| 特性 | librga (C API) | GStreamer rga element |
|---|---|---|
| 使用方式 | C 代码调用 API | GStreamer Pipeline 命令行/代码 |
| 适用场景 | 自定义应用、嵌入式程序 | 多媒体流水线、视频处理 |
| 灵活度 | 高,可精确控制每个参数 | 中,受 GStreamer caps 约束 |
| 学习成本 | 较高,需理解 RGA API | 较低,熟悉 GStreamer 即可 |
| DMA-BUF 支持 | 直接操作 fd | 自动管理 |
6. 常见问题
6.1 RGA 设备节点不存在
现象: /dev/rga 文件不存在,dmesg 无 RGA 相关输出。
解决方案:
# 检查内核配置
zcat /proc/config.gz | grep ROCKCHIP_RGA
# 应输出:CONFIG_ROCKCHIP_RGA=y 或 =m
# 如果为模块,手动加载
sudo modprobe rockchip_rga
# 检查设备树
cat /proc/device-tree/rga/status
# 如果输出 not okay,需要修改设备树6.2 RGA 操作报错 "Invalid argument"
现象: 调用 RGA_runBlit() 返回 -1,errno 为 EINVAL。
解决方案:
# 常见原因:输入/输出格式不支持或参数非法
# 检查图像尺寸是否超出硬件限制(以 RK3576 RGA 实际规格为准)
# 确认 src.width, src.height, dst.width, dst.height 均在范围内
# 检查 buffer 是否已分配
# src.fd 和 dst.fd 必须是有效的 DMA-BUF fd
# 或 src.buf 和 dst.buf 是有效的内存地址
# 调试方法:开启 RGA 驱动调试日志
echo 8 > /proc/sys/kernel/printk
echo 0x1f > /sys/module/rockchip_rga/parameters/debug_mask6.3 RGA 处理结果图像颜色异常
现象: 格式转换后图像颜色偏绿、偏紫或出现色块。
解决方案:
# 常见原因:源/目标像素格式配置不匹配
# 检查 NV12 和 NV21 的区别:
# NV12: Y + 交替的 Cb Cr(U 在前)
# NV21: Y + 交替的 Cr Cb(V 在前)
# 确认摄像头输出的实际格式
v4l2-ctl --device=/dev/video0 --list-formats-ext
# 确认 RGA 格式设置与输入数据一致
# 如果摄像头输出 NV21 但 RGA 配置为 NV12,颜色会异常
# YUV RGB 转换公式不同时也会导致偏色
# 确认 RGA 使用的色彩空间标准(BT.601 / BT.709)6.4 RGA 处理速度慢,未达到预期性能
现象: RGA 处理帧率低于预期。
解决方案:
# 检查 RGA 时钟频率
cat /sys/kernel/debug/clk/clk_summary | grep rga
# 确认使用了 DMA-BUF 而非内存拷贝
# 使用 fd 方式传递 buffer 可避免内存拷贝开销
# 错误做法:malloc + memcpy → RGA
# 正确做法:DMA-BUF fd 直接传递
# 检查 CPU 负载
top -bn1 | head -5
# 如果 RGA 处理期间 CPU 占用仍然很高,说明可能没有走硬件路径
# 检查是否多线程竞争
# RGA 硬件同时只能处理一个操作队列
# 多线程并发提交可能导致排队等待6.5 librga 链接错误
现象: 编译时出现 undefined reference to 'rga_init' 等链接错误。
解决方案:
# 确认 librga 已安装
ls /usr/lib/librga*
ls /usr/include/rga/RockchipRga.h
# 编译时添加链接选项
gcc -o my_app my_app.c -lrga -I/usr/include/rga
# 如果 librga 安装在非标准路径
gcc -o my_app my_app.c -L/path/to/librga -lrga -I/path/to/include/rga
# 使用 pkg-config(如果支持)
gcc -o my_app my_app.c $(pkg-config --cflags --libs librga)
# 动态库路径问题
export LD_LIBRARY_PATH=/usr/lib:$LD_LIBRARY_PATH
ldconfig6.6 多线程并发 RGA 操作死锁
现象: 多个线程同时调用 RGA API 时程序卡死或崩溃。
解决方案:
// RGA 不是线程安全的,需要应用层加锁
#include <pthread.h>
static pthread_mutex_t rga_mutex = PTHREAD_MUTEX_INITIALIZER;
void thread_safe_rga_blit(rga_context *ctx, rga_info_t *src, rga_info_t *dst) {
pthread_mutex_lock(&rga_mutex);
RGA_runBlit(ctx, src, dst, NULL);
pthread_mutex_unlock(&rga_mutex);
}
// 或者为每个线程创建独立的 rga_context
// 每个线程使用 rga_init() 初始化自己的上下文6.7 RGA 处理后图像内容不完整或偏移
现象: 输出图像只显示部分内容,或内容位置偏移。
解决方案:
// 检查 src.rect 和 dst.rect 的设置
// rect 的 x, y 是左上角坐标,width/height 是宽高
// 错误示例:rect 超出 buffer 边界
src.rect.x = 0;
src.rect.y = 0;
src.rect.width = 3840; // 但 src.width 只有 1920
src.rect.height = 2160; // 但 src.height 只有 1080
// 正确示例:
src.rect.x = 0;
src.rect.y = 0;
src.rect.width = src.width; // 不超过源 buffer 尺寸
src.rect.height = src.height;
dst.rect.x = 0;
dst.rect.y = 0;
dst.rect.width = dst.width; // 不超过目标 buffer 尺寸
dst.rect.height = dst.height;7. 相关文档
| 文档名称 | 路径 | 说明 |
|---|---|---|
| 显示框架 | 显示框架 | DRM/KMS 显示与 RGA 零拷贝 |
| 摄像头应用 | 摄像头应用 | 摄像头采集与 RGA 联合使用 |
| 视频编解码 (MPP) | 视频编解码(MPP) | 解码后格式转换与缩放 |
| 音频开发与 ALSA | 音频开发与ALSA | 音频采集与播放 |
| C/C++ 应用开发入门 | C/C++应用开发入门 | 编译环境与基础开发 |
| 模型转换与量化 | 模型转换与量化 | AI 推理前图像预处理 |
