# RTOS UVC Host Guide

## Overview

The RT-Smart user-space UVC host APIs use the `uvc_host_*` prefix, and image formats are described with `FOURCC`.

Related sample directories:

- `src/rtsmart/examples/mpp/sample_uvc_host`
- `src/rtsmart/examples/mpp/sample_uvc_dev_picture`
- `src/rtsmart/examples/mpp/sample_uvc_dev_vicap`

Currently supported input formats:

- `USBH_VIDEO_FOURCC_YUY2`
- `USBH_VIDEO_FOURCC_UYVY`
- `USBH_VIDEO_FOURCC_NV12`
- `USBH_VIDEO_FOURCC_I420`
- `USBH_VIDEO_FOURCC_MJPEG`

Among them:

- `MJPEG` is a compressed stream format
- `YUY2`, `UYVY`, `NV12`, and `I420` are raw pixel formats

## Data Structures

User-space header:

```bash
src/rtsmart/mpp/userapps/api/mpi_uvc_api.h
```

### `struct uvc_format`

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

Field description:

| Field | Description |
| --- | --- |
| `width` | Requested or negotiated image width |
| `height` | Requested or negotiated image height |
| `fourcc` | Image format, using `USBH_VIDEO_FOURCC_*` |
| `frameinterval` | Frame interval in `100ns`, for example `10000000 / 30` for `30fps` |

Notes:

- `uvc_host_init()` uses this structure both for user input and for returning the final negotiated mode.
- If the requested width, height, or frame rate cannot be matched exactly, the lower layer returns the actual negotiated result.

### `struct uvc_frame`

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

Field description:

| Field | Description |
| --- | --- |
| `index` | UVC buffer index for the current frame |
| `bytesused` | Valid data length in bytes |
| `userptr` | User-space virtual address of the current frame |
| `v_info` | Video-frame info for raw image data |
| `v_stream` | VDEC input info for `MJPEG` stream data |

Notes:

- Internal mapping fields such as `length` and `offset` are not exposed in the user-space structure.
- `userptr` is valid only while the frame is held by the application.
- After `uvc_host_put_frame()` is called, `userptr` must not be used again.
- For `MJPEG`, `bytesused` is typically used when writing the compressed data directly to a file.

## FOURCC Definitions

```c
#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 Reference

### `int uvc_host_init(struct uvc_format *fmt);`

Initialize the UVC host device.

```c
int uvc_host_init(struct uvc_format *fmt);
```

Notes:

- `fmt` carries the requested `width`, `height`, `fourcc`, and `frameinterval`.
- After successful initialization, `fmt` is updated with the negotiated mode.
- It is recommended to complete VB initialization before using this API.

Return value:

- `0` on success
- negative value on failure

### `int uvc_host_start_stream(void);`

Start the UVC video stream.

```c
int uvc_host_start_stream(void);
```

Return value:

- `0` on success
- negative value on failure

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

Get one UVC frame.

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

Notes:

- The call blocks until a frame is received or the timeout expires.
- On success, `frame` is filled with the buffer info and `userptr`.
- Every successful `uvc_host_get_frame()` must be paired with `uvc_host_put_frame()`.

Return value:

- `0` on success
- negative value on failure

### `int uvc_host_put_frame(struct uvc_frame *frame);`

Return one frame buffer to the driver.

```c
int uvc_host_put_frame(struct uvc_frame *frame);
```

Notes:

- After the buffer is returned, the driver may reuse it.
- `userptr` must not be accessed after this call.

### `void uvc_host_exit(void);`

Close the UVC device and release resources.

```c
void uvc_host_exit(void);
```

Notes:

- If streaming has already started, `uvc_host_exit()` performs the stream cleanup.
- There is currently no separate `uvc_host_stop_stream()` API.

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

Query device vendor and product information.

```c
int uvc_host_get_devinfo(char *info, int len);
```

Notes:

- On success, the returned string is typically in the form `vendor#product`.
- The API can open the device temporarily even if `uvc_host_init()` has not been called yet.

### `int uvc_host_get_formats(struct uvc_format **fmts);`

Enumerate all supported device modes.

```c
int uvc_host_get_formats(struct uvc_format **fmts);
```

Notes:

- The return value is the number of formats.
- `*fmts` is allocated internally and must be released with `uvc_host_free_formats()`.
- Each `uvc_format` entry corresponds to one concrete `width + height + fourcc + frameinterval` combination.

### `void uvc_host_free_formats(struct uvc_format **fmts);`

Release the array returned by `uvc_host_get_formats()`.

```c
void uvc_host_free_formats(struct uvc_format **fmts);
```

## Raw-Format Conversion Helpers

Besides direct frame access, three helper conversion APIs are provided:

```c
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);
```

These helpers:

- do not need an extra `struct uvc_format`
- use the format negotiated by the most recent `uvc_host_init()`
- apply only to raw pixel formats, not to `MJPEG`

### `uvc_host_raw_to_nv12`

Supported input formats:

- `YUY2`
- `UYVY`
- `NV12`
- `I420`

Required destination buffer size:

```c
dst_len >= width * height * 3 / 2
```

Notes:

- `NV12 -> NV12` is a direct copy path.
- If `dst == frame->userptr` and the current format is already `NV12`, the helper does not duplicate the copy.

### `uvc_host_raw_to_rgb565`

Supported input formats:

- `YUY2`
- `UYVY`

Required destination buffer size:

```c
dst_len >= width * height * 2
```

### `uvc_host_raw_to_yuyv`

Supported input formats:

- `YUY2`
- `UYVY`

Required destination buffer size:

```c
dst_len >= width * height * 2
```

Notes:

- `YUY2` is already in YUYV byte order.
- If the current format is `YUY2` and `dst == frame->userptr`, the helper avoids a duplicate copy.
- If the current format is `UYVY`, the helper converts it to YUYV order.

## Basic Usage Flow

The typical call order is:

1. Initialize VB.
1. Optionally call `uvc_host_get_devinfo()` or `uvc_host_get_formats()`.
1. Call `uvc_host_init()`.
1. Call `uvc_host_start_stream()`.
1. Repeatedly call `uvc_host_get_frame()` and `uvc_host_put_frame()`.
1. Call `uvc_host_exit()` before exit.

### Example 1: Write `MJPEG` Data to a File

```c
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();
```

### Example 2: Convert Raw Format to `NV12` and Send It to `VO`

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

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();
```

## Sample Program

The current host-side sample is:

```bash
src/rtsmart/examples/mpp/sample_uvc_host/uvc_test.c
```

Command line:

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

Parameter description:

| Parameter | Description |
| --- | --- |
| `connector_type` | Display type enum value |
| `rotation` | Whether to rotate, `0` or `1` |
| `fourcc` | `YUY2`, `UYVY`, `NV12`, `I420`, `MJPEG`, or a numeric value |
| `width` | Target width |
| `height` | Target height |
| `total_frame` | Number of frames to process |

Run examples:

```bash
/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
```

Notes:

- The program prints both the input `fourcc` and the final negotiated `fourcc`.
- The `MJPEG` path uses VDEC internally before display.
- The non-`MJPEG` path converts the raw frame through `uvc_host_raw_to_nv12()` and then sends it to `VO`.
- The sample periodically prints FPS.

### Get `connector_type`

Use:

```bash
list_connector
```

## Configuration Options

### `make menuconfig`

```text
RT-Smart UserSpace Examples Configuration
-> Enable MPP examples
-> Enable Build sample_uvc_host
```

### `make rtsmart-menuconfig`

```text
Components Configuration
-> Enable CherryUSB
-> Enable CherryUSB Host
-> CherryUSB Host Controller Driver (Using DesignWare Driver)

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

## Notes

1. Every successful `uvc_host_get_frame()` must be paired with `uvc_host_put_frame()`.
1. `userptr` must not be saved or reused after `uvc_host_put_frame()`.
1. If you only need device info or format enumeration, you can call `uvc_host_get_devinfo()` or `uvc_host_get_formats()` without calling `uvc_host_init()` first.
1. For the `VO` display path, converting the raw format to `NV12` first is the more common approach.
1. It is not recommended to keep a UVC camera and other long-running high-bandwidth bulk devices on the same USB hub, because USB bandwidth may become insufficient.
