Skip to content

Commit

Permalink
feat(usb_host_uvc): support get frame list when device insert
Browse files Browse the repository at this point in the history
  • Loading branch information
lijunru-hub committed Jan 6, 2025
1 parent 3fa43ce commit c037c57
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 21 deletions.
43 changes: 31 additions & 12 deletions host/class/uvc/usb_host_uvc/include/usb/uvc_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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 */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
80 changes: 80 additions & 0 deletions host/class/uvc/usb_host_uvc/uvc_descriptor_parsing.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Check warning

Code scanning / clang-tidy

Potential leak of memory pointed to by 'new_frame_info' [clang-analyzer-unix.Malloc] Warning

Potential leak of memory pointed to by 'new_frame_info' [clang-analyzer-unix.Malloc]

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;
}
21 changes: 12 additions & 9 deletions host/class/uvc/usb_host_uvc/uvc_host.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down

0 comments on commit c037c57

Please sign in to comment.