RK3568 uvc_app

虚拟相机

Posted by LXG on May 28, 2025

Rockchip_Quick_Start_Linux_USB_Gadget_CN.pdf

Rockchip_Trouble_Shooting_Linux4.19_USB_Gadget_UVC_CN.pdf

uvc_app 代码


rk3568_linux/external/uvc_app$ tree
.
├── camera_uvc.c
├── CMakeLists.txt
├── main.c
├── readme.md
├── uvc
│   ├── drm.c
│   ├── drm.h
│   ├── mpi_enc.c
│   ├── mpi_enc.h
│   ├── mpp_common.h
│   ├── rk_type.h
│   ├── uevent.c
│   ├── uevent.h
│   ├── uvc_control.c
│   ├── uvc_control.h
│   ├── uvc_encode.cpp
│   ├── uvc_encode.h
│   ├── uvc-gadget.c
│   ├── uvc-gadget.h
│   ├── uvc_video.cpp
│   ├── uvc_video.h
│   ├── yuv.c
│   └── yuv.h
└── uvc_config.sh

Cmake


cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
PROJECT(uvc_app)

include_directories(uvc)

set(LIB_SOURCE
    uvc/uvc-gadget.c
    uvc/uvc_video.cpp
    uvc/yuv.c
    uvc/uvc_control.c
    uvc/uvc_encode.cpp
    uvc/mpi_enc.c
    uvc/uevent.c
    uvc/drm.c
)
add_library(rkuvc SHARED ${LIB_SOURCE})

target_link_libraries(rkuvc pthread drm rockchip_mpp)

set(SOURCE
    main.c
    ${LIB_SOURCE}
)

ADD_EXECUTABLE(uvc_app ${SOURCE})

target_link_libraries(uvc_app pthread drm rockchip_mpp)

set(CAMERA_SOURCE
    camera_uvc.c
    ${LIB_SOURCE}
)

ADD_EXECUTABLE(camera_uvc ${CAMERA_SOURCE})

target_link_libraries(camera_uvc rkisp rkisp_api pthread drm rockchip_mpp)

install(TARGETS rkuvc DESTINATION lib)
install(DIRECTORY ./uvc DESTINATION include
        FILES_MATCHING PATTERN "*.h")

install(TARGETS uvc_app DESTINATION bin)
install(DIRECTORY . DESTINATION bin
        FILES_MATCHING PATTERN "*.sh"
        PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE
        GROUP_READ GROUP_WRITE GROUP_EXECUTE
        WORLD_READ WORLD_WRITE WORLD_EXECUTE)

install(TARGETS camera_uvc DESTINATION bin)

rkisp rkisp_api

RKISP(Rockchip ISP 驱动)

  • 定义:RKISP 是 Rockchip 芯片上集成的硬件图像信号处理单元(ISP)的驱动模块,负责对摄像头采集的原始图像数据进行预处理,比如去噪、自动曝光、自动白平衡、色彩校正等。
  • 作用:ISP 硬件加速图像处理,提高图像质量并降低 CPU 负担。RKISP 驱动提供接口将摄像头采集的 raw 数据转换为可用的图像数据流。
  • 硬件依赖:必须有 Rockchip SoC 集成的 ISP 硬件才支持。没有 ISP 硬件,RKISP 驱动及功能无法使用。

RKISP_API(RKISP 软件接口库)

  • 定义:RKISP_API 是一套面向应用层或中间件的软件接口库,封装了对 RKISP 驱动的调用和操作,方便应用程序控制 ISP 功能,比如启动/停止 ISP,设置参数等。
  • 作用:提供简洁的接口供摄像头应用或多媒体框架调用 ISP 功能。
  • 硬件依赖:同样需要搭配带有 ISP 硬件的芯片使用。

芯片支持情况

芯片型号 ISP 支持情况 备注
RK3568 支持硬件 ISP 具备高性能多功能 ISP,支持多路摄像头输入和高质量图像处理。适合中高端应用。
RV1106 支持硬件 ISP 针对安防/智能摄像头优化,集成低功耗 ISP,支持 RKISP 驱动。
RK3506 不支持硬件 ISP 主要面向低功耗音视频处理,无集成 ISP,需依赖外部 ISP 或软件处理。

rockchip_mpp

MPP 全称是 Media Processing Platform,是 Rockchip 提供的一套多媒体处理框架,主要用于视频编解码、图像处理等功能。

MPP 依赖于芯片内置的专用硬件多媒体加速器(比如视频编码器、解码器、图像处理单元)

芯片型号 MPP 支持情况 备注
RK3568 完整支持 内置强大多媒体硬件加速单元,支持视频编码、解码、图像处理。适合中高端多媒体应用。
RV1106 支持 具备低功耗多媒体硬件加速,针对智能摄像头及安防场景优化,支持基本的编码解码功能。
RK3506 不支持或支持有限 主要面向低功耗音视频处理,缺乏完整硬件多媒体加速单元,MPP 硬件加速功能有限或无。

drm

Direct Rendering Manager (DRM) 是 Linux 内核中的一个图形子系统,负责管理 GPU、显示控制器、帧缓冲等硬件资源。

需要硬件支持,因为 DRM 是直接管理图形硬件的

芯片型号 DRM 支持情况 备注
RK3568 完整支持 内置 GPU(Mali),Linux 下有成熟的 DRM 驱动支持,支持硬件加速和多显示输出。
RV1106 部分支持 集成较简单的图形处理单元,Linux DRM 支持有限,主要面向低功耗应用,如智能摄像头。
RK3506 支持较弱或无 图形硬件较弱或缺少独立 GPU,Linux DRM 驱动支持有限,适合轻量级显示需求。

main.c


#include <stdio.h>
#include <unistd.h>
#include "uvc_control.h"
#include "uvc_video.h"
#include "mpi_enc.h"
#include "drm.h"

int main(int argc, char* argv[])
{
    int fd;                 // DRM 设备文件描述符
    int ret;                // 函数返回值存储变量
    unsigned int handle;    // DRM 申请的缓冲句柄
    char *buffer;           // 指向映射缓冲区的指针
    int handle_fd;          // DRM 缓冲的文件描述符
    size_t size;            // 缓冲大小(字节)
    int width, height;      // 输入的图像宽高
    int y, uv;              // 用于初始化缓冲区的偏移和大小计算
    int extra_cnt = 0;      // 额外计数器,用于传递给读取函数
    uint32_t flags = 0;     // 控制标志,传递给 uvc_control_run

    // 检查命令行参数,必须传入宽和高两个参数
    if (argc != 3) {
        printf("Usage: uvc_app width height\n");
        printf("e.g. uvc_app 640 480\n");
        return -1;
    }
    // 解析宽高
    width = atoi(argv[1]);
    height = atoi(argv[2]);
    if (width == 0 || height == 0) {
        printf("Usage: uvc_app width height\n");
        printf("e.g. uvc_app 640 480\n");
        return -1;
    }

    // 打开 DRM 设备,返回文件描述符
    fd = drm_open();
    if (fd < 0)
        return -1;

    // 计算缓冲区大小(YUV420格式,宽高对齐16字节)
    size = MPP_ALIGN(width, 16) * MPP_ALIGN(height, 16) * 3 / 2;

    // 申请 DRM 缓冲区,size为大小,16为对齐,handle存储缓冲区句柄
    ret = drm_alloc(fd, size, 16, &handle, 0);
    if (ret)
        return -1;

    // 根据缓冲句柄获取文件描述符,用于映射和传递缓冲
    ret = drm_handle_to_fd(fd, handle, &handle_fd, 0);
    if (ret)
        return -1;

    // 映射缓冲区到用户空间,返回指向映射地址的指针
    buffer = (char*)drm_map_buffer(fd, handle, size);
    if (!buffer) {
        printf("drm map buffer fail.\n");
        return -1;
    }

    // 初始化buffer内容,下面部分根据宽高计算Y、UV分量的大小与偏移
    y = width * height / 4;
    memset(buffer, 128, y);           // 设置buffer前1/4部分为128
    memset(buffer + y, 64, y);        // 后续部分设置不同的固定值(似乎是测试用)
    memset(buffer + y * 2, 128, y);
    memset(buffer + y * 3, 192, y);
    uv = width * height / 8;
    memset(buffer + y * 4, 0, uv);
    memset(buffer + y * 4 + uv, 64, uv);
    memset(buffer + y * 4 + uv * 2, 128, uv);
    memset(buffer + y * 4 + uv * 3, 192, uv);

    // 启动UVC控制线程,flags表示运行模式(这里是只运行一次)
    flags = UVC_CONTROL_LOOP_ONCE;
    uvc_control_run(flags);

    // 主循环不断读取摄像头数据,处理并传递给UVC协议层发送
    while(1) {
        extra_cnt++;
        // 读取摄像头数据填充buffer,handle_fd为缓冲文件描述符,size是读取大小
        // extra_cnt和sizeof(extra_cnt)用于传递额外参数,具体含义依赖于实现
        uvc_read_camera_buffer(buffer, handle_fd, size, &extra_cnt, sizeof(extra_cnt));

        usleep(30000); // 延时约30ms,实现约30帧每秒的采样频率
    }

    // 等待UVC控制线程退出(理论上此处不会执行到,因为死循环)
    uvc_control_join(flags);

    // 清理资源,解除映射,释放缓冲,关闭设备
    drm_unmap_buffer(buffer, size);
    drm_free(fd, handle);
    drm_close(fd);

    return 0;
}

  • 该程序通过 DRM 接口申请、映射缓冲区
  • 使用 UVC 控制模块开启USB视频设备
  • 在循环中持续读取摄像头数据放入缓冲,并通过 UVC 协议层发送出去
  • 实现了一个基于 DRM 与 UVC 的简单USB摄像头采集发送示例

RV1106 是否支持 uvc_app

  • RV1106 是支持视频处理和ISP的,但其 DRM 支持较弱或者不完整,主要聚焦于 ISP 和硬件编解码。
  • 目前 Rockchip 官方对 RV1106 的视频框架主要是基于 ISP API 和硬件编码接口,而不是完整的 DRM/KMS 框架。
  • 因此,这段程序中大量依赖 DRM 缓冲管理的部分在 RV1106 上可能无法直接运行,需要移植或者改用其他接口(比如直接使用 V4L2、ISP API)。

DRM + MPP 与 V4L2(使用虚拟图像填充) 模式的对比表

对比项 DRM + MPP 模式 V4L2 模式(虚拟图像填充)
图像来源 手动填充 buffer,走 DRM 分配显存,结合 MPP 编码 构造虚拟帧内存填充(如 YUYV 数据),写入 buffer
内存分配方式 使用 drm_alloc() 获取 DMA 可映射显存 使用 malloc() 或 mmap 虚拟内存
编码流程 借助 MPP 硬件编解码库实现,如 H.264/MJPEG 编码 可选择软件/硬件编码,也可原始格式(如 YUYV)输出
硬件依赖 依赖 DRM 显存接口 + MPP 硬件(RK 系平台支持) 只需标准 V4L2 支持(可用于虚拟摄像头模拟)
典型用途 真实摄像头数据采集 + 编码推流,资源占用更低 无需摄像头,测试图像输出 / 自动化验证
开发复杂度 较高,需要初始化 DRM 和 MPP 较低,逻辑简单,易于快速验证
可移植性 受限于 DRM 和 MPP API,特定平台依赖较重 高,通用 Linux 上的 V4L2 框架即可
UVC集成 通常需要适配 buffer FD 与 UVC 推送接口 可直接 read() 或自定义 uvc_read_camera_buffer() 推送

DRM + MPP 与 V4L2(虚拟图像)性能对比表

DRM + MPP 与 V4L2(虚拟图像)性能对比表

性能项 DRM + MPP 模式 V4L2 模式(虚拟图像填充)
图像生成速度 较快(直接填充显存,内存带宽高) 快(CPU 填充,可能略慢于 DRM)
编码速度 高(使用 MPP 硬件加速,支持 H.264/MJPEG) 低(通常为软件编码或无编码)
CPU 占用 低(编码由 MPP 处理) 较高(图像构造和处理全由 CPU 执行)
内存占用 低(显存管理高效,零拷贝路径) 较高(内存复制或分配不够优化)
实时性能 优(适合 30fps、60fps 视频) 一般(依赖 CPU 性能和调度)
功耗表现 好(利用硬件引擎,CPU 负担小) 差(高 CPU 活动,尤其在 ARM SoC 上)
系统负载稳定性 高(Mature Path) 中(需小心内存泄漏、调度影响)