UVC
1. Overview
This section mainly explains the workflow of UVC and how customers can use UVC cameras.
For example projects on using UVC CAMERA, please refer to:
For detailed information on using UVC CAMERA, please refer to:
For UVC CAMERA API reference, please refer to:
For API reference on using frame buffer related API interfaces to implement image memory buffer management, please refer to:
2. Supported Features
Supports up to 4 UVCs output simultaneously, configured by the macro UVC_PORT_MAX (CONFIG_USBHOST_HUB_MAX_EHPORTS);
Supports dual streams, which means a single camera can output two types of streams simultaneously;
Supports up to 8 resolutions by default. If the camera supports more resolutions, corresponding software modifications are needed by changing the macro: USBH_VIDEO_FRAME_MAX_NUM;
Users need to enable the corresponding features through configuration parameters according to their application scenarios
4. UVC Camera Usage Flow
Power on UVC camera
Create UVC camera controller
Open UVC camera
Close UVC camera
Delete UVC camera
Suspend UVC camera
Resume UVC camera
Power off UVC camera
4.1 Power on UVC camera
Call the following interface to power on the UVC camera:
bk_err_t bk_usbh_hub_multiple_devices_power_on(E_USB_HOST_MODE_T mode, uint8_t port, E_USB_DEVICE_T device);
- Description:
Powers on the UVC camera, returns error code. BK7258 supports power control for UVC cameras. When powering on, you need to select the corresponding port and device type.
- Parameters:
mode: USB host mode, fixed as USB_HOST_MODE. port: USB port number, valid range is 1 to UVC_PORT_MAX (default 4). device: USB device type, default is USB_UVC_DEVICE, also supports USB_UVC_H26X_DEVICE.
When powering on, you need to register connection success and disconnection callback functions first, and then power on the device. Reference code:
/**
* @brief UVC connection success callback function
* @details This callback function is triggered when a UVC device is successfully connected. It stores the port information and triggers the semaphore.
* @param port_info Pointer to USB hub port information structure
* @param arg Pointer to user argument (uvc_test_info_t structure)
*/
static void uvc_connect_successful_callback(bk_usb_hub_port_info *port_info, void *arg)
{
LOGI("%s, %d\n", __func__, __LINE__);
uvc_test_info_t *info = (uvc_test_info_t *)arg;
if (info == NULL)
return;
rtos_lock_mutex(&info->uvc_mutex);
info->port_info[port_info->port_index - 1] = port_info;
rtos_set_semaphore(&info->uvc_connect_semaphore);
rtos_unlock_mutex(&info->uvc_mutex);
}
/**
* @brief UVC disconnection callback function
* @details This callback function is triggered when a UVC device is disconnected. It clears the port information.
* @param port_info Pointer to USB hub port information structure
* @param arg Pointer to user argument (uvc_test_info_t structure)
*/
static void uvc_disconnect_callback(bk_usb_hub_port_info *port_info, void *arg)
{
LOGI("%s, %d\n", __func__, __LINE__);
uvc_test_info_t *info = (uvc_test_info_t *)arg;
if (info == NULL)
return;
rtos_lock_mutex(&info->uvc_mutex);
info->port_info[port_info->port_index - 1] = NULL;
rtos_unlock_mutex(&info->uvc_mutex);
}
/**
* @brief UVC camera device power on function
* @details This function powers on the UVC camera device on all ports. It registers the connection and disconnection callbacks.
* @param device USB device type (USB_UVC_DEVICE or USB_UVC_H26X_DEVICE)
* @return avdk_err_t Error code (AVDK_ERR_OK if successful)
*/
static avdk_err_t uvc_camera_device_power_on(E_USB_DEVICE_T device)
{
avdk_err_t ret = AVDK_ERR_OK;
// Register connection and disconnection callback functions for each port, and power on the UVC device of the corresponding port
for (uint8_t port = 1; port <= UVC_PORT_MAX; port++)
{
bk_usbh_hub_port_register_connect_callback(port, device, uvc_connect_successful_callback, uvc_test_info);
bk_usbh_hub_port_register_disconnect_callback(port, device, uvc_disconnect_callback, uvc_test_info);
bk_usbh_hub_multiple_devices_power_on(USB_HOST_MODE, port, device);
}
// Check if UVC port has been connected successfully
for (uint8_t port = 1; port <= UVC_PORT_MAX; port++)
{
ret = bk_usbh_hub_port_check_device(port, device, &uvc_test_info->port_info[port - 1]);
if (ret == AVDK_ERR_OK)
{
break;
}
}
return ret;
}
/**
* @brief UVC camera power on handle function
* @details This function powers on the UVC camera device and waits for the connection success callback.
* @param info Pointer to UVC test information structure
* @param timeout Timeout value in milliseconds
* @return avdk_err_t Error code (AVDK_ERR_OK if successful)
*/
static avdk_err_t uvc_camera_power_on_handle(uvc_test_info_t *info, uint32_t timeout)
{
// Power on UVC camera device and wait for connection success callback
avdk_err_t ret = AVDK_ERR_OK;
/*USB_UVC_H26X_DEVICE: for h26x stream, this can not execute*/
//ret = uvc_camera_device_power_on(USB_UVC_H26X_DEVICE);
/*USB_UVC_DEVICE: for mjpeg stream*/
// there only consider single stream, not consider multistream(mjpeg/h26x)
ret = uvc_camera_device_power_on(USB_UVC_DEVICE);
// If not checked successfully, wait for the connection success callback
if (ret != AVDK_ERR_OK)
{
ret = rtos_get_semaphore(&info->uvc_connect_semaphore, timeout);
if (ret != AVDK_ERR_OK)
{
LOGE("%s, %d, timeout:%d\n", __func__, __LINE__, timeout);
}
}
return ret;
}
// power on
{
avdk_err_t ret = AVDK_ERR_OK;
ret = uvc_camera_power_on_handle(uvc_test_info, 4000);
if (ret != AVDK_ERR_OK)
{
LOGE("%s, %d: uvc_camera_power_on_handle failed\n", __func__, __LINE__);
ret = uvc_camera_power_off_handle(uvc_test_info);
if (ret != AVDK_ERR_OK)
{
LOGE("%s, %d: uvc_camera_power_off_handle failed\n", __func__, __LINE__);
}
goto exit;
}
...
}
- Description:
Need to power on UVC before using it. Need to register callback functions (connection success/disconnection) before power on. Backup/delete port_info, it is recommended to add protection with mutex locks to prevent port_info from becoming invalid and causing abnormal pointer access issues when suddenly disconnected while using port_info. After power on is completed, first check if connection is successful, because when UAC is opened before UVC, the registered connection success callback will not be triggered, causing the semaphore not to be set. Call:
bk_usbh_hub_port_check_deviceto check if connection is successful. If not connected successfully, wait for connection success callback with timeout configured by user. Only proceed with subsequent operations after successful connection. If connection times out, it is recommended to directly turn off the power to achieve low power consumption.
4.2 Create UVC Camera Controller
After power on is completed and UVC device is successfully enumerated, call the following interface to create UVC camera controller:
avdk_err_t bk_camera_uvc_ctlr_new(bk_camera_ctlr_handle_t *handle, bk_uvc_ctlr_config_t *config);
- Description:
Creates UVC camera controller, returns controller handle. The passed configuration includes two parts:
First part: bk_uvc_config_t, contains resolution frame rate configuration that UVC needs to output, image format configuration to output, etc. Second part: bk_uvc_callback_t, through this callback function to apply for space to store target data and return image data to user, user manages target data by themselves.
- Parameters:
handle: UVC camera controller handle. config: UVC camera controller configuration parameters.
Related data structures: bk_uvc_ctlr_config_t, bk_cam_uvc_config_t, bk_uvc_callback_t.
Reference path: ap/include/components/uvc_camera_types.h.
bk_cam_uvc_config_t supports the following default configurations:
/**
* @brief Default UVC Configuration (864x480 Resolution at 30 FPS with MJPEG Format)
*
* Provides a standard configuration for UVC camera initialization with:
* - Single stream mode
* - Port 1
* - No frame dropping: Supports configuring to discard image data output when the camera starts, avoiding abnormal image data at startup.
* - Resolution: 864x480 pixels
* - Frame rate: 30 FPS
* - Image format: MJPEG
* - No user argument
*/
#define BK_UVC_864X480_30FPS_MJPEG_CONFIG()\
{\
.type = UVC_SINGLE_STREAM, \
.port = 1, \
.drop_num = 0, \
.img_format = IMAGE_MJPEG, \
.width = 864, \
.height = 480, \
.fps = 30, \
.arg = NULL, \
}
// Register callback function for applying space to store target data.
// It is recommended that users select idle nodes from their own maintained linked list based on the image format for the requested frames. Maintain two linked lists with the complete operation below: one free list and one ready list.
// Each time a request is made, take a node from the free list; after the request is completed, add the node to the ready list. When needed, take nodes from the ready list and release them directly back to the free list after use.
// For details, you can refer to: ``./projects/dvp_example/dvp_test/src/dvp_frame_list.c``, create yourself frame list.
/**
* @brief UVC frame buffer allocation function
* @details Allocates the corresponding frame buffer according to the image format and initializes the basic properties of the frame buffer
* @param format Image format, supports IMAGE_YUV/IMAGE_MJPEG/IMAGE_H264 and other formats
* @param size The size of the buffer to be allocated (bytes)
* @return Returns the allocated frame buffer pointer if successful, returns NULL if failed
*/
static frame_buffer_t *uvc_frame_malloc(image_format_t format, uint32_t size)
{
frame_buffer_t *frame = NULL;
if (format == IMAGE_YUV)
{
// Users can add their own operations, such as taking a node from the free linked list
// frame = frame_buffer_display_malloc(size);
}
else
{
// Users can add their own operations, such as taking a node from the free linked list
// frame = frame_buffer_encode_malloc(size);
}
if (frame)
{
frame->sequence = 0;
frame->length = 0;
frame->timestamp = 0;
frame->size = size;
}
return frame;
}
// Register callback function for processing UVC image data completion.
/**
* @brief UVC frame buffer completion callback function
* @details When a UVC camera outputs a frame of image data, this callback function will be called to notify the application layer that the image data is complete.
* @param port UVC port number to which the image data belongs
* @param frame Pointer to the frame buffer structure containing image data
* @param result Image data processing result, 0 means success, other values mean failure. Successful frames can be pushed to the user's linked list for management, and failed frames are directly released
*/
static void uvc_frame_complete(uint8_t port, image_format_t format, frame_buffer_t *frame, int result)
{
if (frame->sequence % 30 == 0)
{
LOGD("%s: port:%d, seq:%d, length:%d, format:%s, h264_type:%x, result:%d\n", __func__, port, frame->sequence,
frame->length, (format == IMAGE_MJPEG) ? "mjpeg" : "h264", frame->h264_type, result);
}
if (format == IMAGE_YUV)
{
// Users can add their own operations, such as adding the frame to the ready linked list
// frame_buffer_display_free(frame);
}
else
{
// Users can add their own operations, such as adding the frame to the ready linked list
// frame_buffer_encode_free(frame);
}
}
/**
* @brief UVC event callback function
* @details This callback function will be called when an event occurs on the UVC device, and is mainly used to print event codes currently
* @param port_info Pointer to port information structure, containing port information of UVC device connection
* @param arg User-defined parameters, which can be passed in when creating a UVC controller
* @param code Event code, indicating the type of UVC event that occurred. Please refer to bk_camera_uvc_event_code_t for specific event codes
*/
static void uvc_event_callback(bk_usb_hub_port_info *port_info,void *arg, uvc_error_code_t code)
{
LOGD("uvc_event_callback: %d", code);
}
static const bk_uvc_callback_t uvc_cbs = {
.malloc = uvc_frame_malloc,
.complete = uvc_frame_complete,
.uvc_event_callback = uvc_event_callback,
};
/**
* @brief Checkout UVC port information based on user configuration, customers can custom select whether to do the check
*
* This function checks the UVC port information based on the provided user configuration.
* It verifies the port, format, width, and height against the supported UVC formats.
*
* @param info Pointer to the UVC test information structure
* @param user_config Pointer to the user configuration structure
* @return avdk_err_t Error code indicating the success or failure of the operation
*/
static avdk_err_t uvc_checkout_port_info(uvc_test_info_t *info, bk_cam_uvc_config_t *user_config)
{
if (info == NULL)
{
LOGE("%s: info is NULL\n", __func__);
return AVDK_ERR_INVAL;
}
if (user_config == NULL)
{
LOGE("%s: config is NULL\n", __func__);
return AVDK_ERR_INVAL;
}
// No need to lock in the main function, as each format processing function handles locking internally
LOGD("%s, %d, port:%d, format:%d, W*H:%d*%d\r\n", __func__, __LINE__, user_config->port, user_config->img_format,
user_config->width, user_config->height);
avdk_err_t ret = AVDK_ERR_OK;
switch (user_config->img_format)
{
case IMAGE_YUV:
ret = uvc_checkout_port_info_yuv(info, user_config);
break;
case IMAGE_MJPEG:
ret = uvc_checkout_port_info_mjpeg(info, user_config);
break;
case IMAGE_H264:
ret = uvc_checkout_port_info_h264(info, user_config);
break;
case IMAGE_H265:
ret = uvc_checkout_port_info_h265(info, user_config);
break;
default:
LOGE("%s, please check usb output format:%d\r\n", __func__, user_config->img_format);
ret = AVDK_ERR_UNSUPPORTED;
break;
}
return ret;
}
// reference create new controller
{
bk_uvc_ctlr_config_t uvc_ctlr_config = {
.config = BK_UVC_864X480_30FPS_MJPEG_CONFIG(),
.cbs = &uvc_cbs,
};
// you can change the config here
uvc_ctlr_config.config.img_format = output_format;
uvc_ctlr_config.config.width = ppi >> 16;
uvc_ctlr_config.config.height = ppi & 0xFFFF;
uvc_ctlr_config.config.user_data = test_info;
if (argc > 2)
{
port = os_strtoul(argv[2], NULL, 10);
if (port > UVC_PORT_MAX || port == 0)
{
LOGE("%s, %d: invalid port %d\n", __func__, __LINE__, port);
goto exit;
}
uvc_ctlr_config.config.port = port;
}
// check user config match the port info or not
ret = uvc_checkout_user_info(test_info, &uvc_ctlr_config.config);
if (ret != AVDK_ERR_OK)
{
LOGE("%s, %d: uvc_checkout_user_info failed, ret:%d\n", __func__, __LINE__, ret);
goto exit;
}
// create new
ret = bk_camera_uvc_ctlr_new(&test_info->handle[port - 1], &uvc_ctlr_config);
}
- Description:
Checks UVC port information. Checks if the user’s output configuration is supported by this port. During the checking process, it is recommended to add protection with mutex locks to ensure thread safety. The check can also be skipped, as long as the input resolution is normal, it will not affect the opening of UVC.
4.3 Open UVC Camera
Call the following interface to open UVC camera:
avdk_err_t bk_camera_open(bk_camera_ctlr_handle_t handle);
- Description:
Opens UVC camera, returns error code. After opening UVC camera, the registered callback function will continuously apply for data space for storage. After opening UVC camera, data collection will start and the registered callback function will be called to return the collected data to the user. The user needs to pay attention to whether the frame is valid and choose to use or release it. If a problem occurs, the registered callback function uvc_event_callback will be called to return an error code. After UVC is opened, the connection success/disconnection callback functions registered by the user are no longer effective. All status information, including connection/disconnection status, is returned to the user through uvc_event_callback.
- Parameters:
handle: UVC camera controller handle.
4.4 Close UVC Camera
Call the following interface to close UVC camera:
avdk_err_t bk_camera_close(bk_camera_ctlr_handle_t handle);
- Description:
Closes UVC camera, returns error code. After closing UVC camera, the registered callback function will stop applying for data space and will not call the registered callback function. After closing UVC camera, data collection will stop.
- Parameters:
handle: UVC camera controller handle.
4.5 Delete UVC Camera
Call the following interface to delete UVC camera:
avdk_err_t bk_camera_delete(bk_camera_ctlr_handle_t handle);
- Description:
Deletes UVC camera, returns error code. Releases the resources of the UVC camera controller.
- Parameters:
handle: UVC camera controller handle.
4.6 Suspend All UVC Cameras
Call the following interface to suspend UVC camera:
avdk_err_t bk_camera_suspend(bk_camera_ctlr_handle_t handle);
- Description:
Suspends UVC camera, returns error code. After suspending UVC camera, the registered callback function will stop applying for data space and will not call the registered callback function. After suspending UVC camera, data collection will stop. When UVC is suspended here, UVC will still continue to collect data, but the collected data will not be processed, so there is no valid frame data output.
- Parameters:
handle: UVC camera controller handle.
4.7 Resume All UVC Cameras
Call the following interface to resume UVC camera:
avdk_err_t bk_camera_resume(bk_camera_ctlr_handle_t handle);
- Description:
Resumes UVC camera, returns error code. After resuming UVC camera, the registered callback function will continue to apply for data space and will continue to call the registered callback function. After resuming UVC camera, data collection will continue.
- Parameters:
handle: UVC camera controller handle.
4.8 UVC Power Off
Call the following interface to power off UVC camera:
bk_err_t bk_usbh_hub_multiple_devices_power_down(USB_HOST_MODE, port, device);
Description: - Powers off UVC camera, returns error code.
Parameters: - port: USB port number. - device: USB device number, default is USB_UVC_DEVICE, also supports USB_UVC_H26X_DEVICE.
Refer to the following power-off logic:
/*
* uvc_camera_device_power_off - Power off UVC camera device
* @device: USB device type (USB_UVC_DEVICE or USB_UVC_H26X_DEVICE)
*
* This function powers off the specified UVC camera device by iterating through all UVC ports,
* unregistering connection and disconnection callback functions, and calling the underlying API
* to power down the device.
*
* The function is typically called when shutting down the UVC system to ensure all connected
* devices are properly powered down.
*/
static void uvc_camera_device_power_off(E_USB_DEVICE_T device)
{
// Iterate through all possible UVC ports
for (uint8_t port = 1; port <= UVC_PORT_MAX; port++)
{
// Unregister the connection callback for this port and device
bk_usbh_hub_port_register_connect_callback(port, device, NULL, NULL);
// Unregister the disconnection callback for this port and device
bk_usbh_hub_port_register_disconnect_callback(port, device, NULL, NULL);
// Power down the specified device on this port
bk_usbh_hub_multiple_devices_power_down(USB_HOST_MODE, port, device);
}
}
/**
* @brief UVC camera device power-off handle function
* @details This function is called when the UVC system is shut down to ensure all connected UVC devices are properly powered off.
* It first checks if all UVC ports are closed, then releases semaphores, powers off devices, and clears port information.
* @param info Pointer to UVC test information structure
* @return Returns AVDK_ERR_OK on success, returns AVDK_ERR_GENERIC on failure
*/
static avdk_err_t uvc_camera_power_off_handle(uvc_test_info_t *info)
{
// Before power-off, check if all ports are closed
avdk_err_t ret = AVDK_ERR_GENERIC;
if (info == NULL)
{
LOGE("%s, %d: info is NULL\n", __func__, __LINE__);
return ret;
}
uint8_t all_closed = true;
for (uint8_t port = 1; port <= UVC_PORT_MAX; port++)
{
if (info->handle[port - 1] != NULL)
{
avdk_err_t err = bk_camera_close(info->handle[port - 1]);
if (err != AVDK_ERR_OK)
{
LOGE("%s, %d: bk_camera_close failed, port:%d, err:%d\n", __func__, __LINE__, port, err);
}
err = bk_camera_delete(info->handle[port - 1]);
if (err != AVDK_ERR_OK)
{
LOGE("%s, %d: bk_camera_delete failed, port:%d, err:%d\n", __func__, __LINE__, port, err);
}
info->handle[port - 1] = NULL;
}
}
// Power off the UVC camera device
uvc_camera_device_power_off(USB_UVC_DEVICE);
//uvc_camera_device_power_off(USB_UVC_H26X_DEVICE);
// Clear port information
for (uint8_t port = 1; port <= UVC_PORT_MAX; port++)
{
info->port_info[port - 1] = NULL;
}
return AVDK_ERR_OK;
}
5. UVC Support Solutions
UVC directly outputs MJPEG images, decodes them through a JPEG decoder, converts MJPEG images to YUV format, then displays them on an LCD display, and the MJPEG output from UVC is combined with a WiFi module for image transmission.
UVC directly outputs MJPEG images, decodes them through a JPEG decoder, converts MJPEG images to YUV format, then encodes them into H.264 format through an H264 encoder, then displays the decoded images on an LCD display, and combines the WiFi module for image transmission of the encoded H.264 images. You can refer to the doobell solution.
UVC directly outputs dual-stream H264&MJPEG images (one port outputs two formats, or two ports output H264 and MJPEG respectively), decodes MJPEG images through a JPEG decoder, converts MJPEG images to YUV format, then displays the decoded images on an LCD display, and combines the WiFi module for image transmission of the H264 images output from UVC.