RTOS UVC Host 使用说明#

概述#

RT-Smart UVC Host 用户态接口使用 uvc_host_* 命名,图像格式使用 FOURCC 描述。

相关示例目录:

  • src/rtsmart/examples/mpp/sample_uvc_host

  • src/rtsmart/examples/mpp/sample_uvc_dev_picture

  • src/rtsmart/examples/mpp/sample_uvc_dev_vicap

当前支持的输入格式为:

  • USBH_VIDEO_FOURCC_YUY2

  • USBH_VIDEO_FOURCC_UYVY

  • USBH_VIDEO_FOURCC_NV12

  • USBH_VIDEO_FOURCC_I420

  • USBH_VIDEO_FOURCC_MJPEG

其中:

  • MJPEG 是压缩码流格式

  • YUY2UYVYNV12I420 是原始像素格式

数据结构#

用户态头文件:src/rtsmart/mpp/userapps/api/mpi_uvc_api.h

struct uvc_format#

struct uvc_format {
    unsigned int width;
    unsigned int height;
    unsigned int fourcc;
    unsigned int frameinterval;
};

字段说明:

字段

说明

width

期望或协商后的图像宽度

height

期望或协商后的图像高度

fourcc

图像格式,使用 USBH_VIDEO_FOURCC_*

frameinterval

帧间隔,单位为 100ns;例如 30fps 可写为 10000000 / 30

说明:

  • uvc_host_init() 既使用它接收用户输入,也会把协商后的实际模式写回去

  • 如果用户指定的分辨率/帧率无法完全匹配,底层会返回实际协商结果

struct uvc_frame#

struct uvc_frame {
    unsigned int index;
    unsigned int bytesused;
    char *userptr;
    union {
        k_video_frame_info v_info;
        k_vdec_stream v_stream;
    };
};

字段说明:

字段

说明

index

当前帧对应的 UVC buffer 索引

bytesused

当前帧实际有效数据长度

userptr

当前帧的用户态虚拟地址

v_info

原始图像对应的视频帧信息

v_stream

MJPEG 码流对应的 VDEC 输入信息

说明:

  • 用户态结构中不包含内部使用的 buffer 映射字段,例如 lengthoffset

  • userptr 仅在该帧持有期间有效,调用 uvc_host_put_frame() 后不能继续使用

  • 对于 MJPEG,如果要直接写文件,通常使用 bytesused

FOURCC 定义#

#define USBH_VIDEO_FOURCC(a, b, c, d) \
    ((uint32_t)(uint8_t)(a) | ((uint32_t)(uint8_t)(b) << 8) | \
     ((uint32_t)(uint8_t)(c) << 16) | ((uint32_t)(uint8_t)(d) << 24))

#define USBH_VIDEO_FOURCC_YUY2  USBH_VIDEO_FOURCC('Y', 'U', 'Y', '2')
#define USBH_VIDEO_FOURCC_UYVY  USBH_VIDEO_FOURCC('U', 'Y', 'V', 'Y')
#define USBH_VIDEO_FOURCC_NV12  USBH_VIDEO_FOURCC('N', 'V', '1', '2')
#define USBH_VIDEO_FOURCC_I420  USBH_VIDEO_FOURCC('I', '4', '2', '0')
#define USBH_VIDEO_FOURCC_MJPEG USBH_VIDEO_FOURCC('M', 'J', 'P', 'G')

API 介绍#

int uvc_host_init(struct uvc_format *fmt);#

初始化 UVC Host 设备。

int uvc_host_init(struct uvc_format *fmt);

说明:

  • fmt 输入用户期望的 width / height / fourcc / frameinterval

  • 初始化成功后,fmt 会被更新为协商后的实际模式

  • 当前 UVC buffer 由底层统一管理,使用前建议先完成 VB 初始化

返回值:

  • 成功返回 0

  • 失败返回负值

int uvc_host_start_stream(void);#

启动 UVC 视频流。

int uvc_host_start_stream(void);

返回值:

  • 成功返回 0

  • 失败返回负值

int uvc_host_get_frame(struct uvc_frame *frame, unsigned int timeout_ms);#

获取一帧 UVC 数据。

int uvc_host_get_frame(struct uvc_frame *frame, unsigned int timeout_ms);

说明:

  • 该接口会阻塞,直到获取到一帧数据或超时

  • 成功后 frame 中会返回 buffer 信息和 userptr

  • 每次成功获取后,都必须配对调用一次 uvc_host_put_frame()

返回值:

  • 成功返回 0

  • 失败返回负值

int uvc_host_put_frame(struct uvc_frame *frame);#

归还一帧 buffer。

int uvc_host_put_frame(struct uvc_frame *frame);

说明:

  • 归还后该 buffer 可以重新被驱动复用

  • 调用后不能再继续使用该帧的 userptr

void uvc_host_exit(void);#

关闭 UVC 设备并释放资源。

void uvc_host_exit(void);

说明:

  • 如果流已经启动,uvc_host_exit() 会负责执行停流清理

  • 当前没有额外提供独立的 uvc_host_stop_stream()

int uvc_host_get_devinfo(char *info, int len);#

获取设备厂商/产品信息。

int uvc_host_get_devinfo(char *info, int len);

说明:

  • 成功时会返回形如 厂商#产品 的字符串

  • 即使尚未 uvc_host_init(),接口也会临时打开设备进行查询

int uvc_host_get_formats(struct uvc_format **fmts);#

枚举设备支持的全部模式。

int uvc_host_get_formats(struct uvc_format **fmts);

说明:

  • 返回值为模式数量

  • *fmts 由接口内部申请,使用完成后必须调用 uvc_host_free_formats()

  • 每一项 uvc_format 都对应一个具体的 width + height + fourcc + frameinterval

void uvc_host_free_formats(struct uvc_format **fmts);#

释放 uvc_host_get_formats() 返回的模式数组。

void uvc_host_free_formats(struct uvc_format **fmts);

原始格式转换辅助接口#

除了直接取流,当前还提供了 3 个常用原始格式转换接口:

int uvc_host_raw_to_nv12(const struct uvc_frame *frame, void *dst, size_t dst_len);
int uvc_host_raw_to_rgb565(const struct uvc_frame *frame, void *dst, size_t dst_len);
int uvc_host_raw_to_yuyv(const struct uvc_frame *frame, void *dst, size_t dst_len);

这些接口的特点:

  • 不再需要额外传入 struct uvc_format

  • 它们直接使用最近一次 uvc_host_init() 协商得到的格式

  • 只适用于原始像素格式,不适用于 MJPEG

uvc_host_raw_to_nv12#

支持输入格式:

  • YUY2

  • UYVY

  • NV12

  • I420

目标缓冲区大小要求:

dst_len >= width * height * 3 / 2

说明:

  • NV12 -> NV12 时属于直通复制

  • dst == frame->userptr 且当前格式已经是 NV12 时,不会重复拷贝

uvc_host_raw_to_rgb565#

支持输入格式:

  • YUY2

  • UYVY

目标缓冲区大小要求:

dst_len >= width * height * 2

uvc_host_raw_to_yuyv#

支持输入格式:

  • YUY2

  • UYVY

目标缓冲区大小要求:

dst_len >= width * height * 2

说明:

  • YUY2 本身就是 YUYV 字节序

  • 当当前格式为 YUY2dst == frame->userptr 时,不会重复拷贝

  • 当当前格式为 UYVY 时,会转换成 YUYV 排列

基本使用流程#

典型调用顺序如下:

  1. 初始化 VB

  2. 可选:调用 uvc_host_get_devinfo() / uvc_host_get_formats()

  3. 调用 uvc_host_init()

  4. 调用 uvc_host_start_stream()

  5. 循环调用 uvc_host_get_frame() / uvc_host_put_frame()

  6. 程序结束时调用 uvc_host_exit()

示例 1:MJPEG 数据写文件#

struct uvc_format fmt = {
    .width = 640,
    .height = 480,
    .fourcc = USBH_VIDEO_FOURCC_MJPEG,
    .frameinterval = 10000000 / 30,
};
struct uvc_frame frame;

kd_mpi_vb_set_config(&config);
kd_mpi_vb_init();

if (uvc_host_init(&fmt) != 0) {
    return -1;
}

if (uvc_host_start_stream() != 0) {
    uvc_host_exit();
    return -1;
}

if (uvc_host_get_frame(&frame, 3000) == 0) {
    FILE *file = fopen("/sdcard/test.jpg", "wb");
    if (file) {
        fwrite(frame.userptr, 1, frame.bytesused, file);
        fclose(file);
    }
    uvc_host_put_frame(&frame);
}

uvc_host_exit();

示例 2:原始格式转 NV12 后送 VO 显示#

struct uvc_format fmt = {
    .width = 640,
    .height = 480,
    .fourcc = USBH_VIDEO_FOURCC_YUY2,
    .frameinterval = 10000000 / 30,
};
struct uvc_frame frame;

/* vo_vaddr / vo_size / vf_info 由 VO 侧提前准备好 */

if (uvc_host_init(&fmt) != 0) {
    return -1;
}

if (uvc_host_start_stream() != 0) {
    uvc_host_exit();
    return -1;
}

while (uvc_host_get_frame(&frame, 5000) == 0) {
    if (uvc_host_raw_to_nv12(&frame, vo_vaddr, vo_size) == 0) {
        kd_mpi_vo_insert_frame(K_VO_LAYER_VIDEO1, &vf_info);
    }
    uvc_host_put_frame(&frame);
}

uvc_host_exit();

示例程序#

当前 Host 示例位于:

  • src/rtsmart/examples/mpp/sample_uvc_host/uvc_test.c

命令行参数:

Usage: ./sample_uvc_host [connector_type] [rotation] [fourcc] [width] [height] [total_frame]

参数说明:

参数

说明

connector_type

屏幕类型枚举值

rotation

是否旋转,01

fourcc

支持 YUY2 / UYVY / NV12 / I420 / MJPEG,也支持直接传数值

width

目标宽度

height

目标高度

total_frame

处理帧数

运行示例:

/sdcard/app/examples/mpp/sample_uvc_host.elf 20 1 MJPEG 640 480 1000000
/sdcard/app/examples/mpp/sample_uvc_host.elf 20 1 YUY2 640 480 1000000

说明:

  • 程序会打印输入 fourcc 和实际协商后的 fourcc

  • MJPEG 路径内部走 VDEC 解码后显示

  • MJPEG 路径会先调用 uvc_host_raw_to_nv12() 再送 VO 显示

  • 程序会周期性打印 FPS

获取 connector_type#

可以通过以下命令查看:

msh />list_connector

配置选项#

make menuconfig#

    > RT-Smart UserSpace Examples Configuration > Enable MPP examples
        -> Enable Build sample_uvc_host # 选中该配置

make rtsmart-menuconfig#

    > Components Configuration > Enable CherryUSB > Enable CherryUSB Host
        -> CherryUSB Host Controller Driver (Using DesignWare Driver)  #选择 (Using DesignWare Driver

    > Components Configuration > Enable CherryUSB > Enable CherryUSB Host > Enable CherryUSB Host Class Driver
        ->  Enable UVC

注意事项#

  1. uvc_host_get_frame() 成功后,必须配对调用 uvc_host_put_frame()

  2. userptr 不能跨 uvc_host_put_frame() 保存和使用。

  3. 如果只是做格式枚举或读取设备信息,可以只调用 uvc_host_get_formats() / uvc_host_get_devinfo(),不必先 uvc_host_init()

  4. 对于 VO 显示路径,当前更常见的做法是把原始格式先转换为 NV12

  5. 不建议把 UVC 摄像头和高带宽 Bulk 设备长期挂在同一个 Hub 后使用,可能会遇到 USB 带宽不足问题。

评论列表
条评论
登录