From c037c579b99e941ef0f752178d1cae0f0b133a56 Mon Sep 17 00:00:00 2001 From: Li Junru Date: Mon, 6 Jan 2025 20:55:01 +0800 Subject: [PATCH] feat(usb_host_uvc): support get frame list when device insert --- .../uvc/usb_host_uvc/include/usb/uvc_host.h | 43 +++++++--- .../private_include/uvc_descriptors_priv.h | 2 + .../uvc/usb_host_uvc/uvc_descriptor_parsing.c | 80 +++++++++++++++++++ host/class/uvc/usb_host_uvc/uvc_host.c | 21 ++--- 4 files changed, 125 insertions(+), 21 deletions(-) diff --git a/host/class/uvc/usb_host_uvc/include/usb/uvc_host.h b/host/class/uvc/usb_host_uvc/include/usb/uvc_host.h index 7a828a3f..5eb6b09e 100644 --- a/host/class/uvc/usb_host_uvc/include/usb/uvc_host.h +++ b/host/class/uvc/usb_host_uvc/include/usb/uvc_host.h @@ -26,13 +26,43 @@ enum uvc_host_driver_event { UVC_HOST_DRIVER_EVENT_DEVICE_CONNECTED = 0x0, }; + +/** + * @brief Formats supported by this driver + */ +enum uvc_host_stream_format { + UVC_VS_FORMAT_UNDEFINED = 0, // Invalid format. Do not request this format from the camera. + UVC_VS_FORMAT_MJPEG, + UVC_VS_FORMAT_YUY2, + UVC_VS_FORMAT_H264, + UVC_VS_FORMAT_H265, +}; + +typedef struct { + enum uvc_host_stream_format format; /**< Format of this frame buffer */ + unsigned h_res; /**< Horizontal resolution */ + unsigned v_res; /**< Vertical resolution */ + uint32_t default_interval; + uint8_t interval_type; /**< 0: Continuous frame interval, 1..255: The number of discrete frame intervals supported (n) */ + union { + struct { + uint32_t interval_min; + uint32_t interval_max; + uint32_t interval_step; + }; + uint32_t interval[3]; // We must put a fixed size here because of the union type. This is flexible size array though + }; +} uvc_host_frame_info_t; + typedef struct { enum uvc_host_driver_event type; union { struct { uint8_t dev_addr; uint8_t iface_num; //!< Disconnection event - } device_connected; // UVC_HOST_DEVICE_DISCONNECTED + uvc_host_frame_info_t **frame_info; + size_t frame_info_num; + } device_connected; // UVC_HOST_DEVICE_CONNECTED }; } uvc_host_driver_event_data_t; @@ -83,17 +113,6 @@ typedef struct { }; } uvc_host_stream_event_data_t; -/** - * @brief Formats supported by this driver - */ -enum uvc_host_stream_format { - UVC_VS_FORMAT_UNDEFINED = 0, // Invalid format. Do not request this format from the camera. - UVC_VS_FORMAT_MJPEG, - UVC_VS_FORMAT_YUY2, - UVC_VS_FORMAT_H264, - UVC_VS_FORMAT_H265, -}; - typedef struct { unsigned h_res; /**< Horizontal resolution */ unsigned v_res; /**< Vertical resolution */ diff --git a/host/class/uvc/usb_host_uvc/private_include/uvc_descriptors_priv.h b/host/class/uvc/usb_host_uvc/private_include/uvc_descriptors_priv.h index 676f25d6..9d8123dc 100644 --- a/host/class/uvc/usb_host_uvc/private_include/uvc_descriptors_priv.h +++ b/host/class/uvc/usb_host_uvc/private_include/uvc_descriptors_priv.h @@ -80,6 +80,8 @@ bool uvc_desc_is_uvc_device(const usb_config_desc_t *cfg_desc); void uvc_print_desc(const usb_standard_desc_t *_desc); +esp_err_t uvc_desc_get_frame_list(const usb_config_desc_t *config_desc, uint8_t bInterfaceNumber, uvc_host_frame_info_t ***frame_info_list, size_t *list_size); + #ifdef __cplusplus } #endif diff --git a/host/class/uvc/usb_host_uvc/uvc_descriptor_parsing.c b/host/class/uvc/usb_host_uvc/uvc_descriptor_parsing.c index 3ffb8c52..a3b61aa3 100644 --- a/host/class/uvc/usb_host_uvc/uvc_descriptor_parsing.c +++ b/host/class/uvc/usb_host_uvc/uvc_descriptor_parsing.c @@ -391,3 +391,83 @@ bool uvc_desc_is_uvc_device(const usb_config_desc_t *cfg_desc) } return false; } + +esp_err_t uvc_desc_get_frame_list(const usb_config_desc_t *config_desc, uint8_t bInterfaceNumber, uvc_host_frame_info_t ***frame_info_list, size_t *list_size) +{ + esp_err_t ret = ESP_OK; + UVC_CHECK(config_desc && frame_info_list && list_size, ESP_ERR_INVALID_ARG); + uvc_host_frame_info_t **new_frame_info = NULL; + *list_size = 0; + const uvc_vs_input_header_desc_t *input_header = uvc_desc_get_streaming_input_header(config_desc, bInterfaceNumber); + UVC_CHECK(input_header, ESP_ERR_NOT_FOUND); + + // Find requested Format descriptors + int format_offset = 0; + const usb_standard_desc_t *current_desc = (const usb_standard_desc_t *)input_header; + while ((current_desc = usb_parse_next_descriptor_of_type(current_desc, input_header->wTotalLength, UVC_CS_INTERFACE, &format_offset))) { + if (!uvc_desc_is_format_desc(current_desc)) { + continue; + } + + const uvc_format_desc_t *this_format = (const uvc_format_desc_t *)(current_desc); + enum uvc_host_stream_format format_type = uvc_desc_parse_format(this_format); + if (UVC_VS_FORMAT_UNDEFINED == format_type) { + continue; + } + // We found required Format Descriptor + // Now we look for correct Frame Descriptors which should be directly after Format + while ((current_desc = usb_parse_next_descriptor_of_type(current_desc, input_header->wTotalLength, UVC_CS_INTERFACE, &format_offset))) { + if (!uvc_desc_is_frame_desc(current_desc)) { + break; + } + uvc_frame_desc_t *this_frame = (uvc_frame_desc_t *)current_desc; + uvc_host_frame_info_t **temp = realloc(new_frame_info, (*list_size + 1) * sizeof(uvc_host_frame_info_t *)); + UVC_CHECK(temp != NULL, ESP_ERR_NO_MEM); + new_frame_info = temp; + new_frame_info[*list_size] = malloc(sizeof(uvc_host_frame_info_t)); + UVC_CHECK((new_frame_info)[*list_size] != NULL, ESP_ERR_NO_MEM); + + uvc_host_frame_info_t *frame_info = (new_frame_info)[*list_size]; + frame_info->format = format_type; + frame_info->h_res = this_frame->wWidth; + frame_info->v_res = this_frame->wHeight; + switch (format_type) { + case UVC_VS_FORMAT_MJPEG: + frame_info->default_interval = this_frame->mjpeg_uncompressed.dwDefaultFrameInterval; + frame_info->interval_type = this_frame->mjpeg_uncompressed.bFrameIntervalType; + if (frame_info->interval_type == 0) { + frame_info->interval_min = this_frame->mjpeg_uncompressed.dwMinFrameInterval; + frame_info->interval_max = this_frame->mjpeg_uncompressed.dwMaxFrameInterval; + frame_info->interval_step = this_frame->mjpeg_uncompressed.dwFrameIntervalStep; + } else { + // TODO: make 3 can be configured + for (int i = 0; i < 3; i ++) { + frame_info->interval[i] = this_frame->mjpeg_uncompressed.dwFrameInterval[i]; + } + } + break; + case UVC_VS_FORMAT_H265: + case UVC_VS_FORMAT_H264: + frame_info->default_interval = this_frame->frame_based.dwDefaultFrameInterval; + frame_info->interval_type = this_frame->frame_based.bFrameIntervalType; + if (frame_info->interval_type == 0) { + frame_info->interval_min = this_frame->frame_based.dwMinFrameInterval; + frame_info->interval_max = this_frame->frame_based.dwMaxFrameInterval; + frame_info->interval_step = this_frame->frame_based.dwFrameIntervalStep; + } else { + // TODO: make 3 can be configured + for (int i = 0; i < 3; i ++) { + frame_info->interval[i] = this_frame->frame_based.dwFrameInterval[i]; + } + } + break; + default: + break; + } + + (*list_size)++; + } + } + *frame_info_list = new_frame_info; + return ret; +} \ No newline at end of file diff --git a/host/class/uvc/usb_host_uvc/uvc_host.c b/host/class/uvc/usb_host_uvc/uvc_host.c index f70ecc1c..bb473eb2 100644 --- a/host/class/uvc/usb_host_uvc/uvc_host.c +++ b/host/class/uvc/usb_host_uvc/uvc_host.c @@ -77,22 +77,25 @@ static esp_err_t uvc_host_interface_check(uint8_t addr, const usb_config_desc_t is_uvc_interface = true; if (p_uvc_host_driver->user_cb) { + uvc_host_frame_info_t **frame_info = NULL; + size_t frame_info_num = 0; + if (uvc_desc_get_frame_list(config_desc, iface_desc->bInterfaceNumber, &frame_info, &frame_info_num) != ESP_OK) { + ESP_LOGE(TAG, "Failed to get frame list for interface %d", iface_desc->bInterfaceNumber); + return ESP_FAIL; + } + const uvc_host_driver_event_data_t conn_event = { .type = UVC_HOST_DRIVER_EVENT_DEVICE_CONNECTED, .device_connected.dev_addr = addr, .device_connected.iface_num = iface_desc->bInterfaceNumber, + .device_connected.frame_info = frame_info, + .device_connected.frame_info_num = frame_info_num }; p_uvc_host_driver->user_cb(&conn_event, p_uvc_host_driver->user_ctx); - } - - const usb_intf_desc_t *iface_alt_desc = GET_NEXT_INTERFACE_DESC(iface_desc, total_length, iface_offset); - // Skip all alternate settings belonging to the current interface - while (iface_alt_desc != NULL) { - // Check if the alternate setting is for the same interface - if (iface_alt_desc->bInterfaceNumber != iface_desc->bInterfaceNumber) { - break; + for (int i = 0; i < frame_info_num; i++) { + free(frame_info[i]); } - iface_alt_desc = GET_NEXT_INTERFACE_DESC(iface_alt_desc, total_length, iface_offset); + free(frame_info); } } }