MEDAI-W2_RGA+DRM_HDMI输出_CSDN文章

深度解析 RK3518 平台 RGA + DRM HDMI OUT 视频输出技术:MEDAI-W2 拉流解码器的 2D 硬件合成与零拷贝送显全链路

产品背景:MEDAI-W2 是一款基于瑞芯微 RK3518 平台的拉流解码器,可通过网络拉取 RTSP/RTMP 等流媒体信号,解码后输出到 HDMI,并支持 UVC(USB Video Class)与 NET1 两路视频通道。设备内置 Web 管理系统(固件版本 2.1),在"解码设置"首页实时展示 CPU / 内存 / 核心温度,并并列呈现 HDMI1、UVC、NET1 三个输出通道状态(《MEDAI-W2 拉流解码器使用说明书》3.3 节)。

前文已解析 UVC 输入 × MPP 硬解码、V4L2/UVC 框架与自动格式协商 与 OpenGL 绿幕扣图 + DRM 送显 三条链路。本文聚焦解码之后、上屏之前的"最后一公里" :如何利用 RK3518 的 RGA(2D 图形加速)做格式转换 / 缩放 / 图层合成 ,再把结果经由 DRM/KMS 零拷贝送显到 HDMI。这条"RGA 合成 + DRM 直显"路径无需 GPU shader,是低成本、低功耗地把解码画面稳定投到 HDMI 的标准做法。


一、RK3518 视频输出与 RGA 架构总览

1.1 为什么是 "RGA + DRM",而不是 GPU

MEDAI-W2 把解码画面送到 HDMI,本质要完成三件事:色彩空间转换(NV12 → RGB)、分辨率适配(解码分辨率 → HDMI 时序分辨率)、可选的图层叠加(OSD/水印/画中画)。

方案 适用场景 代价
RGA(2D 硬件) 转格式 / 缩放 / 旋转 / 叠加 OSD 几乎零 CPU,功耗低,API 简单
GPU(Mali-450 OpenGL ES) 复杂逐像素特效(如绿幕抠像) 需写 shader,适合"像素算法"类任务
CPU 软件处理 兜底 / 调试 1080p 即打满,不可取

大多数"解码 → HDMI 显示"需求里,画面只是原样呈现或加个角标 ,没有逐像素算法------这正是 RGA 的主场。RGA 在单条 blit 指令里同时完成"转格式 + 缩放 + 叠加",结果直接落到可被 DRM 扫描的缓冲,全程不经过 CPU。

1.2 软件栈分层

复制代码
┌──────────────────────────────────────────────────┐
│  应用层:MEDAI-W2 视频服务(拉流 + RGA 合成 + 显示) │
├──────────────────────────────────────────────────┤
│  用户空间框架:MPP / RGA(librga) / GBM / libdrm     │
│   ├─ librga:rga_info_t + c_RkRgaBlit              │
│   ├─ GBM:Generic Buffer Manager                   │
│   └─ libdrm:drmModeAddFB / drmModePageFlip         │
├──────────────────────────────────────────────────┤
│  内核层:drm / rockchip-drm(VOP) / rockchip-rga     │
│   ├─ VOP(Video Output Processor,显示控制器)      │
│   ├─ RGA 字符设备(/dev/rga 或 V4L2 M2M 节点)      │
│   └─ HDMI 控制器                                   │
├──────────────────────────────────────────────────┤
│  硬件:RGA(2D 加速) / VOP / HDMI PHY               │
└──────────────────────────────────────────────────┘

核心思想:MPP 解码出 NV12(dmabuf)→ RGA 转格式/缩放/叠加写入 GBM 缓冲 → DRM 把同一块缓冲翻页扫描到 HDMI,三个环节用 fd 传递缓冲,零拷贝。


二、RGA(2D 图形加速)技术原理

2.1 RGA 能做什么

RGA(Raster Graphic Acceleration Unit)是瑞芯微 SoC 内置的 2D 图形引擎,单条指令即可组合以下操作:

能力 说明 在 MEDAI-W2 中的作用
格式转换 YUV(NV12/NV21/YUV420P) ↔ RGB(565/888/8888) VPU 的 NV12 输出 → RGBA 供扫描/编码
缩放 最近邻 / 双线性,任意比例 解码分辨率适配 HDMI 时序(如 1280×720 → 1920×1080)
旋转 / 镜像 0/90/180/270°、H/V flip 横竖屏适配、镜像预览
裁剪(Crop) 源矩形取子区域 画中画、区域选取
Alpha 混合 多图层带透明度叠加 OSD 角标、水印、画中画合成
Color Key 指定颜色透明 简单抠像(与 OpenGL 抠像互补)

关键点:转换、缩放、叠加可以在同一次 c_RkRgaBlit 里一气呵成,远比"先转格式、再缩放、再混合"的 CPU 流水线高效。

2.2 RGA 的两种访问方式

RK3518 上 RGA 有两种用户态入口,工程中任选其一:

  1. librga(厂商库,推荐) :rga/RgaApi.h 提供 c_RkRgaInit / c_RkRgaBlit,API 直观,本文示例使用此方式;
  2. V4L2 M2M(标准接口) :RGA 注册为 V4L2_BUF_TYPE_VIDEO_OUTPUT_M2M / CAPTURE_M2M 的 mem2mem 设备(节点如 /dev/videoX),用标准 VIDIOC_QBUF/streamon 驱动,跨平台兼容性更好。

两者底层都走 rockchip-rga 内核驱动,且都支持 dmabuf fd 作为输入/输出,这是零拷贝的基础。

2.3 RGA 与 dmabuf 零拷贝

RGA 的输入/输出缓冲区均可直接用 dmabuf fd 描述:

  • 输入 fd 来自 MPP 解码帧(MppBuffer,DMA-BUF/ION 分配,见《MPP 硬解码》2.4 节);
  • 输出 fd 来自 GBM 分配的扫描缓冲(见第四节)。

RGA 在硬件层面完成"读源 fd → 转换/缩放/混合 → 写目的 fd",不经过任何 CPU 拷贝,与 MPP、DRM 共享同一套 DMA-BUF 内存体系。


三、RGA 视频处理实现(参考代码)

3.1 NV12 → RGBA 转换 + 缩放(单 blit)

c 复制代码
/* MEDAI-W2:RGA 把 VPU 解码的 NV12 转 RGBA 并缩放到 HDMI 分辨率 */
#include "rga/RgaApi.h"
#include "rga/rga.h"

int rga_nv12_to_rgba(int src_fd, int dst_fd,
                     int sw, int sh, int dw, int dh)
{
    rga_info_t src = {0};
    rga_info_t dst = {0};

    /* 源:VPU 解码帧(NV12 / YUV420SP) */
    src.fd       = src_fd;
    src.mmuFlag  = 1;
    src.rect.x = 0; src.rect.y = 0;
    src.rect.width  = sw;  src.rect.height = sh;
    src.rect.wstride = sw; src.rect.hstride = sh;
    src.format   = RK_FORMAT_YCbCr_420_SP;     /* NV12 */

    /* 目的:GBM 扫描缓冲(BGRA) */
    dst.fd       = dst_fd;
    dst.mmuFlag  = 1;
    dst.rect.x = 0; dst.rect.y = 0;
    dst.rect.width  = dw;  dst.rect.height = dh;
    dst.rect.wstride = dw; dst.rect.hstride = dh;
    dst.format   = RK_FORMAT_BGRA_8888;        /* 与 DRM plane 格式匹配 */

    /* 缩放质量:双线性;单次 blit 完成 转格式+缩放 */
    if (c_RkRgaBlit(&src, &dst, NULL) < 0)
        return -1;
    return 0;
}

一句 c_RkRgaBlit 同时完成 YUV→RGB 转换 与 任意比例缩放 ,比"RGA 转格式 + 再缩放"两步更省。输出 dst_fd 是 GBM 分配的扫描缓冲(见 4.2 节),因此转完即"可上屏"。

3.2 OSD / 图层叠加(Alpha 混合)

MEDAI-W2 的 HDMI 输出常需要在画面上叠一个角标、时间戳或水印------交给 RGA 的 Alpha 混合最合适:

c 复制代码
/* 把带透明通道的 OSD 图层叠加到背景(src over dst) */
int rga_blend_osd(int bg_fd, int osd_fd, int w, int h)
{
    rga_info_t src = {0};   /* OSD 图层(RGBA,含 alpha) */
    rga_info_t dst = {0};   /* 背景(RGA 输出缓冲,已含视频) */

    dst.fd = bg_fd;  dst.mmuFlag = 1;
    dst.rect.width = w; dst.rect.height = h;
    dst.rect.wstride = w; dst.rect.hstride = h;
    dst.format = RK_FORMAT_BGRA_8888;

    src.fd = osd_fd; src.mmuFlag = 1;
    src.rect.width = w; src.rect.height = h;
    src.rect.wstride = w; src.rect.hstride = h;
    src.format = RK_FORMAT_BGRA_8888;
    src.alpha  = 0xff;        /* 全局 alpha(也可用每像素 alpha) */
    src.blend  = 1;           /* 开启 alpha 混合:src 覆盖到 dst */

    if (c_RkRgaBlit(&src, &dst, NULL) < 0)
        return -1;
    return 0;
}

同一思路可扩展到画中画(PIP):把第二路(如 NET1)缩小后叠加到主画面指定矩形,全部由 RGA 一次或多次 blit 完成。

3.3 完整合成到输出缓冲

复制代码
解码帧(NV12, dmabuf) ─┐
                       ├─→ RGA blit #1:转格式+缩放 → 背景缓冲(BGRA)
OSD/水印(RGBA, dmabuf)─┘
OSD 图层 ──→ RGA blit #2:alpha 混合 → 同一背景缓冲(叠加角标)
                       ↓
                  GBM 扫描缓冲(HDMI 可直接扫)

四、DRM/KMS HDMI 送显原理

4.1 DRM/KMS 四要素

DRM(Direct Rendering Manager)/ KMS(Kernel Mode Setting)把显示硬件抽象为:

对象 作用 在 MEDAI-W2 中
Connector 物理接口 HDMI1 接口
Encoder 时序编码为接口信号 HDMI 编码器
CRTC 扫描控制器 VOP 显示控制器
Plane / Framebuffer 待扫描缓冲 GBM 分配的 RGBA 缓冲

"送显"= 把一块 Framebuffer 绑定到 CRTC,VOP 按 HDMI 时序把它扫到屏幕。

4.2 GBM:RGA 与 DRM 的共享缓冲

要让 RGA 的输出直接被 DRM 扫描,二者必须共用同一块缓冲------由 GBM 分配:

  • gbm_surface_create(..., GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING):分配既能被 RGA 写入、又能被 DRM 扫描的缓冲;
  • RGA 的 dst_fd 就是这个 GBM buffer 导出的 dmabuf;
  • 渲染/合成完成后 gbm_surface_lock_front_buffer() 取出,注册为 DRM Framebuffer,drmModePageFlip 翻到 HDMI。

无 memcpy、无 glReadPixels------RGA 写的内存就是 DRM 扫的内存。

4.3 GBM + DRM 显示初始化(参考实现)

c 复制代码
#include <gbm.h>
#include <xf86drm.h>
#include <xf86drmMode.h>

/* 1. 打开 DRM 设备 */
int drm_fd = open("/dev/dri/card0", O_RDWR);

/* 2. 取 HDMI connector / mode(省略资源遍历,取 connected 的 HDMI) */
drmModeRes *res = drmModeGetResources(drm_fd);
drmModeConnector *conn = pick_hdmi_connector(res);   /* status==connected */
drmModeModeInfo mode = conn->modes[0];               /* 当前 HDMI 时序 */
uint32_t conn_id = conn->connector_id;
uint32_t crtc_id = pick_crtc(res, conn);

/* 3. 设置显示模式(一次即可) */
drmModeSetCrtc(drm_fd, crtc_id, 0, 0, 0, &conn_id, 1, &mode);

/* 4. 创建 GBM 设备与 surface(RGA 输出 + DRM 扫描共用) */
struct gbm_device *gbm = gbm_create_device(drm_fd);
struct gbm_surface *gs = gbm_surface_create(
        gbm, mode.hdisplay, mode.vdisplay,
        GBM_FORMAT_ARGB8888,
        GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING);

/* 5. 渲染/合成循环:RGA 写入 GBM 缓冲 → DRM 翻页 */
while (running) {
    /* 取出后置缓冲交给 RGA 写入(以 dmabuf fd 形式) */
    struct gbm_bo *bo = gbm_surface_lock_front_buffer(gs);
    int rga_dst_fd = gbm_bo_get_fd(bo);          /* 导出 dmabuf fd */

    /* RGA:NV12(解码帧) → RGBA + 缩放 → 写入该 GBM 缓冲 */
    rga_nv12_to_rgba(decoded_fd, rga_dst_fd,
                     src_w, src_h, mode.hdisplay, mode.vdisplay);
    /* 可选:叠加 OSD */
    rga_blend_osd(rga_dst_fd, osd_fd, mode.hdisplay, mode.vdisplay);

    /* 注册为 DRM Framebuffer 并翻页送显 */
    uint32_t fb_id = 0, stride = gbm_bo_get_stride(bo);
    uint32_t handle = gbm_bo_get_handle(bo).u32;
    drmModeAddFB(drm_fd, mode.hdisplay, mode.vdisplay,
                 24, 32, stride, handle, &fb_id);
    drmModePageFlip(drm_fd, crtc_id, fb_id,
                    DRM_MODE_PAGE_FLIP_EVENT, NULL);

    gbm_surface_release_buffer(gs, bo);
}

注意:上面为教学闭环,实际产品会预分配双/三缓冲、用翻页事件驱动下一帧(vsync 同步),并在 RGA 与 DRM 之间用缓冲池解耦,避免相互阻塞。

4.4 双缓冲与翻页事件

  • 双缓冲 :GBM surface 维护前/后缓冲,RGA 写后缓冲,DRM 扫前缓冲,drmModePageFlip 交换,杜绝撕裂;
  • DRM_MODE_PAGE_FLIP_EVENT:翻页在 VBlank 完成时才回调,应用据此调度下一帧,天然 vsync 同步,抖动最小。

五、MEDAI-W2 全链路:RGA 合成 + DRM HDMI 零拷贝送显

5.1 管线全景

复制代码
                    ┌────────────────────────────────────────┐
                    │           MEDAI-W2 (RK3518)            │
                    │                                        │
  RTSP/RTMP 拉流 ──┐ │   ┌─────────┐   ┌──────────────────┐  │
       或 UVC 输入 ─┼─→ │ MPP VPU │ → │  NV12 帧(dmabuf)  │  │
                  │ │   │  硬解   │   └────────┬─────────┘  │
                  │ │   └─────────┘            │           │
                  │ │                          ▼           │
                  │ │   OSD/水印 ──┐   ┌─────────────────┐  │
                  │ │              ├─→ │  RGA 2D 合成    │  │ 转格式+缩放
                  │ │  第二路(NET1)─┘   │ (dmabuf 零拷贝) │  │ +叠加/PIP
                  │ │              └─→ └────────┬────────┘  │
                  │ │                           ▼          │
                  │ │                  ┌─────────────────┐  │
                  │ │                  │ GBM/dmabuf 缓冲 │  │ RGA 写出 =
                  │ │                  │ (= DRM 扫描缓冲) │  │ DRM 扫描
                  │ │                  └────────┬────────┘  │
                  │ │                           ▼          │
                  │ │                  ┌─────────────────┐  │
                  │ │                  │ DRM/KMS 翻页    │  │ page flip
                  │ │                  │ (零拷贝送显)     │  │
                  │ │                  └────────┬────────┘  │
                  └─┼───────────────────────────┼──────────┘
                    │                           ▼
                    │                      HDMI 显示器
                    │
          (同一合成帧也可编码 → Web 双路预览 / NET1 输出)

5.2 dmabuf 如何贯穿全链路

  1. VPU 输出 NV12 即 dmabuf fd(《MPP 硬解码》2.4 节);
  2. RGA 导入该 fd 作源,写出到 GBM 分配的 dmabuf(RGA 支持 dmabuf 输入输出);
  3. GBM buffer 导出 fd 直接作为 DRM Framebuffer 句柄,drmModePageFlip 扫描;
  4. OSD/第二路同样以 dmabuf 形式喂给 RGA 做混合。

全程 无 CPU 像素搬运------这是说明书首页 CPU 长期低位(7%)却稳定投屏的根本原因。

5.3 与 Web 管理系统的映射

  • HDMI1 通道状态:由 DRM/KMS 翻页是否成功驱动,对应说明书 3.3 节"输出状态"区的 HDMI1 卡片;若显示"未连接",按 FAQ Q5 检查 HDMI 线缆与显示设备;
  • 画面预览(UVC / NET1):合成帧可再经 VPU/H.264 编码推给 Web 端做双路预览(与《UVC 输入 × MPP》3.1 节四路消费一致);
  • 系统监控:CPU 低、温度可控,正是 RGA + DRM 卸载成功的体现。

六、调试与验证

6.1 RGA 侧

bash 复制代码
# 确认 RGA 设备节点(librga 或 V4L2 M2M 两种形态)
ls -l /dev/rga*           # librga 直接访问
ls -l /dev/video*         # 若 RGA 以 V4L2 M2M 注册,用 v4l2-ctl 验证

# 内核 RGA 驱动日志
dmesg | grep -i rga

# 厂商自带测试:格式转换 / 缩放 / 混合
# 参考 SDK:external/rk-rga/test/ 下的 rga_sample

6.2 DRM / HDMI 侧

bash 复制代码
# 确认 DRM 设备节点
ls -l /dev/dri/

# 列出显示管线与当前 mode(最关键)
modetest -c                # connector + 当前 HDMI 时序
modetest -p                # plane 支持格式(确认 ARGB8888/XRGB8888)

# 内核显示驱动日志
dmesg | grep -i "drm\|rockchip\|hdmi\|vop"

# 实时观察 HDMI 中断(翻页/vsync)
cat /proc/interrupts | grep -i hdmi

6.3 典型问题定位

现象 可能原因 排查手段
HDMI 黑屏 GBM 格式不被 VOP 支持 modetest -p 确认 plane 支持 ARGB8888/XRGB8888
画面拉伸/比例错 RGA 缩放未保持宽高比 计算目标矩形留黑边,或设正确 dst 尺寸
色彩偏色 RGA 源/目的格式不匹配(NV12 vs NV21) 核对 src.format 与解码实际格式
画面撕裂 未双缓冲 / 未等翻页事件 检查 4.4 节双缓冲与 page flip
CPU 飙高 RGA 未走 dmabuf,落内存后回读 确认 src/dst 均用 dmabuf fd,无 memcpy
HDMI 显示"未连接" 线缆/显示设备问题 说明书 FAQ Q5:检查 HDMI 线缆与显示设备

七、总结

MEDAI-W2 基于 RK3518 的"RGA 合成 + DRM HDMI 送显"链路,可以归纳为 "一核调度、一块引擎、一条总线、零次拷贝":

  • 一核调度:CPU 只做拉流协议栈、参数下发与翻页事件调度,不碰像素;
  • 一块引擎:RGA 在单条 blit 里完成 转格式 + 缩放 + 叠加,覆盖绝大多数"解码→显示"需求;
  • 一条总线:GBM/dmabuf 贯穿 VPU → RGA → DRM,缓冲以 fd 形式在设备间流转;
  • 零次拷贝 :RGA 写的内存即 DRM 扫的内存,全程无 memcpy/无回读。

相比 GPU 路径,RGA + DRM 更轻量、功耗更低、API 更简单,是 MEDAI-W2 把解码画面稳定投到 HDMI1 的标准实现;而需要逐像素特效(如绿幕抠像)时,再切换到前文的 OpenGL 路径即可。两条显示链路共用同一套 GBM/dmabuf 与 DRM/KMS 基础设施,可灵活组合。该范式同样适用于 RK35 系列(RK3518/RK3528/RK3568/RK3576)上的视频网关、商业显示与拉流解码产品。


参考资料

  1. 《MEDAI-W2 拉流解码器使用说明书》(固件版本 2.1,文档版本 V1.0)
  2. 《MEDAI-W2 UVC 输入 × MPP 硬解码》技术解析(同系列文章)
  3. 《MEDAI-W2 V4L2/UVC 框架与自动格式协商》技术解析(同系列文章)
  4. 《MEDAI-W2 OpenGL 绿幕扣图 + DRM 送显》技术解析(同系列文章)
  5. Linux Kernel Documentation: Documentation/gpu/drm-kms.rst、Documentation/gpu/drm-mm.rst
  6. Rockchip librga 仓库与《Rockchip Developer Guide RGA》
  7. 瑞芯微 RK3518 平台技术资料(RGA、VOP/HDMI 章节)
相关推荐
智能直播7 小时前
MEDAI-W2_OpenGL绿幕扣图_DRMBUF_HDMI送显_CSDN文章
实时音视频·视频编解码
智能直播8 小时前
MEDAI-W2_FFmpeg多协议归一拉流_CSDN文章
视频编解码
智能直播8 小时前
MEDAI-W2_Tinyalsa音频_HDMI输出_CSDN文章
音视频·实时音视频·视频编解码
海水冷却1 天前
在线视频聊天:实时沟通正在发生怎样的变化
实时音视频
voipmaker2 天前
GB28181 对接踩坑实录:注册不上、目录不全、流卡顿,一次说清(附 2022 版变更清单)
物联网·音视频·实时音视频·信息与通信·iot·gb28181·国标gb28181
海水冷却2 天前
RTC+AI实时翻译:打破语言障碍的音视频通话方案
实时音视频
shxjnpl3 天前
从 WenetSpeech-Yue 到 SenseVoice:如何通过专项微调提升粤语语音识别效果?
人工智能·语音识别·实时音视频
shxjnpl5 天前
粤语客服录音如何实现自动质检?从 ASR 语音识别到大模型分析
人工智能·语音识别·实时音视频
事圆则缓5 天前
Flutter 与原生交互的最后一层:RTC 视频帧怎么走
flutter·音视频·实时音视频