diff --git a/host/class/uvc/usb_host_uvc_2/CMakeLists.txt b/host/class/uvc/usb_host_uvc_2/CMakeLists.txt new file mode 100644 index 00000000..9c313955 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/CMakeLists.txt @@ -0,0 +1,18 @@ +set(srcs) +set(include) +set(private_include) +# As CONFIG_USB_OTG_SUPPORTED comes from Kconfig, it is not evaluated yet +# when components are being registered. +set(require usb) + +if(CONFIG_USB_OTG_SUPPORTED) + list(APPEND srcs "uvc_host.c" "uvc_descriptors.c" "uvc_frame.c" "uvc_control.c") + list(APPEND include "include") + list(APPEND private_include "private_include" "include/esp_private") +endif() + +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include} + PRIV_INCLUDE_DIRS ${private_include} + REQUIRES ${require} + ) diff --git a/host/class/uvc/usb_host_uvc_2/LICENSE b/host/class/uvc/usb_host_uvc_2/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/host/class/uvc/usb_host_uvc_2/README.md b/host/class/uvc/usb_host_uvc_2/README.md new file mode 100644 index 00000000..0b4622a6 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/README.md @@ -0,0 +1,68 @@ +# Espressif Video USB Host driver + +Tested on ESP32-P4 and customer's dual camera. + +## Limitations +- ISOC transfers only +- The USB device must implement IAD descriptor +- USB reconnections not yet 100% working + + +## FAQ H265 encoding + +Q1: I have two input frames inputI.hevc which is full Intra-coded frame. And a second inputP.hevc which is Predicted frame. How do I decode the second frame to png? + +--- + +### Decoding a P-frame Using an I-frame in HEVC + +To decode a P-frame using the corresponding I-frame, you need to combine both frames into a single stream since a P-frame requires the reference frame (I-frame) to be properly decoded. Here’s how you can do it using FFmpeg: + +#### Step 1: Combine the I-frame and P-frame into a Single Stream + +1. **Concatenate the frames**: Create a text file with the paths to the input HEVC files in the order they should be decoded. + + Create a file `inputs.txt` with the following content: + ```plaintext + file 'inputI.hevc' + file 'inputP.hevc' + ``` + +2. **Concatenate the HEVC files using FFmpeg**: + ```sh + ffmpeg -f concat -safe 0 -i inputs.txt -c copy combined.hevc + ``` + +#### Step 2: Extract and Decode the Second Frame + +1. **Decode the combined HEVC stream and extract the second frame**: + ```sh + ffmpeg -i combined.hevc -vf "select=eq(n\,1)" -vsync vfr second_frame.png + ``` + +#### Explanation + +- The `-f concat -safe 0 -i inputs.txt -c copy combined.hevc` command concatenates the I-frame and P-frame into a single HEVC file. +- The `-vf "select=eq(n\,1)" -vsync vfr second_frame.png` command extracts and decodes the second frame (P-frame) from the combined stream. + +#### Complete Command Sequence + +1. **Create `inputs.txt`**: + ```plaintext + file 'inputI.hevc' + file 'inputP.hevc' + ``` + +2. **Combine the HEVC files**: + ```sh + ffmpeg -f concat -safe 0 -i inputs.txt -c copy combined.hevc + ``` + +3. **Extract and decode the second frame**: + ```sh + ffmpeg -i combined.hevc -vf "select=eq(n\,1)" -vsync vfr second_frame.png + ``` + +By following these steps, you should be able to decode the second frame (P-frame) using the initial I-frame and convert it to PNG. + +--- diff --git a/host/class/uvc/usb_host_uvc_2/TODO b/host/class/uvc/usb_host_uvc_2/TODO new file mode 100644 index 00000000..40c9d7fa --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/TODO @@ -0,0 +1,5 @@ +- [ ] Allow external events handling +- [ ] Allow VID and PID to be 0 +- [x] 1 CTRL transfer for the driver? of 1 transfer for a device? +- [ ] Mock USB host for descriptor parsing +- [x] Support bulk mode diff --git a/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/CMakeLists.txt b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/CMakeLists.txt new file mode 100644 index 00000000..0e0ba5ab --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(basic_uvc_stream) diff --git a/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/CMakeLists.txt b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/CMakeLists.txt new file mode 100644 index 00000000..62c7f7c3 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "basic_uvc_stream.c" + INCLUDE_DIRS ".") diff --git a/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/basic_uvc_stream.c b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/basic_uvc_stream.c new file mode 100644 index 00000000..5394e34b --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/basic_uvc_stream.c @@ -0,0 +1,314 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include +#include +#include +#include +#include +#include +#include "esp_system.h" +#include "esp_log.h" +#include "esp_err.h" + +#include "esp_vfs_fat.h" +#include "sdmmc_cmd.h" +#include "driver/sdmmc_host.h" +#include "sd_pwr_ctrl_by_on_chip_ldo.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" + +#include "usb/usb_host.h" +#include "usb/uvc_host.h" + +#define EXAMPLE_USB_HOST_PRIORITY (15) +#define EXAMPLE_USB_DEVICE_VID (0x2207) +#define EXAMPLE_USB_DEVICE_PID (0x0018) // Customer's dual camera +#define EXAMPLE_FRAME_COUNT (3) +#define EXAMPLE_STREAM_FPS (15) +#define EXAMPLE_RECORDING_LENGTH_S (5) +#define EXAMPLE_NUMBER_OF_STREAMS (2) + +#define MOUNT_POINT "/sdcard" + +static const char *TAG = "UVC example"; +static SemaphoreHandle_t device_disconnected_sem; +static QueueHandle_t rx_frames_queue[EXAMPLE_NUMBER_OF_STREAMS]; + +bool frame_callback(const uvc_frame_t *frame, void *user_ctx) +{ + assert(frame); + assert(user_ctx); + QueueHandle_t frame_q = *((QueueHandle_t *)user_ctx); + // ESP_LOGI(TAG, "Frame callback! data len: %d", frame->data_len); + BaseType_t result = xQueueSend(frame_q, &frame, 0); + if (pdPASS != result) { + ESP_LOGW(TAG, "Queue full, losing frame"); + return true; // We will not process this frame, return it immediately + } + return false; // We only passed the frame to Queue, so we must return false and call uvc_host_frame_return() later +} + +/** + * @brief Device event callback + * + * Apart from handling device disconnection it doesn't do anything useful + * + * @param[in] event Device event type and data + * @param[in] user_ctx Argument we passed to the device open function + */ +static void handle_event(const uvc_host_stream_event_data_t *event, void *user_ctx) +{ + switch (event->type) { + case UVC_HOST_ERROR: + ESP_LOGE(TAG, "USB error has occurred, err_no = %i", event->data.error); + break; + case UVC_HOST_DEVICE_DISCONNECTED: + ESP_LOGI(TAG, "Device suddenly disconnected"); + ESP_ERROR_CHECK(uvc_host_stream_close(event->data.stream_hdl)); + xSemaphoreGive(device_disconnected_sem); + break; + default: + ESP_LOGW(TAG, "Unsupported event: %i", event->type); + break; + } +} + +/** + * @brief USB Host library handling task + * + * @param arg Unused + */ +static void usb_lib_task(void *arg) +{ + while (1) { + // Start handling system events + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + ESP_ERROR_CHECK(usb_host_device_free_all()); + } + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + ESP_LOGI(TAG, "USB: All devices freed"); + // Continue handling USB events to allow device reconnection + } + } +} + +static void frame_handling_task(void *arg) +{ + const uvc_host_stream_config_t *stream_config = (const uvc_host_stream_config_t *)arg; + QueueHandle_t frame_q = *((QueueHandle_t *)(stream_config->user_arg)); + const int uvc_index = stream_config->usb.uvc_function_index; + + while (true) { + uvc_host_stream_hdl_t uvc_stream = NULL; + + ESP_LOGI(TAG, "Opening UVC device 0x%04X:0x%04X-%d\n\t%dx%d@%dFPS...", + stream_config->usb.vid, stream_config->usb.pid, uvc_index, stream_config->vs_format.h_res, stream_config->vs_format.v_res, stream_config->vs_format.fps); + esp_err_t err = uvc_host_stream_open(stream_config, pdMS_TO_TICKS(5000), &uvc_stream); + if (ESP_OK != err) { + ESP_LOGI(TAG, "Failed to open device"); + vTaskDelay(pdMS_TO_TICKS(5000)); + continue; + } + // uvc_host_desc_print(uvc_stream); + ESP_LOGI(TAG, "Device 0x%04X:0x%04X-%d OPENED!", stream_config->usb.vid, stream_config->usb.pid, uvc_index); + vTaskDelay(pdMS_TO_TICKS(100)); + unsigned count = 0; + + // This is the main processing loop. It enables the stream for 2 seconds and then closes it + uvc_host_stream_start(uvc_stream); + while (true) { + ESP_LOGI(TAG, "Stream %d start. Iteration %u", uvc_index, count); + count++; + TickType_t timeout_ticks = pdMS_TO_TICKS(EXAMPLE_RECORDING_LENGTH_S * 1000); + TimeOut_t connection_timeout; + vTaskSetTimeOutState(&connection_timeout); + + uvc_host_stream_unpause(uvc_stream); + do { + uvc_frame_t *frame; + if (xQueueReceive(frame_q, &frame, pdMS_TO_TICKS(1000)) == pdPASS) { + ESP_LOGI(TAG, "Stream %d: New frame! Len: %d", uvc_index, frame->data_len); + + // Process the frame data here!! + + uvc_host_frame_return(uvc_stream, frame); + } else { + ESP_LOGW(TAG, "Stream %d: Frame not received on time", uvc_index); + } + } while (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) == pdFALSE); + ESP_LOGI(TAG, "Stream %d stop", uvc_index); + uvc_host_stream_pause(uvc_stream); + + vTaskDelay(pdMS_TO_TICKS(2000)); + } + uvc_host_stream_stop(uvc_stream); + uvc_host_stream_close(uvc_stream); + + // We are done. Wait for device disconnection and start over + ESP_LOGI(TAG, "Example finished successfully! You can reconnect the device to run again."); + xSemaphoreTake(device_disconnected_sem, portMAX_DELAY); + } +} + +static const uvc_host_stream_config_t stream_mjpeg_config = { + .event_cb = handle_event, + .frame_cb = frame_callback, + .user_arg = &rx_frames_queue[0], + .usb.vid = EXAMPLE_USB_DEVICE_VID, + .usb.pid = EXAMPLE_USB_DEVICE_PID, + .usb.uvc_function_index = 0, + .vs_format.h_res = 720, + .vs_format.v_res = 1280, + .vs_format.fps = 15, + .vs_format.format = UVC_VS_FORMAT_MJPEG, + .advanced.number_of_frame_buffers = EXAMPLE_FRAME_COUNT, + .advanced.frame_size = 300*1024, + .advanced.number_of_urbs = 6, + .advanced.urb_size = 8 * 1024, +}; + +static const uvc_host_stream_config_t stream_h265_config = { + .event_cb = handle_event, + .frame_cb = frame_callback, + .user_arg = &rx_frames_queue[1], + .usb.vid = EXAMPLE_USB_DEVICE_VID, + .usb.pid = EXAMPLE_USB_DEVICE_PID, // Customer's device + .usb.uvc_function_index = 1, + .vs_format.h_res = 1280, + .vs_format.v_res = 720, + .vs_format.fps = 15, + .vs_format.format = UVC_VS_FORMAT_H265, + .advanced.number_of_frame_buffers = EXAMPLE_FRAME_COUNT, + .advanced.frame_size = 300*1024, + .advanced.number_of_urbs = 6, + .advanced.urb_size = 8 * 1024, +}; + +/* +void app_init_sdcard(void) +{ + esp_err_t ret; + // Options for mounting the filesystem. + // If format_if_mount_failed is set to true, SD card will be partitioned and + // formatted in case when mounting fails. + esp_vfs_fat_sdmmc_mount_config_t mount_config = { + .format_if_mount_failed = true, + .max_files = 5, + .allocation_unit_size = 64 * 1024 + }; + sdmmc_card_t *card; + const char mount_point[] = MOUNT_POINT; + ESP_LOGI(TAG, "Initializing SD card"); + + // Use settings defined above to initialize SD card and mount FAT filesystem. + // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. + // Please check its source code and implement error recovery when developing + // production applications. + + ESP_LOGI(TAG, "Using SDMMC peripheral"); + + // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) + // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 40MHz for SDMMC) + // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; + sdmmc_host_t host = SDMMC_HOST_DEFAULT(); + + // For SoCs where the SD power can be supplied both via an internal or external (e.g. on-board LDO) power supply. + // When using specific IO pins (which can be used for ultra high-speed SDMMC) to connect to the SD card + // and the internal LDO power supply, we need to initialize the power supply first. + sd_pwr_ctrl_ldo_config_t ldo_config = { + .ldo_chan_id = 4, + }; + sd_pwr_ctrl_handle_t pwr_ctrl_handle = NULL; + + ret = sd_pwr_ctrl_new_on_chip_ldo(&ldo_config, &pwr_ctrl_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to create a new on-chip LDO power control driver"); + return; + } + host.pwr_ctrl_handle = pwr_ctrl_handle; + host.max_freq_khz = SDMMC_FREQ_HIGHSPEED; + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT(); + + slot_config.width = 4; + slot_config.clk = 43; + slot_config.cmd = 44; + slot_config.d0 = 39; + slot_config.d1 = 40; + slot_config.d2 = 41; + slot_config.d3 = 42; + slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; + + ESP_LOGI(TAG, "Mounting filesystem"); + ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card); + + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + ESP_LOGE(TAG, "Failed to mount filesystem. " + "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); + } else { + ESP_LOGE(TAG, "Failed to initialize the card (%s). " + "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); + } + return; + } + //esp_vfs_fat_sdcard_format(mount_point, card); +} +*/ + +/** + * @brief Main application + * + * Here we open a USB CDC device and send some data to it + */ +void app_main(void) +{ + device_disconnected_sem = xSemaphoreCreateBinary(); + for (int i = 0; i < EXAMPLE_NUMBER_OF_STREAMS; i++) { + rx_frames_queue[i] = xQueueCreate(EXAMPLE_FRAME_COUNT, sizeof(uvc_frame_t *)); + assert(rx_frames_queue[i]); + } + assert(device_disconnected_sem); + + //app_init_sdcard(); // Uncomment this if you want to init the SD card + + // Install USB Host driver. Should only be called once in entire application + ESP_LOGI(TAG, "Installing USB Host"); + const usb_host_config_t host_config = { + .skip_phy_setup = false, + .intr_flags = ESP_INTR_FLAG_LEVEL1, + }; + ESP_ERROR_CHECK(usb_host_install(&host_config)); + + // Create a task that will handle USB library events + BaseType_t task_created = xTaskCreatePinnedToCore(usb_lib_task, "usb_lib", 4096, NULL, EXAMPLE_USB_HOST_PRIORITY, NULL, 0); + assert(task_created == pdTRUE); + + ESP_LOGI(TAG, "Installing UVC driver"); + const uvc_host_driver_config_t uvc_driver_config = { + .driver_task_stack_size = 4 * 1024, + .driver_task_priority = EXAMPLE_USB_HOST_PRIORITY + 1, + .xCoreID = tskNO_AFFINITY, + .create_background_task = true, + }; + ESP_ERROR_CHECK(uvc_host_install(&uvc_driver_config)); + + // USB tasks are pinned to Core0. Frame handling is pinned to Core1 + task_created = xTaskCreatePinnedToCore(frame_handling_task, "mjpeg_handling", 4096, (void *)&stream_mjpeg_config, EXAMPLE_USB_HOST_PRIORITY - 2, NULL, tskNO_AFFINITY); + assert(task_created == pdTRUE); + vTaskDelay(pdMS_TO_TICKS(1000)); + task_created = xTaskCreatePinnedToCore(frame_handling_task, "h265_handling", 4096, (void *)&stream_h265_config, EXAMPLE_USB_HOST_PRIORITY - 3, NULL, tskNO_AFFINITY); + assert(task_created == pdTRUE); +} diff --git a/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/idf_component.yml b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/idf_component.yml new file mode 100644 index 00000000..d978288e --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/main/idf_component.yml @@ -0,0 +1,6 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=5.0" + usb_host_uvc_2: + version: "*" + override_path: "../../.." diff --git a/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/sdkconfig.defaults b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/sdkconfig.defaults new file mode 100644 index 00000000..21c41d50 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/examples/basic_uvc_stream/sdkconfig.defaults @@ -0,0 +1,11 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32p4" +CONFIG_RTC_CLK_SRC_EXT_CRYS=y +CONFIG_RTC_CLK_CAL_CYCLES=1024 +CONFIG_SPIRAM=y +CONFIG_SPIRAM_SPEED_200M=y +CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=2048 +CONFIG_USB_HOST_HW_BUFFER_BIAS_IN=y +CONFIG_IDF_EXPERIMENTAL_FEATURES=y diff --git a/host/class/uvc/usb_host_uvc_2/idf_component.yml b/host/class/uvc/usb_host_uvc_2/idf_component.yml new file mode 100644 index 00000000..7a268bdc --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/idf_component.yml @@ -0,0 +1,9 @@ +## IDF Component Manager Manifest File +version: "0.1.0" +description: USB Host UVC driver +dependencies: + idf: ">=5.0" +targets: + - esp32s2 + - esp32s3 + - esp32p4 diff --git a/host/class/uvc/usb_host_uvc_2/include/esp_private/uvc_control.h b/host/class/uvc/usb_host_uvc_2/include/esp_private/uvc_control.h new file mode 100644 index 00000000..9eb1d6c1 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/include/esp_private/uvc_control.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "usb/uvc_host.h" + + +/** + * @brief Submit transfer to CTRL endpoint + * + * Sends Control transfer as described in USB specification chapter 9. + * This function can be used by device drivers that use custom/vendor specific commands. + * + * @param stream_hdl UVC stream + * @param[in] bmRequestType Field of USB control request + * @param[in] bRequest Field of USB control request + * @param[in] wValue Field of USB control request + * @param[in] wIndex Field of USB control request + * @param[in] wLength Field of USB control request + * @param[inout] data Field of USB control request + * @return + * - ESP_OK: Transfer succeeded + * - ESP_ERR_TIMEOUT: The transfer could not be completed in time + * - ESP_ERR_INVALID_SIZE: The transfer is too big + * - ESP_ERR_INVALID_ARG: stream_hdl is NULL, or data is not NULL and wValue is greater than zero + * - ESP_ERR_INVALID_RESPONSE: Reply corrupted or too short + */ +esp_err_t uvc_host_usb_ctrl(uvc_host_stream_hdl_t stream_hdl, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *data); + +/** + * @brief Negotiate UVC stream format + * + * @param stream_hdl UVC stream + * @param[in] vs_format Requested Video Stream format + * @param[out] vs_result Negotiation result + * @return esp_err_t + */ +esp_err_t uvc_host_stream_control_negotiate(uvc_host_stream_hdl_t stream_hdl, const uvc_host_stream_format_t *vs_format, uvc_vs_ctrl_t *vs_result); diff --git a/host/class/uvc/usb_host_uvc_2/include/usb/usb_types_uvc.h b/host/class/uvc/usb_host_uvc_2/include/usb/usb_types_uvc.h new file mode 100644 index 00000000..bfc8563c --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/include/usb/usb_types_uvc.h @@ -0,0 +1,297 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +// Definitions from USB UVC specification + +#pragma once + +#include +#include +#include "esp_assert.h" + +#define UVC_VERSION_1_0 0x0100 +#define UVC_VERSION_1_1 0x0110 +#define UVC_VERSION_1_5 0x0150 + +/** + * @brief UVC request code + * + * @see USB UVC specification ver 1.5, table A.8 + */ +enum uvc_req_code { + UVC_RC_UNDEFINED = 0x00, + UVC_SET_CUR = 0x01, + UVC_GET_CUR = 0x81, + UVC_GET_MIN = 0x82, + UVC_GET_MAX = 0x83, + UVC_GET_RES = 0x84, + UVC_GET_LEN = 0x85, + UVC_GET_INFO = 0x86, + UVC_GET_DEF = 0x87 +}; + +/** + * @brief VideoStreaming interface control selector + * + * @see USB UVC specification ver 1.5, table A.9.8 + */ +enum uvc_vs_ctrl_selector { + UVC_VS_CONTROL_UNDEFINED = 0x00, + UVC_VS_PROBE_CONTROL = 0x01, + UVC_VS_COMMIT_CONTROL = 0x02, + UVC_VS_STILL_PROBE_CONTROL = 0x03, + UVC_VS_STILL_COMMIT_CONTROL = 0x04, + UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05, + UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06, + UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07, + UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08, + UVC_VS_SYNC_DELAY_CONTROL = 0x09 +}; + +/** + * @brief VideoControl interface descriptor subtype + * + * @see USB UVC specification ver 1.5, table A.5 + */ +enum uvc_vc_desc_subtype { + UVC_VC_DESC_SUBTYPE_DESCRIPTOR_UNDEFINED = 0x00, + UVC_VC_DESC_SUBTYPE_HEADER = 0x01, + UVC_VC_DESC_SUBTYPE_INPUT_TERMINAL = 0x02, + UVC_VC_DESC_SUBTYPE_OUTPUT_TERMINAL = 0x03, + UVC_VC_DESC_SUBTYPE_SELECTOR_UNIT = 0x04, + UVC_VC_DESC_SUBTYPE_PROCESSING_UNIT = 0x05, + UVC_VC_DESC_SUBTYPE_EXTENSION_UNIT = 0x06, + UVC_VC_DESC_SUBTYPE_ENCODING_UNIT = 0x07 +}; + +/** + * @brief VideoStreaming interface descriptor subtype + * + * @see USB UVC specification ver 1.5, table A.6 + */ +enum uvc_vs_desc_subtype { + UVC_VS_DESC_SUBTYPE_UNDEFINED = 0x00, + UVC_VS_DESC_SUBTYPE_INPUT_HEADER = 0x01, + UVC_VS_DESC_SUBTYPE_OUTPUT_HEADER = 0x02, + UVC_VS_DESC_SUBTYPE_STILL_IMAGE_FRAME = 0x03, + UVC_VS_DESC_SUBTYPE_FORMAT_UNCOMPRESSED = 0x04, + UVC_VS_DESC_SUBTYPE_FRAME_UNCOMPRESSED = 0x05, + UVC_VS_DESC_SUBTYPE_FORMAT_MJPEG = 0x06, + UVC_VS_DESC_SUBTYPE_FRAME_MJPEG = 0x07, + UVC_VS_DESC_SUBTYPE_FORMAT_MPEG2TS = 0x0a, + UVC_VS_DESC_SUBTYPE_FORMAT_DV = 0x0c, + UVC_VS_DESC_SUBTYPE_COLORFORMAT = 0x0d, + UVC_VS_DESC_SUBTYPE_FORMAT_FRAME_BASED = 0x10, + UVC_VS_DESC_SUBTYPE_FRAME_FRAME_BASED = 0x11, + UVC_VS_DESC_SUBTYPE_FORMAT_STREAM_BASED = 0x12 +}; + +/** + * @brief + * @see USB UVC specification ver 1.5, table 4-75 + */ +typedef struct { + // Version 1.0 + uint16_t bmHint; + uint8_t bFormatIndex; + uint8_t bFrameIndex; + uint32_t dwFrameInterval; + uint16_t wKeyFrameRate; + uint16_t wPFrameRate; + uint16_t wCompQuality; + uint16_t wCompWindowSize; + uint16_t wDelay; + uint32_t dwMaxVideoFrameSize; + uint32_t dwMaxPayloadTransferSize; + // Version 1.1 and above + uint32_t dwClockFrequency; + uint8_t bmFramingInfo; + uint8_t bPreferredVersion; + uint8_t bMinVersion; + uint8_t bMaxVersion; + // Version 1.5 and above + uint8_t bUsage; + uint8_t bBitDepthLuma; + uint8_t bmSettings; + uint8_t bMaxNumberOfRefFramesPlus1; + uint16_t bmRateControlModes; + uint8_t bmLayoutPerStream[8]; +} __attribute__((packed)) uvc_vs_ctrl_t; +ESP_STATIC_ASSERT(sizeof(uvc_vs_ctrl_t) == 48, "Size of uvc_vs_ctrl_t incorrect"); + +/** + * @brief Video Frame Descriptor + * + * Each format defines its own frame descriptor, but the first 9 fields are always the same. + * + * @see USB Device Class Definition for Video Devices: MJPEG Payload 1.5, table 3-2 + * @see USB Device Class Definition for Video Devices: Frame Based Payload 1.5, table 3-2 + * @see USB Device Class Definition for Video Devices: Uncompressed Payload 1.5, table 3-2 + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubtype; + uint8_t bFrameIndex; + uint8_t bmCapabilities; + uint16_t wWidth; + uint16_t wHeight; + uint32_t dwMinBitRate; + uint32_t dwMaxBitRate; + union { + struct { + uint32_t dwMaxVideoFrameBufferSize; + uint32_t dwDefaultFrameInterval; + uint8_t bFrameIntervalType; /**< 0: Continuous frame interval, 1..255: The number of discrete frame intervals supported (n) */ + union { + struct { + uint32_t dwMinFrameInterval; + uint32_t dwMaxFrameInterval; + uint32_t dwFrameIntervalStep; + }; + uint32_t dwFrameInterval[3]; // We must put a fixed size here because of the union type. This is flexible size array though + }; + } __attribute__((packed)) mjpeg_uncompressed; + struct { + uint32_t dwDefaultFrameInterval; + uint8_t bFrameIntervalType; /**< 0: Continuous frame interval, 1..255: The number of discrete frame intervals supported (n) */ + uint32_t dwBytesPerLine; + union { + struct { + uint32_t dwMinFrameInterval; + uint32_t dwMaxFrameInterval; + uint32_t dwFrameIntervalStep; + }; + uint32_t dwFrameInterval[3]; // We must put a fixed size here because of the union type. This is flexible size array though + }; + } __attribute__((packed)) frame_based; + } __attribute__((packed)); +} __attribute__((packed)) uvc_frame_desc_t; + +/** + * @brief Video Stream format descriptor + * + * Each format defines its own format descriptor, but the first 5 fields are always the same. + * + * @see USB Device Class Definition for Video Devices: MJPEG Payload 1.5, table 3-1 + * @see USB Device Class Definition for Video Devices: Uncompressed Payload 1.5, table 3-1 + * @see USB Device Class Definition for Video Devices: Frame Based Payload 1.5, table 3-1 + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + union { + struct { + uint8_t guidFormat[16]; // Globally Unique Identifier used to identify stream-encoding format + uint8_t bBitsPerPixel; // Number of bits per pixel used to specify color in the decoded video frame + uint8_t bDefaultFrameIndex; // Optimum Frame Index (used to select resolution) for this stream + uint8_t bAspectRatioX; // The X dimension of the picture aspect ratio + uint8_t bAspectRatioY; // The Y dimension of the picture aspect ratio + uint8_t bmInterlaceFlags; // Specifies interlace information + uint8_t bCopyProtect; // Specifies whether duplication of the video stream is restricted + } uncompressed_frame_based; + struct { + uint8_t bmFlags; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; + } mjpeg; + }; +} __attribute__((packed)) uvc_format_desc_t; + +/** + * @brief Video Control Interface Header Descriptor + * + * @see USB UVC specification ver 1.5, table 3-3 + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint16_t bcdUVC; + uint16_t wTotalLength; + uint32_t dwClockFrequency; + uint8_t bInCollection; + uint8_t baInterfaceNr[]; +} __attribute__((packed)) uvc_vc_header_desc_t; + +/** + * @brief Video Streaming Interface Input Header Descriptor + * + * @see USB UVC specification ver 1.5, table 3-14 + */ +typedef struct { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bNumFormats; + uint16_t wTotalLength; + uint8_t bEndpointAddress; + uint8_t bFunctionProtocol; + uint8_t bmInfo; + uint8_t bTerminalLink; + uint8_t bStillCaptureMethod; + uint8_t bTriggerSupport; + uint8_t bTriggerUsage; + uint8_t bControlSize; + uint8_t bmaControls; // This field is an array with size bNumFormats x ControlSize bytes +} __attribute__((packed)) uvc_vs_input_header_desc_t; + +/** + * @brief Video Interface Subclass Codes + * + * @see USB UVC specification ver 1.5, table A.2 + */ +enum uvc_int_subclass_code { + UVC_SC_UNDEFINED = 0x00, + UVC_SC_VIDEOCONTROL = 0x01, + UVC_SC_VIDEOSTREAMING = 0x02, + UVC_SC_VIDEO_INTERFACE_COLLECTION = 0x03 +}; + +/** + * @brief Video Class-Specific Descriptor Types + * + * @see USB UVC specification ver 1.5, table A.4 + */ +enum uvc_descriptor_type { + UVC_CS_UNDEFINED = 0x20, + UVC_CS_DEVICE = 0x21, + UVC_CS_CONFIGURATION = 0x22, + UVC_CS_STRING = 0x23, + UVC_CS_INTERFACE = 0x24, + UVC_CS_ENDPOINT = 0x25 +}; + +/** + * @brief Video and Still Image Payload Header + * + * The extended fields from table 2-6 are not listed here because this driver does not use them + * and they can be extended in next UVC specification versions + * + * @see USB UVC specification ver 1.5, table 2-5 + */ +typedef struct { + uint8_t bHeaderLength; + union { + struct { + uint8_t frame_id: 1; + uint8_t end_of_frame: 1; + uint8_t presentation_time: 1; + uint8_t source_clock_reference: 1; + uint8_t payload_specific: 1; + uint8_t still_image: 1; + uint8_t error: 1 ; + uint8_t end_of_header: 1; + }; + uint8_t val; + } bmHeaderInfo; +} __attribute__((packed)) uvc_payload_header_t; diff --git a/host/class/uvc/usb_host_uvc_2/include/usb/uvc_host.h b/host/class/uvc/usb_host_uvc_2/include/usb/uvc_host.h new file mode 100644 index 00000000..cc7b26ad --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/include/usb/uvc_host.h @@ -0,0 +1,223 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "usb/usb_host.h" +#include "usb/usb_types_uvc.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct uvc_host_stream_s *uvc_host_stream_hdl_t; + +/** + * @brief Configuration structure of USB Host UVC driver + * + */ +typedef struct { + size_t driver_task_stack_size; /**< Stack size of the driver's task */ + unsigned driver_task_priority; /**< Priority of the driver's task */ + int xCoreID; /**< Core affinity of the driver's task */ + bool create_background_task; /**< When set to true, background task handling usb events is created. + Otherwise user has to periodically call uvc_host_handle_events function */ +} uvc_host_driver_config_t; + +/** + * @brief UVC Device Event data structure + * + */ +typedef struct { + enum uvc_host_dev_event_t { + UVC_HOST_ERROR, + UVC_HOST_DEVICE_DISCONNECTED + } type; + union { + int error; //!< Error code from USB Host + uvc_host_stream_hdl_t stream_hdl; //!< Disconnection event + } data; +} uvc_host_stream_event_data_t; + +typedef struct { + unsigned h_res; /**< Horizontal resolution */ + unsigned v_res; /**< Vertical resolution */ + unsigned fps; /**< Frames per second */ + enum { + UVC_VS_FORMAT_UNDEFINED = 0, + UVC_VS_FORMAT_MJPEG, + UVC_VS_FORMAT_UNCOMPRESSED, + UVC_VS_FORMAT_H264, + UVC_VS_FORMAT_H265, + } format; /**< Frame format */ +} uvc_host_stream_format_t; + +/** + * @brief Video Stream frame + * + * This type is returned from frame callback upon receiving new frame + */ +typedef struct { + const uvc_host_stream_format_t vs_format; /**< Format of this frame buffer */ + size_t data_buffer_len; /**< Max data length supported by this frame buffer*/ + size_t data_len; /**< Data length of currently store frame */ + uint8_t *data; /**< Frame data */ +} uvc_frame_t; + +/** + * @brief Stream event callback type + * + * @param[in] event Event structure + * @param[in] user_arg User's argument passed to open function + */ +typedef void (*uvc_host_stream_callback_t)(const uvc_host_stream_event_data_t *event, void *user_ctx); + +/** + * @brief Frame callback type + * + * @param[in] frame Received frame + * @param[in] user_arg User's argument passed to open function + * @return + * - true: The frame was processed by user. The UVC driver owns the frame now. + * - false: The frame was not yet processed by user. + * The user must call uvc_host_frame_return() to return it to UVC driver. + */ +typedef bool (*uvc_host_frame_callback_t)(const uvc_frame_t *frame, void *user_ctx); + +/** + * @brief Configuration structure of UVC device + * + */ +typedef struct { + uvc_host_stream_callback_t event_cb; /**< Stream's event callback function. Can be NULL */ + uvc_host_frame_callback_t frame_cb; /**< Stream's frame callback function */ + void *user_arg; /**< User's argument that will be passed to the callbacks */ + struct { + uint16_t vid; /**< Device's Vendor ID. Set to 0 for any */ + uint16_t pid; /**< Device's Product ID. Set to 0 for any */ + uint8_t uvc_function_index; /**< Index of UVC function you want to use. Set to 0 to use first available UVC function */ + } usb; + uvc_host_stream_format_t vs_format; /**< Video Stream format. Resolution, FPS and encoding */ + struct { + int number_of_frame_buffers; /**< Number of frame buffers. These can be very large as they must hold the full frame.*/ + size_t frame_size; /**< 0: Use frame size from format negotiation result (might be too big). + (0; SIZE_MAX>: Use user provide frame size. */ + int number_of_urbs; /**< Number of URBs for this stream. Triple buffering scheme is recommended */ + size_t urb_size; /**< Size in bytes of 1 URB, 10kB should be enough for start. + Larger value results in less frequent interrupts at the cost of memory consumption */ + } advanced; +} uvc_host_stream_config_t; + +/** + * @brief Install UVC driver + * + * - USB Host Library must already be installed before calling this function (via usb_host_install()) + * - This function should be called before calling any other UVC driver functions + * + * @param[in] driver_config Driver configuration structure. If set to NULL, a default configuration will be used. + * @return esp_err_t + */ +esp_err_t uvc_host_install(const uvc_host_driver_config_t *driver_config); + +/** + * @brief Uninstall UVC driver + * + * - User must ensure that all UVC devices must be closed via uvc_host_stream_close() before calling this function + * + * @return esp_err_t + */ +esp_err_t uvc_host_uninstall(void); + +/** + * @brief Open UVC compliant device + * + * @param[in] stream_config Configuration structure of the device + * @param[in] timeout Timeout in FreeRTOS ticks + * @param[out] stream_hdl_ret UVC stream handle + * @return esp_err_t + */ +esp_err_t uvc_host_stream_open(const uvc_host_stream_config_t *dev_config, int timeout, uvc_host_stream_hdl_t *stream_hdl_ret); + +/** + * @brief Start UVC stream + * + * After this call, the user will be informed about new frames by frame callback + * + * @param[in] stream_hdl UVC handle obtained from uvc_host_stream_open() + * @return esp_err_t + */ +esp_err_t uvc_host_stream_start(uvc_host_stream_hdl_t stream_hdl); + +/** + * @brief Stop UVC stream + * + * @param[in] stream_hdl UVC handle obtained from uvc_host_stream_open() + * @return esp_err_t + */ +esp_err_t uvc_host_stream_stop(uvc_host_stream_hdl_t stream_hdl); + +/** + * @brief Pause UVC stream + * + * After this call, the user will be informed about new frames by frame callback. + * This function does not issue any CTRL request. In only stops receiving new data from streaming endpoint. + * + * @param[in] stream_hdl UVC handle obtained from uvc_host_stream_open() + * @return esp_err_t + */ +esp_err_t uvc_host_stream_pause(uvc_host_stream_hdl_t stream_hdl); + +/** + * @brief Start UVC stream + * + * After this call, the user will be informed about new frames by frame callback. + * This function does not issue any CTRL request. In only starts receiving new data from streaming endpoint. + * + * @param[in] stream_hdl UVC handle obtained from uvc_host_stream_open() + * @return esp_err_t + */ +esp_err_t uvc_host_stream_unpause(uvc_host_stream_hdl_t stream_hdl); + +/** + * @brief Close UVC device and release its resources + * + * @note All in-flight transfers will be prematurely canceled. + * @param[in] stream_hdl UVC handle obtained from uvc_host_stream_open() + * @return + * - ESP_OK: Success - device closed + * - ESP_ERR_INVALID_STATE: stream_hdl is NULL or the UVC driver is not installed + */ +esp_err_t uvc_host_stream_close(uvc_host_stream_hdl_t stream_hdl); + +/** + * @brief Return processed frame back to the driver + * + * In case your frame callback returns true, you MUST NOT call this function. + * In case your frame callback returns false, you MUS call this function after you are done with processing the frame. + * + * @param[in] stream_hdl UVC handle obtained from uvc_host_stream_open() + * @param[in] frame Frame obtained from frame callback + * @return + * - ESP_OK: Success - The frame was returned to the driver + * - ESP_INVALID_ARG: frame or stream_hdl is NULL + * - ESP_FAIL: The frame was not returned to the driver + */ +esp_err_t uvc_host_frame_return(uvc_host_stream_hdl_t stream_hdl, uvc_frame_t *frame); + +/** + * @brief Print device's descriptors + * + * Device and full Configuration descriptors are printed in human readable format to stdout. + * + * @param stream_hdl UVC handle obtained from uvc_host_stream_open() + */ +void uvc_host_desc_print(uvc_host_stream_hdl_t stream_hdl); + +#ifdef __cplusplus +} +#endif diff --git a/host/class/uvc/usb_host_uvc_2/private_include/uvc_descriptors_priv.h b/host/class/uvc/usb_host_uvc_2/private_include/uvc_descriptors_priv.h new file mode 100644 index 00000000..908bdef6 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/private_include/uvc_descriptors_priv.h @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "usb/usb_types_ch9.h" +#include "usb/uvc_host.h" +#include "usb/usb_types_uvc.h" +#include "uvc_types_priv.h" + +#define UVC_DESC_FPS_TO_DWFRAMEINTERVAL(fps) (((fps) != 0) ? 10000000 / (fps) : 0) +#define UVC_DESC_DWFRAMEINTERVAL_TO_FPS(dwFrameInterval) (((dwFrameInterval) != 0) ? 10000000 / (dwFrameInterval) : 0) + +// uvc_idx: For USB devices that offer multiple UVC functions. Set to 0 for all simple cameras. +const uvc_vc_header_desc_t *uvc_desc_get_control_interface_header(uvc_stream_t *uvc_stream, unsigned uvc_idx); + +const uvc_vs_input_header_desc_t *uvc_desc_get_streaming_input_header(uvc_stream_t *uvc_stream, uint8_t bInterfaceNumber); + +int uvc_desc_parse_format(const uvc_format_desc_t *format_desc); + +esp_err_t uvc_desc_get_streaming_intf_and_ep( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + const usb_intf_desc_t **intf_desc_ret, + const usb_ep_desc_t **ep_desc_ret); + +esp_err_t uvc_desc_get_frame_format_by_index( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + uint8_t bFormatIndex, + uint8_t bFrameIndex, + const uvc_format_desc_t **format_desc_ret, + const uvc_frame_desc_t **frame_desc_ret); + +esp_err_t uvc_desc_get_frame_format_by_format( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + const uvc_host_stream_format_t *vs_format, + const uvc_format_desc_t **format_desc_ret, + const uvc_frame_desc_t **frame_desc_ret); + +bool uvc_desc_is_format_supported( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + const uvc_host_stream_format_t *vs_format); diff --git a/host/class/uvc/usb_host_uvc_2/private_include/uvc_frame_priv.h b/host/class/uvc/usb_host_uvc_2/private_include/uvc_frame_priv.h new file mode 100644 index 00000000..cb875177 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/private_include/uvc_frame_priv.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "usb/uvc_host.h" +#include "uvc_types_priv.h" + +/** + * @brief Allocate frame buffers for UVC stream + * + * @param[in] stream_hdl UVC stream handle + * @param[in] nb_of_fb Number of frame buffers to allocate + * @param[in] fb_size Size of 1 frame buffer in bytes + * @return + * - ESP_OK: Success + * - ESP_ERR_NO_MEM: Not enough memory for frame buffers + * - ESP_ERR_INVALID_ARG: Invalid count or size of frame buffers + */ +esp_err_t uvc_frame_allocate(uvc_stream_t *uvc_stream, int nb_of_fb, size_t fb_size); + +/** + * @brief Free allocated frame buffers + * + * @attention The caller must ensure that the frame buffers are not accessed after this call and that streaming is not on + * @param[in] stream_hdl UVC stream + */ +void uvc_frame_free(uvc_stream_t *uvc_stream); + +bool uvc_frame_are_all_returned(uvc_stream_t *uvc_stream); + +uvc_frame_t *uvc_frame_get_empty(uvc_stream_t *uvc_stream); + +esp_err_t uvc_frame_add_data(uvc_frame_t *frame, const uint8_t *data, size_t data_len); + +void uvc_frame_reset(uvc_frame_t *frame); diff --git a/host/class/uvc/usb_host_uvc_2/private_include/uvc_types_priv.h b/host/class/uvc/usb_host_uvc_2/private_include/uvc_types_priv.h new file mode 100644 index 00000000..747199c5 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/private_include/uvc_types_priv.h @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include + +#include "usb/usb_host.h" +#include "usb/uvc_host.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +// UVC check macros +#define UVC_CHECK(cond, ret_val) ({ \ + if (!(cond)) { \ + return (ret_val); \ + } \ +}) + +#define UVC_CHECK_FROM_CRIT(cond, ret_val) ({ \ + if (!(cond)) { \ + UVC_EXIT_CRITICAL(); \ + return ret_val; \ + } \ +}) + +typedef struct uvc_host_stream_s uvc_stream_t; +struct uvc_host_stream_s { + // UVC driver related members + uvc_host_stream_callback_t stream_cb; // User's callback for stream events + uvc_host_frame_callback_t frame_cb; // User's frame callback + void *cb_arg; // Common argument for user's callbacks + SLIST_ENTRY(uvc_host_stream_s) list_entry; + + // Constant USB descriptor values + uint16_t bcdUVC; // Version of UVC specs this device implements + uint8_t bInterfaceNumber; // USB Video Streaming interface claimed by this stream. Needed for Stream start and CTRL transfers + uint8_t bAlternateSetting; // Alternate setting for selected interface. Needed for Stream start + + // USB host related members + usb_device_handle_t dev_hdl; // USB device handle + unsigned num_of_xfers; // Number of USB transfers + usb_transfer_t **xfers; // Pointer to array of USB transfers. Accessible only by the UVC driver + + // Frame buffers + bool streaming; // Flag whether the stream is on/off + uvc_host_stream_format_t vs_format; // Format of the video stream + QueueHandle_t empty_fb_queue; // Queue of empty framebuffers + uvc_frame_t *current_frame; // Frame that is being written to + bool skip_current_frame; // Flag to skip this frame. An error has occurred during fetch + uint8_t current_frame_id; // Current frame ID. Used for start of frame detection + /*!< Bulk Mode */ + uint8_t reassembling; // Indicates whether packet aggregation is occurring in bulk mode. + uint8_t fid; + uint32_t seq, hold_seq; + uint32_t pts, hold_pts; + uint32_t last_scr, hold_last_scr; + size_t got_bytes, hold_bytes; +}; diff --git a/host/class/uvc/usb_host_uvc_2/uvc_control.c b/host/class/uvc/usb_host_uvc_2/uvc_control.c new file mode 100644 index 00000000..6dddd936 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/uvc_control.c @@ -0,0 +1,151 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +// This file will contain all Class-Specific request from USB UVC specification chapter 4 + +#include // For memset + +#include "esp_check.h" + +#include "uvc_control.h" +#include "usb/usb_types_ch9.h" +#include "usb/usb_types_uvc.h" +#include "uvc_types_priv.h" +#include "uvc_descriptors_priv.h" + +static const char *TAG = "UVC-control"; + +static uint16_t uvc_vs_control_size(uint16_t uvc_version) +{ + if (uvc_version < UVC_VERSION_1_1) { + return 26; + } else if (uvc_version < UVC_VERSION_1_5) { + return 34; + } else { + return 48; + } +} + +static esp_err_t uvc_host_stream_control(uvc_host_stream_hdl_t stream_hdl, uvc_vs_ctrl_t *vs_control, uvc_host_stream_format_t *vs_format, enum uvc_req_code req_code, bool commit) +{ + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + uint8_t bmRequestType, bRequest; + uint16_t wValue, wIndex, wLength; + esp_err_t ret = ESP_OK; + const bool set = (req_code == UVC_SET_CUR) ? true : false; + + bmRequestType = USB_BM_REQUEST_TYPE_TYPE_CLASS | USB_BM_REQUEST_TYPE_RECIP_INTERFACE; + bmRequestType |= set ? USB_BM_REQUEST_TYPE_DIR_OUT : USB_BM_REQUEST_TYPE_DIR_IN; + bRequest = (uint8_t)req_code; + wValue = (commit ? UVC_VS_COMMIT_CONTROL : UVC_VS_PROBE_CONTROL) << 8; + wIndex = uvc_stream->bInterfaceNumber; + wLength = uvc_vs_control_size(uvc_stream->bcdUVC); + + if (set) { + // Set Video Stream control parameters + // see USB UVC specification ver 1.5, table 4-75 + const uvc_format_desc_t *format_desc; + const uvc_frame_desc_t *frame_desc; + + ESP_RETURN_ON_ERROR( + uvc_desc_get_frame_format_by_format(uvc_stream, uvc_stream->bInterfaceNumber, vs_format, &format_desc, &frame_desc), + TAG, "Could not find format that matches required format"); + UVC_CHECK(format_desc && frame_desc, ESP_ERR_NOT_FOUND); + + vs_control->bFormatIndex = format_desc->bFormatIndex; + vs_control->bFrameIndex = frame_desc->bFrameIndex; + vs_control->dwFrameInterval = UVC_DESC_FPS_TO_DWFRAMEINTERVAL(vs_format->fps); + } + + // Issue CTRL request + ESP_RETURN_ON_ERROR( + uvc_host_usb_ctrl(stream_hdl, bmRequestType, bRequest, wValue, wIndex, wLength, (uint8_t *)vs_control), + TAG, "Control request failed"); + + if (!set) { + // Get Video Stream control parameters from the received data and parse it to user + const uvc_format_desc_t *format_desc = NULL; + const uvc_frame_desc_t *frame_desc = NULL; + ESP_RETURN_ON_ERROR( + uvc_desc_get_frame_format_by_index(uvc_stream, uvc_stream->bInterfaceNumber, vs_control->bFormatIndex, vs_control->bFrameIndex, &format_desc, &frame_desc), + TAG, "Could not find requested frame format"); + + vs_format->format = uvc_desc_parse_format(format_desc); + vs_format->h_res = frame_desc->wWidth; + vs_format->v_res = frame_desc->wHeight; + vs_format->fps = UVC_DESC_DWFRAMEINTERVAL_TO_FPS(vs_control->dwFrameInterval); + } + return ret; +} + +static inline esp_err_t uvc_host_stream_control_probe_set(uvc_host_stream_hdl_t stream_hdl, uvc_vs_ctrl_t *vs_control, const uvc_host_stream_format_t *vs_format) +{ + return uvc_host_stream_control(stream_hdl, vs_control, (uvc_host_stream_format_t *)vs_format, UVC_SET_CUR, false); +} + +static inline esp_err_t uvc_host_stream_control_probe_get(uvc_host_stream_hdl_t stream_hdl, uvc_vs_ctrl_t *vs_control, uvc_host_stream_format_t *vs_format) +{ + return uvc_host_stream_control(stream_hdl, vs_control, vs_format, UVC_GET_CUR, false); +} + +static inline esp_err_t uvc_host_stream_control_probe_get_max(uvc_host_stream_hdl_t stream_hdl, uvc_vs_ctrl_t *vs_control, uvc_host_stream_format_t *vs_format) +{ + return uvc_host_stream_control(stream_hdl, vs_control, vs_format, UVC_GET_MAX, false); +} + +static inline esp_err_t uvc_host_stream_control_probe_get_min(uvc_host_stream_hdl_t stream_hdl, uvc_vs_ctrl_t *vs_control, uvc_host_stream_format_t *vs_format) +{ + return uvc_host_stream_control(stream_hdl, vs_control, vs_format, UVC_GET_MIN, false); +} + +static inline esp_err_t uvc_host_stream_control_commit(uvc_host_stream_hdl_t stream_hdl, uvc_vs_ctrl_t *vs_control, const uvc_host_stream_format_t *vs_format) +{ + return uvc_host_stream_control(stream_hdl, vs_control, (uvc_host_stream_format_t *)vs_format, UVC_SET_CUR, true); +} + +static inline bool uvc_is_vs_format_equal(const uvc_host_stream_format_t *a, const uvc_host_stream_format_t *b) +{ + if (a->h_res == b->h_res && + a->v_res == b->v_res && + a->fps == b->fps && + a->format == b->format) { + return true; + } + return false; +} + +esp_err_t uvc_host_stream_control_negotiate(uvc_host_stream_hdl_t stream_hdl, const uvc_host_stream_format_t *vs_format, uvc_vs_ctrl_t *vs_result) +{ + /* + This is not a 'real' negotiation as we return OK only if set the format exactly as expected. + In future, we can make vs_format parameter inout. This way we would return the real (close enough) set VS format. + */ + UVC_CHECK(stream_hdl && vs_format, ESP_ERR_INVALID_ARG); + esp_err_t ret = ESP_ERR_NOT_FOUND; + + // Try 2x + memset(vs_result, 0, sizeof(uvc_vs_ctrl_t)); + uvc_vs_ctrl_t fake_result = {0}; + uvc_host_stream_format_t set_format, fake_format; + for (int i = 0; i < 2; i++) { + // We do this to mimic Windows driver + uvc_host_stream_control_probe_get(stream_hdl, &fake_result, &fake_format); + uvc_host_stream_control_probe_set(stream_hdl, &fake_result, &fake_format); + uvc_host_stream_control_probe_get_max(stream_hdl, &fake_result, &fake_format); + uvc_host_stream_control_probe_get_min(stream_hdl, &fake_result, &fake_format); + + ret = uvc_host_stream_control_probe_set(stream_hdl, vs_result, vs_format); + ret |= uvc_host_stream_control_probe_get(stream_hdl, vs_result, &set_format); + UVC_CHECK(ret == ESP_OK, ret); + if (uvc_is_vs_format_equal(&set_format, vs_format)) { + break; + } + } + + ESP_LOGD(TAG, "Frame format negotiation:\n\tRequested: %dx%d@%dFPS\n\tGot: %dx%d@%dFPS", + vs_format->h_res, vs_format->v_res, vs_format->fps, set_format.h_res, set_format.v_res, set_format.fps); + + return uvc_host_stream_control_commit(stream_hdl, vs_result, vs_format); +} diff --git a/host/class/uvc/usb_host_uvc_2/uvc_desc.txt b/host/class/uvc/usb_host_uvc_2/uvc_desc.txt new file mode 100644 index 00000000..6ab8cd5d --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/uvc_desc.txt @@ -0,0 +1,1677 @@ + + =========================== USB Port1 =========================== + +Connection Status : 0x01 (Device is connected) +Port Chain : 3-1-3-4-1 + + ========================== Summary ========================= +Vendor ID : 0x32E4 (Ailipu Technology Co., Ltd.) +Product ID : 0x9422 +USB version : 2.00 +Port maximum Speed : High-Speed +Device maximum Speed : High-Speed +Device Connection Speed : High-Speed +Self Powered : no +Demanded Current : 500 mA +Used Endpoints : 2 + + ======================== USB Device ======================== + + +++++++++++++++++ Device Information ++++++++++++++++++ +Device Description : USB Composite Device +Device Path : \\?\USB#VID_32E4&PID_9422#2020032801#{a5dcbf10-6530-11d2-901f-00c04fb951ed} (GUID_DEVINTERFACE_USB_DEVICE) +Kernel Name : \Device\USBPDO-19 +Device ID : USB\VID_32E4&PID_9422\2020032801 +Hardware IDs : USB\VID_32E4&PID_9422&REV_0100 USB\VID_32E4&PID_9422 +Driver KeyName : {36fc9e60-c465-11cf-8056-444553540000}\0041 (GUID_DEVCLASS_USB) +Driver : \SystemRoot\System32\drivers\usbccgp.sys (Version: 10.0.19041.4355 Date: 2024-05-01) +Driver Inf : C:\Windows\inf\usb.inf +Legacy BusType : PNPBus +Class : USB +Class GUID : {36fc9e60-c465-11cf-8056-444553540000} (GUID_DEVCLASS_USB) +Service : usbccgp +Enumerator : USB +Location Info : Port_#0001.Hub_#0012 +Location IDs : PCIROOT(0)#PCI(0803)#PCI(0004)#USBROOT(0)#USB(1)#USB(3)#USB(4)#USB(1), ACPI(_SB_)#ACPI(PCI0)#ACPI(GP19)#ACPI(XHC4)#ACPI(RHUB)#ACPI(PRT1)#USB(3)#USB(4)#USB(1) +Container ID : {068caf32-6f04-5def-b70f-1dfab2acdd8e} +Manufacturer Info : (Standard USB Host Controller) +Capabilities : 0x94 (Removable, UniqueID, SurpriseRemovalOK) +Status : 0x0180600A (DN_DRIVER_LOADED, DN_STARTED, DN_DISABLEABLE, DN_REMOVABLE, DN_NT_ENUMERATOR, DN_NT_DRIVER) +Problem Code : 0 +Address : 1 +HcDisableSelectiveSuspend: 0 +EnableSelectiveSuspend : 0 +SelectiveSuspendEnabled : 0 +EnhancedPowerMgmtEnabled : 0 +IdleInWorkingState : 0 +WakeFromSleepState : 0 +Power State : D0 (supported: D0, D3, wake from D0) + Child Device 1 : USB Camera (USB Video Device) + Device Path 1 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 2 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\global (STATIC_KSCATEGORY_VIDEO_CAMERA) + Device Path 3 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global (AM_KSCATEGORY_CAPTURE) + Device Path 4 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 5 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 6 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 7 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 8 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 9 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Device Path 10 : \\?\USB#VID_32E4&PID_9422&MI_00#a&149655b1&0&0000#{6994ad05-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_VIDEO) + Kernel Name : \Device\00000492 + Device ID : USB\VID_32E4&PID_9422&MI_00\A&149655B1&0&0000 + Class : Camera + Driver KeyName : {ca3e7ab9-b4c3-4ae6-8251-579ef933890f}\0004 (GUID_DEVCLASS_CAMERA) + Service : usbvideo + Location : 0005.0000.0004.001.003.004.001.000.000 + LocationPaths : PCIROOT(0)#PCI(0803)#PCI(0004)#USBROOT(0)#USB(1)#USB(3)#USB(4)#USB(1)#USBMI(0) PCIROOT(0)#PCI(0803)#PCI(0004)#USBROOT(0)#USB(1)#USB(3)#USB(4)#USB(1)#USB(1) ACPI(_SB_)#ACPI(PCI0)#ACPI(GP19)#ACPI(XHC4)#ACPI(RHUB)#ACPI(PRT1)#USB(3)#USB(4)#USB(1)#USBMI(0) ACPI(_SB_)#ACPI(PCI0)#ACPI(GP19)#ACPI(XHC4)#ACPI(RHUB)#ACPI(PRT1)#USB(3)#USB(4)#USB(1)#USB(1) + Child Device 2 : H264 USB Camera (USB Audio Device) + Device Path 1 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 2 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global (AM_KSCATEGORY_CAPTURE) + Device Path 3 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 4 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 5 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 6 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 7 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 8 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Device Path 9 : \\?\USB#VID_32E4&PID_9422&MI_03#a&149655b1&0&0003#{6994ad04-93ef-11d0-a3cc-00a0c9223196}\global (AM_KSCATEGORY_AUDIO) + Kernel Name : \Device\00000493 + Device ID : USB\VID_32E4&PID_9422&MI_03\A&149655B1&0&0003 + Class : MEDIA + Driver KeyName : {4d36e96c-e325-11ce-bfc1-08002be10318}\0008 (GUID_DEVCLASS_MEDIA) + Service : usbaudio + Location : 0005.0000.0004.001.003.004.001.000.000 + LocationPaths : PCIROOT(0)#PCI(0803)#PCI(0004)#USBROOT(0)#USB(1)#USB(3)#USB(4)#USB(1)#USBMI(3) ACPI(_SB_)#ACPI(PCI0)#ACPI(GP19)#ACPI(XHC4)#ACPI(RHUB)#ACPI(PRT1)#USB(3)#USB(4)#USB(1)#USBMI(3) + Child Device 1 : Microphone (H264 USB Camera) (Audio Endpoint) + Device ID : SWD\MMDEVAPI\{0.0.1.00000000}.{D056F20B-7F71-4AD3-B253-6D0DCC067823} + Class : AudioEndpoint + Driver KeyName : {c166523c-fe0c-4a94-a586-f1a80cfbbf3e}\0029 (AUDIOENDPOINT_CLASS_UUID) + + +++++++++++++++++ Registry USB Flags +++++++++++++++++ +HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\usbflags\32E494220100 + osvc : REG_BINARY 00 00 + NewInterfaceUsage : REG_DWORD 00000000 (0) + + ---------------- Connection Information --------------- +Connection Index : 0x01 (Port 1) +Connection Status : 0x01 (DeviceConnected) +Current Config Value : 0x01 (Configuration 1) +Device Address : 0x08 (8) +Is Hub : 0x00 (no) +Device Bus Speed : 0x02 (High-Speed) +Number Of Open Pipes : 0x01 (1 pipe to data endpoints) +Pipe[0] : EndpointID=3 Direction=IN ScheduleOffset=0 Type=Interrupt +Data (HexDump) : 01 00 00 00 12 01 00 02 EF 02 01 40 E4 32 22 94 ...........@.2". + 00 01 02 01 03 01 01 02 00 08 00 01 00 00 00 01 ................ + 00 00 00 07 05 83 03 10 00 06 00 00 00 00 .............. + + --------------- Connection Information V2 ------------- +Connection Index : 0x01 (1) +Length : 0x10 (16 bytes) +SupportedUsbProtocols : 0x03 + Usb110 : 1 (yes, port supports USB 1.1) + Usb200 : 1 (yes, port supports USB 2.0) + Usb300 : 0 (no, port not supports USB 3.0) + ReservedMBZ : 0x00 +Flags : 0x00 + DevIsOpAtSsOrHigher : 0 (Device is not operating at SuperSpeed or higher) + DevIsSsCapOrHigher : 0 (Device is not SuperSpeed capable or higher) + DevIsOpAtSsPlusOrHigher : 0 (Device is not operating at SuperSpeedPlus or higher) + DevIsSsPlusCapOrHigher : 0 (Device is not SuperSpeedPlus capable or higher) + ReservedMBZ : 0x00 +Data (HexDump) : 01 00 00 00 10 00 00 00 03 00 00 00 00 00 00 00 ................ + + ---------------------- Device Descriptor ---------------------- +bLength : 0x12 (18 bytes) +bDescriptorType : 0x01 (Device Descriptor) +bcdUSB : 0x200 (USB Version 2.00) +bDeviceClass : 0xEF (Miscellaneous) +bDeviceSubClass : 0x02 +bDeviceProtocol : 0x01 (IAD - Interface Association Descriptor) +bMaxPacketSize0 : 0x40 (64 bytes) +idVendor : 0x32E4 (Ailipu Technology Co., Ltd.) +idProduct : 0x9422 +bcdDevice : 0x0100 +iManufacturer : 0x02 (String Descriptor 2) + Language 0x0409 : "H264 USB Camera" +iProduct : 0x01 (String Descriptor 1) + Language 0x0409 : "H264 USB Camera" +iSerialNumber : 0x03 (String Descriptor 3) + Language 0x0409 : "2020032801" +bNumConfigurations : 0x01 (1 Configuration) +Data (HexDump) : 12 01 00 02 EF 02 01 40 E4 32 22 94 00 01 02 01 .......@.2"..... + 03 01 .. + + ------------------ Configuration Descriptor ------------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x02 (Configuration Descriptor) +wTotalLength : 0x0592 (1426 bytes) +bNumInterfaces : 0x05 (5 Interfaces) +bConfigurationValue : 0x01 (Configuration 1) +iConfiguration : 0x00 (No String Descriptor) +bmAttributes : 0x80 + D7: Reserved, set 1 : 0x01 + D6: Self Powered : 0x00 (no) + D5: Remote Wakeup : 0x00 (no) + D4..0: Reserved, set 0 : 0x00 +MaxPower : 0xFA (500 mA) +Data (HexDump) : 09 02 92 05 05 01 00 80 FA 08 0B 00 03 0E 03 00 ................ + 05 09 04 00 00 01 0E 01 00 05 0E 24 01 00 01 73 ...........$...s + 00 C0 E1 E4 00 02 01 02 09 24 03 05 01 01 00 04 .........$...... + 00 1C 24 06 03 70 33 F0 28 11 63 2E 4A BA 2C 68 ..$..p3.(.c.J.,h + 90 EB 33 40 16 18 01 02 03 FF FF FF 00 1A 24 06 ..3@..........$. + 04 94 73 DF DD 3E 97 27 47 BE D9 04 ED 64 26 DC ..s..>.'G....d&. + 67 08 01 03 01 FF 00 12 24 02 01 01 02 00 00 00 g.......$....... + 00 00 00 00 00 03 0E 20 00 0B 24 05 02 01 00 00 ....... ..$..... + 02 7F 17 00 09 24 03 06 01 01 00 04 00 07 05 83 .....$.......... + 03 10 00 06 05 25 03 40 00 09 04 01 00 00 0E 02 .....%.@........ + 00 00 0F 24 01 02 47 02 81 00 05 00 00 00 01 00 ...$..G......... + 00 0B 24 06 01 08 00 01 00 00 00 00 26 24 07 01 ..$.........&$.. + 00 80 07 38 04 00 C0 A9 1D 00 80 53 3B 4D 4A 3F ...8.......S;MJ? + 00 15 16 05 00 03 15 16 05 00 80 1A 06 00 2A 2C ..............*, + 0A 00 26 24 07 02 00 00 05 D0 02 00 00 2F 0D 00 ..&$........./.. + 00 5E 1A 4D 22 1C 00 15 16 05 00 03 15 16 05 00 .^.M"........... + 80 1A 06 00 2A 2C 0A 00 26 24 07 03 00 20 03 58 ....*,..&$... .X + 02 00 D0 DD 06 00 A0 BB 0D 4D A8 0E 00 15 16 05 .........M...... + 00 03 15 16 05 00 80 1A 06 00 2A 2C 0A 00 26 24 ..........*,..&$ + 07 04 00 80 02 E0 01 00 00 65 04 00 00 CA 08 4D .........e.....M + 62 09 00 15 16 05 00 03 15 16 05 00 80 1A 06 00 b............... + 2A 2C 0A 00 26 24 07 05 00 80 02 68 01 00 C0 4B *,..&$.....h...K + 03 00 80 97 06 4D 0A 07 00 15 16 05 00 03 15 16 .....M.......... + 05 00 80 1A 06 00 2A 2C 0A 00 26 24 07 06 00 60 ......*,..&$...` + 01 20 01 00 40 73 01 00 80 E6 02 4D 1A 03 00 15 . ..@s.....M.... + 16 05 00 03 15 16 05 00 80 1A 06 00 2A 2C 0A 00 ............*,.. + 26 24 07 07 00 40 01 F0 00 00 40 19 01 00 80 32 &$...@....@....2 + 02 4D 5A 02 00 15 16 05 00 03 15 16 05 00 80 1A .MZ............. + 06 00 2A 2C 0A 00 26 24 07 08 00 80 07 38 04 00 ..*,..&$.....8.. + C0 A9 1D 00 80 53 3B 4D 4A 3F 00 15 16 05 00 03 .....S;MJ?...... + 15 16 05 00 80 1A 06 00 2A 2C 0A 00 1B 24 04 02 ........*,...$.. + 06 59 55 59 32 00 00 10 00 80 00 00 AA 00 38 9B .YUY2.........8. + 71 10 01 00 00 00 00 26 24 05 01 00 80 02 E0 01 q......&$....... + 00 00 65 04 00 00 CA 08 00 60 09 00 15 16 05 00 ..e......`...... + 03 15 16 05 00 80 1A 06 00 2A 2C 0A 00 1E 24 05 .........*,...$. + 02 00 20 03 58 02 00 D0 DD 06 00 D0 DD 06 00 A6 .. .X........... + 0E 00 2A 2C 0A 00 01 2A 2C 0A 00 26 24 05 03 00 ..*,...*,..&$... + 80 02 68 01 00 C0 4B 03 00 80 97 06 00 08 07 00 ..h...K......... + 15 16 05 00 03 15 16 05 00 80 1A 06 00 2A 2C 0A .............*,. + 00 26 24 05 04 00 60 01 20 01 00 40 73 01 00 80 .&$...`. ..@s... + E6 02 00 18 03 00 15 16 05 00 03 15 16 05 00 80 ................ + 1A 06 00 2A 2C 0A 00 26 24 05 05 00 40 01 F0 00 ...*,..&$...@... + 00 40 19 01 00 80 32 02 00 58 02 00 15 16 05 00 .@....2..X...... + 03 15 16 05 00 80 1A 06 00 2A 2C 0A 00 26 24 05 .........*,..&$. + 06 00 80 02 E0 01 00 00 65 04 00 00 CA 08 00 60 ........e......` + 09 00 15 16 05 00 03 15 16 05 00 80 1A 06 00 2A ...............* + 2C 0A 00 06 24 0D 01 01 04 09 04 01 01 01 0E 02 ,...$........... + 00 00 07 05 81 05 80 00 01 09 04 01 02 01 0E 02 ................ + 00 00 07 05 81 05 00 01 01 09 04 01 03 01 0E 02 ................ + 00 00 07 05 81 05 20 03 01 09 04 01 04 01 0E 02 ...... ......... + 00 00 07 05 81 05 20 0B 01 09 04 01 05 01 0E 02 ...... ......... + 00 00 07 05 81 05 20 13 01 09 04 01 06 01 0E 02 ...... ......... + 00 00 07 05 81 05 00 14 01 09 04 02 00 00 0E 02 ................ + 00 00 0E 24 01 01 60 01 82 00 06 00 00 00 01 00 ...$..`......... + 1C 24 10 01 08 48 32 36 34 00 00 10 00 80 00 00 .$...H264....... + AA 00 38 9B 71 10 01 00 00 00 00 01 26 24 11 01 ..8.q.......&$.. + 00 80 07 38 04 00 40 E3 09 00 80 C6 13 15 16 05 ...8..@......... + 00 03 00 00 00 00 15 16 05 00 80 1A 06 00 2A 2C ..............*, + 0A 00 26 24 11 02 00 00 05 D0 02 00 00 65 04 00 ..&$.........e.. + 00 CA 08 15 16 05 00 03 00 00 00 00 15 16 05 00 ................ + 80 1A 06 00 2A 2C 0A 00 26 24 11 03 00 20 03 58 ....*,..&$... .X + 02 00 F0 49 02 00 E0 93 04 15 16 05 00 03 00 00 ...I............ + 00 00 15 16 05 00 80 1A 06 00 2A 2C 0A 00 26 24 ..........*,..&$ + 11 04 00 80 02 E0 01 00 00 77 01 00 00 EE 02 15 .........w...... + 16 05 00 03 00 00 00 00 15 16 05 00 80 1A 06 00 ................ + 2A 2C 0A 00 26 24 11 05 00 80 02 68 01 00 40 19 *,..&$.....h..@. + 01 00 80 32 02 15 16 05 00 03 00 00 00 00 15 16 ...2............ + 05 00 80 1A 06 00 2A 2C 0A 00 26 24 11 06 00 60 ......*,..&$...` + 01 20 01 00 C0 7B 00 00 80 F7 00 15 16 05 00 03 . ...{.......... + 00 00 00 00 15 16 05 00 80 1A 06 00 2A 2C 0A 00 ............*,.. + 26 24 11 07 00 40 01 F0 00 00 C0 5D 00 00 80 BB &$...@.....].... + 00 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 26 24 11 08 00 80 07 38 04 00 ..*,..&$.....8.. + 40 E3 09 00 80 C6 13 15 16 05 00 03 00 00 00 00 @............... + 15 16 05 00 80 1A 06 00 2A 2C 0A 00 06 24 0D 01 ........*,...$.. + 01 04 09 04 02 01 01 0E 02 00 00 07 05 82 05 80 ................ + 00 01 09 04 02 02 01 0E 02 00 00 07 05 82 05 00 ................ + 01 01 09 04 02 03 01 0E 02 00 00 07 05 82 05 20 ............... + 03 01 09 04 02 04 01 0E 02 00 00 07 05 82 05 20 ............... + 0B 01 09 04 02 05 01 0E 02 00 00 07 05 82 05 20 ............... + 13 01 09 04 02 06 01 0E 02 00 00 07 05 82 05 00 ................ + 14 01 08 0B 03 02 01 00 00 00 09 04 03 00 00 01 ................ + 01 00 00 09 24 01 00 01 29 00 01 04 0C 24 02 01 ....$...)....$.. + 01 02 00 01 00 00 00 00 0B 24 06 02 01 02 01 00 .........$...... + 02 00 00 09 24 03 03 01 01 00 02 00 09 04 04 00 ....$........... + 00 01 02 00 00 09 04 04 01 01 01 02 00 00 07 24 ...............$ + 01 03 01 01 00 1D 24 02 01 01 02 10 07 40 1F 00 ......$......@.. + 11 2B 00 80 3E 00 22 56 00 C0 5D 00 44 AC 00 80 .+..>."V..].D... + BB 00 09 05 84 05 92 01 04 00 00 07 25 01 01 00 ............%... + 92 01 .. + + ------------------- IAD Descriptor -------------------- +bLength : 0x08 (8 bytes) +bDescriptorType : 0x0B +bFirstInterface : 0x00 +bInterfaceCount : 0x03 +bFunctionClass : 0x0E (Video) +bFunctionSubClass : 0x03 (Video Interface Collection) +bFunctionProtocol : 0x00 (PC_PROTOCOL_UNDEFINED protocol) +iFunction : 0x05 (String Descriptor 5) + Language 0x0409 : "USB Camera" +Data (HexDump) : 08 0B 00 03 0E 03 00 05 ........ + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x00 +bAlternateSetting : 0x00 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x01 (Video Control) +bInterfaceProtocol : 0x00 +iInterface : 0x05 (String Descriptor 5) + Language 0x0409 : "USB Camera" +Data (HexDump) : 09 04 00 00 01 0E 01 00 05 ......... + + ------- Video Control Interface Header Descriptor ----- +bLength : 0x0E (14 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x01 (Video Control Header) +bcdUVC : 0x0100 (UVC Version 1.00) +wTotalLength : 0x0073 (115 bytes) +dwClockFreq : 0x00E4E1C0 (15 MHz) +bInCollection : 0x02 (2 VideoStreaming interfaces) +baInterfaceNr[1] : 0x01 +baInterfaceNr[2] : 0x02 +Data (HexDump) : 0E 24 01 00 01 73 00 C0 E1 E4 00 02 01 02 .$...s........ + + ------- Video Control Output Terminal Descriptor ------ +bLength : 0x09 (9 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x03 (Output Terminal) +bTerminalID : 0x05 +wTerminalType : 0x0101 (TT_STREAMING) +bAssocTerminal : 0x00 (Not associated with an Input Terminal) +bSourceID : 0x04 +iTerminal : 0x00 +Data (HexDump) : 09 24 03 05 01 01 00 04 00 .$....... + + --------- Video Control Extension Unit Descriptor ----- +bLength : 0x1C (28 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x06 (Extension Unit) +bUnitID : 0x03 +guidExtensionCode : {28F03370-6311-4A2E-BA2C-6890EB334016} +bNumControls : 0x18 +bNrInPins : 0x01 (1 pins) +baSourceID[1] : 0x02 +bControlSize : 0x03 +bmControls : 0xFF, 0xFF, 0xFF + D0 : 1 yes - Vendor-Specific (Optional) + D1 : 1 yes - Vendor-Specific (Optional) + D2 : 1 yes - Vendor-Specific (Optional) + D3 : 1 yes - Vendor-Specific (Optional) + D4 : 1 yes - Vendor-Specific (Optional) + D5 : 1 yes - Vendor-Specific (Optional) + D6 : 1 yes - Vendor-Specific (Optional) + D7 : 1 yes - Vendor-Specific (Optional) + D8 : 1 yes - Vendor-Specific (Optional) + D9 : 1 yes - Vendor-Specific (Optional) + D10 : 1 yes - Vendor-Specific (Optional) + D11 : 1 yes - Vendor-Specific (Optional) + D12 : 1 yes - Vendor-Specific (Optional) + D13 : 1 yes - Vendor-Specific (Optional) + D14 : 1 yes - Vendor-Specific (Optional) + D15 : 1 yes - Vendor-Specific (Optional) + D16 : 1 yes - Vendor-Specific (Optional) + D17 : 1 yes - Vendor-Specific (Optional) + D18 : 1 yes - Vendor-Specific (Optional) + D19 : 1 yes - Vendor-Specific (Optional) + D20 : 1 yes - Vendor-Specific (Optional) + D21 : 1 yes - Vendor-Specific (Optional) + D22 : 1 yes - Vendor-Specific (Optional) + D23 : 1 yes - Vendor-Specific (Optional) +iExtension : 0x00 +Data (HexDump) : 1C 24 06 03 70 33 F0 28 11 63 2E 4A BA 2C 68 90 .$..p3.(.c.J.,h. + EB 33 40 16 18 01 02 03 FF FF FF 00 .3@......... + + --------- Video Control Extension Unit Descriptor ----- +bLength : 0x1A (26 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x06 (Extension Unit) +bUnitID : 0x04 +guidExtensionCode : {DDDF7394-973E-4727-BED9-04ED6426DC67} +bNumControls : 0x08 +bNrInPins : 0x01 (1 pins) +baSourceID[1] : 0x03 +bControlSize : 0x01 +bmControls : 0xFF + D0 : 1 yes - Vendor-Specific (Optional) + D1 : 1 yes - Vendor-Specific (Optional) + D2 : 1 yes - Vendor-Specific (Optional) + D3 : 1 yes - Vendor-Specific (Optional) + D4 : 1 yes - Vendor-Specific (Optional) + D5 : 1 yes - Vendor-Specific (Optional) + D6 : 1 yes - Vendor-Specific (Optional) + D7 : 1 yes - Vendor-Specific (Optional) +iExtension : 0x00 +Data (HexDump) : 1A 24 06 04 94 73 DF DD 3E 97 27 47 BE D9 04 ED .$...s..>.'G.... + 64 26 DC 67 08 01 03 01 FF 00 d&.g...... + + -------- Video Control Input Terminal Descriptor ------ +bLength : 0x12 (18 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x02 (Input Terminal) +bTerminalID : 0x01 +wTerminalType : 0x0201 (ITT_CAMERA) +bAssocTerminal : 0x00 (Not associated with an Output Terminal) +iTerminal : 0x00 +Camera Input Terminal Data: +wObjectiveFocalLengthMin : 0x0000 +wObjectiveFocalLengthMax : 0x0000 +wOcularFocalLength : 0x0000 +bControlSize : 0x03 +bmControls : 0x0E, 0x20, 0x00 + D0 : 0 no - Scanning Mode + D1 : 1 yes - Auto-Exposure Mode + D2 : 1 yes - Auto-Exposure Priority + D3 : 1 yes - Exposure Time (Absolute) + D4 : 0 no - Exposure Time (Relative) + D5 : 0 no - Focus (Absolute) + D6 : 0 no - Focus (Relative) + D7 : 0 no - Iris (Absolute) + D8 : 0 no - Iris (Relative) + D9 : 0 no - Zoom (Absolute) + D10 : 0 no - Zoom (Relative) + D11 : 0 no - Pan (Absolute) + D12 : 0 no - Pan (Relative) + D13 : 1 yes - Roll (Absolute) + D14 : 0 no - Roll (Relative) + D15 : 0 no - Tilt (Absolute) + D16 : 0 no - Tilt (Relative) + D17 : 0 no - Focus Auto + D18 : 0 no - Reserved + D19 : 0 no - Reserved + D20 : 0 no - Reserved + D21 : 0 no - Reserved + D22 : 0 no - Reserved + D23 : 0 no - Reserved +Data (HexDump) : 12 24 02 01 01 02 00 00 00 00 00 00 00 00 03 0E .$.............. + 20 00 . + + -------- Video Control Processing Unit Descriptor ----- +bLength : 0x0B (11 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x05 (Processing Unit) +bUnitID : 0x02 +bSourceID : 0x01 +wMaxMultiplier : 0x0000 +bControlSize : 0x02 +bmControls : 0x7F, 0x17 + D0 : 1 yes - Brightness + D1 : 1 yes - Contrast + D2 : 1 yes - Hue + D3 : 1 yes - Saturation + D4 : 1 yes - Sharpness + D5 : 1 yes - Gamma + D6 : 1 yes - White Balance Temperature + D7 : 0 no - White Balance Component + D8 : 1 yes - Backlight Compensation + D9 : 1 yes - Gain + D10 : 1 yes - Power Line Frequency + D11 : 0 no - Hue, Auto + D12 : 1 yes - White Balance Temperature, Auto + D13 : 0 no - White Balance Component, Auto + D14 : 0 no - Digital Multiplier + D15 : 0 no - Digital Multiplier Limit +iProcessing : 0x00 +Data (HexDump) : 0B 24 05 02 01 00 00 02 7F 17 00 .$......... + + ------- Video Control Output Terminal Descriptor ------ +bLength : 0x09 (9 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x03 (Output Terminal) +bTerminalID : 0x06 +wTerminalType : 0x0101 (TT_STREAMING) +bAssocTerminal : 0x00 (Not associated with an Input Terminal) +bSourceID : 0x04 +iTerminal : 0x00 +Data (HexDump) : 09 24 03 06 01 01 00 04 00 .$....... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x83 (Direction=IN EndpointID=3) +bmAttributes : 0x03 (TransferType=Interrupt) +wMaxPacketSize : 0x0010 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x10 (16 bytes per packet) +bInterval : 0x06 (6 ms) +Data (HexDump) : 07 05 83 03 10 00 06 ....... + + --- Class-specific VC Interrupt Endpoint Descriptor --- +bLength : 0x05 (5 bytes) +bDescriptorType : 0x25 (Video Control Endpoint) +bDescriptorSubtype : 0x03 (Interrupt) +wMaxTransferSize : 0x0040 (64 bytes) +Data (HexDump) : 05 25 03 40 00 .%.@. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x00 +bNumEndpoints : 0x00 (Default Control Pipe only) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 00 00 0E 02 00 00 ......... + + ---- VC-Specific VS Video Input Header Descriptor ----- +bLength : 0x0F (15 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x01 (Input Header) +bNumFormats : 0x02 +wTotalLength : 0x0247 (583 bytes) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmInfo : 0x00 (Dynamic Format Change not supported) +bTerminalLink : 0x05 +bStillCaptureMethod : 0x00 (No Still Capture) +nbTriggerSupport : 0x00 (Hardware Triggering not supported) +bTriggerUsage : 0x00 (Host will initiate still image capture) +nbControlSize : 0x01 +Video Payload Format 1 : 0x00 + D0 : 0 no - Key Frame Rate + D1 : 0 no - P Frame Rate + D2 : 0 no - Compression Quality + D3 : 0 no - Compression Window Size + D4 : 0 no - Generate Key Frame + D5 : 0 no - Update Frame Segment + D6 : 0 no - Reserved + D7 : 0 no - Reserved +Video Payload Format 2 : 0x00 + D0 : 0 no - Key Frame Rate + D1 : 0 no - P Frame Rate + D2 : 0 no - Compression Quality + D3 : 0 no - Compression Window Size + D4 : 0 no - Generate Key Frame + D5 : 0 no - Update Frame Segment + D6 : 0 no - Reserved + D7 : 0 no - Reserved +Data (HexDump) : 0F 24 01 02 47 02 81 00 05 00 00 00 01 00 00 .$..G.......... + + ----- Video Streaming MJPEG Format Type Descriptor ---- +bLength : 0x0B (11 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x06 (Format MJPEG) +bFormatIndex : 0x01 (1) +bNumFrameDescriptors : 0x08 (8) +bmFlags : 0x00 (Sample size is not fixed) +bDefaultFrameIndex : 0x01 (1) +bAspectRatioX : 0x00 +bAspectRatioY : 0x00 +bmInterlaceFlags : 0x00 + D0 IL stream or variable: 0 (no) + D1 Fields per frame : 0 (2 fields) + D2 Field 1 first : 0 (no) + D3 Reserved : 0 + D4..5 Field pattern : 0 (Field 1 only) + D6..7 Display Mode : 0 (Bob only) +bCopyProtect : 0x00 (No restrictions) +*!*ERROR: no Color Matching Descriptor for this format +Data (HexDump) : 0B 24 06 01 08 00 01 00 00 00 00 .$......... + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +---> This is the Default (optimum) Frame index +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x01 +bmCapabilities : 0x00 +wWidth : 0x0780 (1920) +wHeight : 0x0438 (1080) +dwMinBitRate : 0x1DA9C000 (497664000 bps -> 62.208 MB/s) +dwMaxBitRate : 0x3B538000 (995328000 bps -> 124.416 MB/s) +dwMaxVideoFrameBufferSize: 0x003F4A4D (4147789 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 01 00 80 07 38 04 00 C0 A9 1D 00 80 53 &$.....8.......S + 3B 4D 4A 3F 00 15 16 05 00 03 15 16 05 00 80 1A ;MJ?............ + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x02 +bmCapabilities : 0x00 +wWidth : 0x0500 (1280) +wHeight : 0x02D0 (720) +dwMinBitRate : 0x0D2F0000 (221184000 bps -> 27.648 MB/s) +dwMaxBitRate : 0x1A5E0000 (442368000 bps -> 55.296 MB/s) +dwMaxVideoFrameBufferSize: 0x001C224D (1843789 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 02 00 00 05 D0 02 00 00 2F 0D 00 00 5E &$........./...^ + 1A 4D 22 1C 00 15 16 05 00 03 15 16 05 00 80 1A .M"............. + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x03 +bmCapabilities : 0x00 +wWidth : 0x0320 (800) +wHeight : 0x0258 (600) +dwMinBitRate : 0x06DDD000 (115200000 bps -> 14.400 MB/s) +dwMaxBitRate : 0x0DBBA000 (230400000 bps -> 28.800 MB/s) +dwMaxVideoFrameBufferSize: 0x000EA84D (960589 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 03 00 20 03 58 02 00 D0 DD 06 00 A0 BB &$... .X........ + 0D 4D A8 0E 00 15 16 05 00 03 15 16 05 00 80 1A .M.............. + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x04 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x01E0 (480) +dwMinBitRate : 0x04650000 (73728000 bps -> 9.216 MB/s) +dwMaxBitRate : 0x08CA0000 (147456000 bps -> 18.432 MB/s) +dwMaxVideoFrameBufferSize: 0x0009624D (614989 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 04 00 80 02 E0 01 00 00 65 04 00 00 CA &$.........e.... + 08 4D 62 09 00 15 16 05 00 03 15 16 05 00 80 1A .Mb............. + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x05 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x0168 (360) +dwMinBitRate : 0x034BC000 (55296000 bps -> 6.912 MB/s) +dwMaxBitRate : 0x06978000 (110592000 bps -> 13.824 MB/s) +dwMaxVideoFrameBufferSize: 0x00070A4D (461389 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 05 00 80 02 68 01 00 C0 4B 03 00 80 97 &$.....h...K.... + 06 4D 0A 07 00 15 16 05 00 03 15 16 05 00 80 1A .M.............. + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x06 +bmCapabilities : 0x00 +wWidth : 0x0160 (352) +wHeight : 0x0120 (288) +dwMinBitRate : 0x01734000 (24330240 bps -> 3.41 MB/s) +dwMaxBitRate : 0x02E68000 (48660480 bps -> 6.82 MB/s) +dwMaxVideoFrameBufferSize: 0x00031A4D (203341 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 06 00 60 01 20 01 00 40 73 01 00 80 E6 &$...`. ..@s.... + 02 4D 1A 03 00 15 16 05 00 03 15 16 05 00 80 1A .M.............. + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x07 +bmCapabilities : 0x00 +wWidth : 0x0140 (320) +wHeight : 0x00F0 (240) +dwMinBitRate : 0x01194000 (18432000 bps -> 2.304 MB/s) +dwMaxBitRate : 0x02328000 (36864000 bps -> 4.608 MB/s) +dwMaxVideoFrameBufferSize: 0x00025A4D (154189 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 07 00 40 01 F0 00 00 40 19 01 00 80 32 &$...@....@....2 + 02 4D 5A 02 00 15 16 05 00 03 15 16 05 00 80 1A .MZ............. + 06 00 2A 2C 0A 00 ..*,.. + + ----- Video Streaming MJPEG Frame Type Descriptor ----- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x07 (MJPEG Frame Type) +bFrameIndex : 0x08 +bmCapabilities : 0x00 +wWidth : 0x0780 (1920) +wHeight : 0x0438 (1080) +dwMinBitRate : 0x1DA9C000 (497664000 bps -> 62.208 MB/s) +dwMaxBitRate : 0x3B538000 (995328000 bps -> 124.416 MB/s) +dwMaxVideoFrameBufferSize: 0x003F4A4D (4147789 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 07 08 00 80 07 38 04 00 C0 A9 1D 00 80 53 &$.....8.......S + 3B 4D 4A 3F 00 15 16 05 00 03 15 16 05 00 80 1A ;MJ?............ + 06 00 2A 2C 0A 00 ..*,.. + + ------- VS Uncompressed Format Type Descriptor -------- +bLength : 0x1B (27 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x04 (Uncompressed Format Type) +bFormatIndex : 0x02 (2) +bNumFrameDescriptors : 0x06 (6) +guidFormat : {32595559-0000-0010-8000-00AA00389B71} (YUY2) +bBitsPerPixel : 0x10 (16 bits) +bDefaultFrameIndex : 0x01 (1) +bAspectRatioX : 0x00 +bAspectRatioY : 0x00 +bmInterlaceFlags : 0x00 + D0 IL stream or variable: 0 (no) + D1 Fields per frame : 0 (2 fields) + D2 Field 1 first : 0 (no) + D3 Reserved : 0 + D4..5 Field pattern : 0 (Field 1 only) + D6..7 Display Mode : 0 (Bob only) +bCopyProtect : 0x00 (No restrictions) +Data (HexDump) : 1B 24 04 02 06 59 55 59 32 00 00 10 00 80 00 00 .$...YUY2....... + AA 00 38 9B 71 10 01 00 00 00 00 ..8.q...... + + -------- VS Uncompressed Frame Type Descriptor -------- +---> This is the Default (optimum) Frame index +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x05 (Uncompressed Frame Type) +bFrameIndex : 0x01 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x01E0 (480) +dwMinBitRate : 0x04650000 (73728000 bps -> 9.216 MB/s) +dwMaxBitRate : 0x08CA0000 (147456000 bps -> 18.432 MB/s) +dwMaxVideoFrameBufferSize: 0x00096000 (614400 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 05 01 00 80 02 E0 01 00 00 65 04 00 00 CA &$.........e.... + 08 00 60 09 00 15 16 05 00 03 15 16 05 00 80 1A ..`............. + 06 00 2A 2C 0A 00 ..*,.. + + -------- VS Uncompressed Frame Type Descriptor -------- +bLength : 0x1E (30 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x05 (Uncompressed Frame Type) +bFrameIndex : 0x02 +bmCapabilities : 0x00 +wWidth : 0x0320 (800) +wHeight : 0x0258 (600) +dwMinBitRate : 0x06DDD000 (115200000 bps -> 14.400 MB/s) +dwMaxBitRate : 0x06DDD000 (115200000 bps -> 14.400 MB/s) +dwMaxVideoFrameBufferSize: 0x000EA600 (960000 bytes) +dwDefaultFrameInterval : 0x000A2C2A (66.6666 ms -> 15.000 fps) +bFrameIntervalType : 0x01 (1 discrete frame interval supported) +adwFrameInterval[1] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 1E 24 05 02 00 20 03 58 02 00 D0 DD 06 00 D0 DD .$... .X........ + 06 00 A6 0E 00 2A 2C 0A 00 01 2A 2C 0A 00 .....*,...*,.. + + -------- VS Uncompressed Frame Type Descriptor -------- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x05 (Uncompressed Frame Type) +bFrameIndex : 0x03 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x0168 (360) +dwMinBitRate : 0x034BC000 (55296000 bps -> 6.912 MB/s) +dwMaxBitRate : 0x06978000 (110592000 bps -> 13.824 MB/s) +dwMaxVideoFrameBufferSize: 0x00070800 (460800 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 05 03 00 80 02 68 01 00 C0 4B 03 00 80 97 &$.....h...K.... + 06 00 08 07 00 15 16 05 00 03 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + -------- VS Uncompressed Frame Type Descriptor -------- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x05 (Uncompressed Frame Type) +bFrameIndex : 0x04 +bmCapabilities : 0x00 +wWidth : 0x0160 (352) +wHeight : 0x0120 (288) +dwMinBitRate : 0x01734000 (24330240 bps -> 3.41 MB/s) +dwMaxBitRate : 0x02E68000 (48660480 bps -> 6.82 MB/s) +dwMaxVideoFrameBufferSize: 0x00031800 (202752 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 05 04 00 60 01 20 01 00 40 73 01 00 80 E6 &$...`. ..@s.... + 02 00 18 03 00 15 16 05 00 03 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + -------- VS Uncompressed Frame Type Descriptor -------- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x05 (Uncompressed Frame Type) +bFrameIndex : 0x05 +bmCapabilities : 0x00 +wWidth : 0x0140 (320) +wHeight : 0x00F0 (240) +dwMinBitRate : 0x01194000 (18432000 bps -> 2.304 MB/s) +dwMaxBitRate : 0x02328000 (36864000 bps -> 4.608 MB/s) +dwMaxVideoFrameBufferSize: 0x00025800 (153600 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 05 05 00 40 01 F0 00 00 40 19 01 00 80 32 &$...@....@....2 + 02 00 58 02 00 15 16 05 00 03 15 16 05 00 80 1A ..X............. + 06 00 2A 2C 0A 00 ..*,.. + + -------- VS Uncompressed Frame Type Descriptor -------- +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x05 (Uncompressed Frame Type) +bFrameIndex : 0x06 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x01E0 (480) +dwMinBitRate : 0x04650000 (73728000 bps -> 9.216 MB/s) +dwMaxBitRate : 0x08CA0000 (147456000 bps -> 18.432 MB/s) +dwMaxVideoFrameBufferSize: 0x00096000 (614400 bytes) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 05 06 00 80 02 E0 01 00 00 65 04 00 00 CA &$.........e.... + 08 00 60 09 00 15 16 05 00 03 15 16 05 00 80 1A ..`............. + 06 00 2A 2C 0A 00 ..*,.. + + ------- VS Color Matching Descriptor Descriptor ------- +bLength : 0x06 (6 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x0D (Color Matching) +bColorPrimaries : 0x01 (BT.709, sRGB) +bTransferCharacteristics : 0x01 (BT.709) +bMatrixCoefficients : 0x04 (SMPTE 170M) +Data (HexDump) : 06 24 0D 01 01 04 .$.... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x01 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 01 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0080 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x80 (128 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 81 05 80 00 01 ....... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x02 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 02 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0100 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x100 (256 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 81 05 00 01 01 ....... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x03 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 03 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0320 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x320 (800 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 81 05 20 03 01 .... .. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x04 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 04 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0B20 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x01 (1 additional transactions per microframe -> allows 513..1024 byte per packet) + Bits 10..0 : 0x320 (800 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 81 05 20 0B 01 .... .. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x05 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 05 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x1320 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x02 (2 additional transactions per microframe -> allows 683..1024 bytes per packet) + Bits 10..0 : 0x320 (800 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 81 05 20 13 01 .... .. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x01 +bAlternateSetting : 0x06 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 01 06 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x81 (Direction=IN EndpointID=1) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x1400 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x02 (2 additional transactions per microframe -> allows 683..1024 bytes per packet) + Bits 10..0 : 0x400 (1024 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 81 05 00 14 01 ....... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x00 +bNumEndpoints : 0x00 (Default Control Pipe only) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 00 00 0E 02 00 00 ......... + + ---- VC-Specific VS Video Input Header Descriptor ----- +bLength : 0x0E (14 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x01 (Input Header) +bNumFormats : 0x01 +wTotalLength : 0x0160 (352 bytes) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmInfo : 0x00 (Dynamic Format Change not supported) +bTerminalLink : 0x06 +bStillCaptureMethod : 0x00 (No Still Capture) +nbTriggerSupport : 0x00 (Hardware Triggering not supported) +bTriggerUsage : 0x00 (Host will initiate still image capture) +nbControlSize : 0x01 +Video Payload Format 1 : 0x00 + D0 : 0 no - Key Frame Rate + D1 : 0 no - P Frame Rate + D2 : 0 no - Compression Quality + D3 : 0 no - Compression Window Size + D4 : 0 no - Generate Key Frame + D5 : 0 no - Update Frame Segment + D6 : 0 no - Reserved + D7 : 0 no - Reserved +Data (HexDump) : 0E 24 01 01 60 01 82 00 06 00 00 00 01 00 .$..`......... + + ---- VS Frame Based Payload Format Type Descriptor ---- +*!*ERROR: This format is NOT ALLOWED for UVC 1.0 devices +bLength : 0x1C (28 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x10 (Frame Based Format Type) +bFormatIndex : 0x01 (1) +bNumFrameDescriptors : 0x08 (8) +guidFormat : {34363248-0000-0010-8000-00AA00389B71} (H264) +bBitsPerPixel : 0x10 (16 bits) +bDefaultFrameIndex : 0x01 (1) +bAspectRatioX : 0x00 +bAspectRatioY : 0x00 +bmInterlaceFlags : 0x00 + D0 IL stream or variable: 0 (no) + D1 Fields per frame : 0 (2 fields) + D2 Field 1 first : 0 (no) + D3 Reserved : 0 + D4..5 Field pattern : 0 (Field 1 only) + D6..7 Display Mode : 0 (Bob only) +bCopyProtect : 0x00 (No restrictions) +bVariableSize : 0x01 (Variable Size) +Data (HexDump) : 1C 24 10 01 08 48 32 36 34 00 00 10 00 80 00 00 .$...H264....... + AA 00 38 9B 71 10 01 00 00 00 00 01 ..8.q....... + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x01 +bmCapabilities : 0x00 +wWidth : 0x0780 (1920) +wHeight : 0x0438 (1080) +dwMinBitRate : 0x09E34000 (165888000 bps -> 20.736 MB/s) +dwMaxBitRate : 0x13C68000 (331776000 bps -> 41.472 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 01 00 80 07 38 04 00 40 E3 09 00 80 C6 &$.....8..@..... + 13 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x02 +bmCapabilities : 0x00 +wWidth : 0x0500 (1280) +wHeight : 0x02D0 (720) +dwMinBitRate : 0x04650000 (73728000 bps -> 9.216 MB/s) +dwMaxBitRate : 0x08CA0000 (147456000 bps -> 18.432 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 02 00 00 05 D0 02 00 00 65 04 00 00 CA &$.........e.... + 08 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x03 +bmCapabilities : 0x00 +wWidth : 0x0320 (800) +wHeight : 0x0258 (600) +dwMinBitRate : 0x0249F000 (38400000 bps -> 4.800 MB/s) +dwMaxBitRate : 0x0493E000 (76800000 bps -> 9.600 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 03 00 20 03 58 02 00 F0 49 02 00 E0 93 &$... .X...I.... + 04 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x04 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x01E0 (480) +dwMinBitRate : 0x01770000 (24576000 bps -> 3.72 MB/s) +dwMaxBitRate : 0x02EE0000 (49152000 bps -> 6.144 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 04 00 80 02 E0 01 00 00 77 01 00 00 EE &$.........w.... + 02 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x05 +bmCapabilities : 0x00 +wWidth : 0x0280 (640) +wHeight : 0x0168 (360) +dwMinBitRate : 0x01194000 (18432000 bps -> 2.304 MB/s) +dwMaxBitRate : 0x02328000 (36864000 bps -> 4.608 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 05 00 80 02 68 01 00 40 19 01 00 80 32 &$.....h..@....2 + 02 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x06 +bmCapabilities : 0x00 +wWidth : 0x0160 (352) +wHeight : 0x0120 (288) +dwMinBitRate : 0x007BC000 (8110080 bps -> 1.13 MB/s) +dwMaxBitRate : 0x00F78000 (16220160 bps -> 2.27 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 06 00 60 01 20 01 00 C0 7B 00 00 80 F7 &$...`. ...{.... + 00 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x07 +bmCapabilities : 0x00 +wWidth : 0x0140 (320) +wHeight : 0x00F0 (240) +dwMinBitRate : 0x005DC000 (6144000 bps -> 768 KB/s) +dwMaxBitRate : 0x00BB8000 (12288000 bps -> 1.536 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 07 00 40 01 F0 00 00 C0 5D 00 00 80 BB &$...@.....].... + 00 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ----- VS Frame Based Payload Frame Type Descriptor ---- +*!*ERROR bDescriptorSubtype did not exist in UVC 1.0 +bLength : 0x26 (38 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x11 (Frame Based Payload Frame Type) +bFrameIndex : 0x08 +bmCapabilities : 0x00 +wWidth : 0x0780 (1920) +wHeight : 0x0438 (1080) +dwMinBitRate : 0x09E34000 (165888000 bps -> 20.736 MB/s) +dwMaxBitRate : 0x13C68000 (331776000 bps -> 41.472 MB/s) +dwDefaultFrameInterval : 0x00051615 (33.3333 ms -> 30.000 fps) +bFrameIntervalType : 0x03 (3 discrete frame intervals supported) +dwBytesPerLine : 0x00 (0 bytes) +adwFrameInterval[1] : 0x00051615 (33.3333 ms -> 30.000 fps) +adwFrameInterval[2] : 0x00061A80 (40.0000 ms -> 25.000 fps) +adwFrameInterval[3] : 0x000A2C2A (66.6666 ms -> 15.000 fps) +Data (HexDump) : 26 24 11 08 00 80 07 38 04 00 40 E3 09 00 80 C6 &$.....8..@..... + 13 15 16 05 00 03 00 00 00 00 15 16 05 00 80 1A ................ + 06 00 2A 2C 0A 00 ..*,.. + + ------- VS Color Matching Descriptor Descriptor ------- +bLength : 0x06 (6 bytes) +bDescriptorType : 0x24 (Video Streaming Interface) +bDescriptorSubtype : 0x0D (Color Matching) +bColorPrimaries : 0x01 (BT.709, sRGB) +bTransferCharacteristics : 0x01 (BT.709) +bMatrixCoefficients : 0x04 (SMPTE 170M) +Data (HexDump) : 06 24 0D 01 01 04 .$.... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x01 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 01 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0080 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x80 (128 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 82 05 80 00 01 ....... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x02 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 02 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0100 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x100 (256 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 82 05 00 01 01 ....... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x03 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 03 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0320 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x320 (800 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 82 05 20 03 01 .... .. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x04 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 04 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0B20 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x01 (1 additional transactions per microframe -> allows 513..1024 byte per packet) + Bits 10..0 : 0x320 (800 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 82 05 20 0B 01 .... .. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x05 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 05 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x1320 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x02 (2 additional transactions per microframe -> allows 683..1024 bytes per packet) + Bits 10..0 : 0x320 (800 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 82 05 20 13 01 .... .. + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x02 +bAlternateSetting : 0x06 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x02 (Video Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 02 06 01 0E 02 00 00 ......... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x82 (Direction=IN EndpointID=2) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x1400 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x02 (2 additional transactions per microframe -> allows 683..1024 bytes per packet) + Bits 10..0 : 0x400 (1024 bytes per packet) +bInterval : 0x01 (1 ms) +Data (HexDump) : 07 05 82 05 00 14 01 ....... + + ------------------- IAD Descriptor -------------------- +bLength : 0x08 (8 bytes) +bDescriptorType : 0x0B +bFirstInterface : 0x03 +bInterfaceCount : 0x02 +bFunctionClass : 0x01 (Audio) +bFunctionSubClass : 0x00 (undefined) +bFunctionProtocol : 0x00 +iFunction : 0x00 (No String Descriptor) +Data (HexDump) : 08 0B 03 02 01 00 00 00 ........ + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x03 +bAlternateSetting : 0x00 +bNumEndpoints : 0x00 (Default Control Pipe only) +bInterfaceClass : 0x01 (Audio) +bInterfaceSubClass : 0x01 (Audio Control) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 03 00 00 01 01 00 00 ......... + + ------ Audio Control Interface Header Descriptor ------ +bLength : 0x09 (9 bytes) +bDescriptorType : 0x24 (Audio Interface Descriptor) +bDescriptorSubtype : 0x01 (Header) +bcdADC : 0x0100 +wTotalLength : 0x0029 (41 bytes) +bInCollection : 0x01 +baInterfaceNr[1] : 0x04 +Data (HexDump) : 09 24 01 00 01 29 00 01 04 .$...)... + + ------- Audio Control Input Terminal Descriptor ------- +bLength : 0x0C (12 bytes) +bDescriptorType : 0x24 (Audio Interface Descriptor) +bDescriptorSubtype : 0x02 (Input Terminal) +bTerminalID : 0x01 +wTerminalType : 0x0201 (Microphone) +bAssocTerminal : 0x00 +bNrChannels : 0x01 (1 channel) +wChannelConfig : 0x0000 (-) +iChannelNames : 0x00 (No String Descriptor) +iTerminal : 0x00 (No String Descriptor) +Data (HexDump) : 0C 24 02 01 01 02 00 01 00 00 00 00 .$.......... + + -------- Audio Control Feature Unit Descriptor -------- +bLength : 0x0B (11 bytes) +bDescriptorType : 0x24 (Audio Interface Descriptor) +bDescriptorSubtype : 0x06 (Feature Unit) +bUnitID : 0x02 (2) +bSourceID : 0x01 (1) +bControlSize : 0x02 (2 bytes per control) +bmaControls[0] : 0x01, 0x00 + D0: Mute : 1 + D1: Volume : 0 + D2: Bass : 0 + D3: Mid : 0 + D4: Treble : 0 + D5: Graphic Equalizer : 0 + D6: Automatic Gain : 0 + D7: Delay : 0 + D8: Bass Boost : 0 + D9: Loudness : 0 + D10: Reserved : 0 + D11: Reserved : 0 + D12: Reserved : 0 + D13: Reserved : 0 + D14: Reserved : 0 + D15: Reserved : 0 +bmaControls[1] : 0x02, 0x00 + D0: Mute : 0 + D1: Volume : 1 + D2: Bass : 0 + D3: Mid : 0 + D4: Treble : 0 + D5: Graphic Equalizer : 0 + D6: Automatic Gain : 0 + D7: Delay : 0 + D8: Bass Boost : 0 + D9: Loudness : 0 + D10: Reserved : 0 + D11: Reserved : 0 + D12: Reserved : 0 + D13: Reserved : 0 + D14: Reserved : 0 + D15: Reserved : 0 +iFeature : 0x00 (No String Descriptor) +Data (HexDump) : 0B 24 06 02 01 02 01 00 02 00 00 .$......... + + ------- Audio Control Output Terminal Descriptor ------ +bLength : 0x09 (9 bytes) +bDescriptorType : 0x24 (Audio Interface Descriptor) +bDescriptorSubtype : 0x03 (Output Terminal) +bTerminalID : 0x03 +wTerminalType : 0x0101 (USB streaming) +bAssocTerminal : 0x00 (0) +bSourceID : 0x02 (2) +iTerminal : 0x00 (No String Descriptor) +Data (HexDump) : 09 24 03 03 01 01 00 02 00 .$....... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x04 +bAlternateSetting : 0x00 +bNumEndpoints : 0x00 (Default Control Pipe only) +bInterfaceClass : 0x01 (Audio) +bInterfaceSubClass : 0x02 (Audio Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 04 00 00 01 02 00 00 ......... + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x04 +bAlternateSetting : 0x01 +bNumEndpoints : 0x01 (1 Endpoint) +bInterfaceClass : 0x01 (Audio) +bInterfaceSubClass : 0x02 (Audio Streaming) +bInterfaceProtocol : 0x00 +iInterface : 0x00 (No String Descriptor) +Data (HexDump) : 09 04 04 01 01 01 02 00 00 ......... + + -------- Audio Streaming Interface Descriptor --------- +bLength : 0x07 (7 bytes) +bDescriptorType : 0x24 (Audio Interface Descriptor) +bDescriptorSubtype : 0x01 +bTerminalLink : 0x03 +bDelay : 0x01 +wFormatTag : 0x0001 (PCM) +Data (HexDump) : 07 24 01 03 01 01 00 .$..... + + ------- Audio Streaming Format Type Descriptor -------- +bLength : 0x1D (29 bytes) +bDescriptorType : 0x24 (Audio Interface Descriptor) +bDescriptorSubtype : 0x02 (Format Type) +bFormatType : 0x01 (FORMAT_TYPE_I) +bNrChannels : 0x01 (1 channel) +bSubframeSize : 0x02 (2 bytes per subframe) +bBitResolution : 0x10 (16 bits per sample) +bSamFreqType : 0x07 (supports 7 sample frequencies) +tSamFreq[1] : 0x01F40 (8000 Hz) +tSamFreq[2] : 0x02B11 (11025 Hz) +tSamFreq[3] : 0x03E80 (16000 Hz) +tSamFreq[4] : 0x05622 (22050 Hz) +tSamFreq[5] : 0x05DC0 (24000 Hz) +tSamFreq[6] : 0x0AC44 (44100 Hz) +tSamFreq[7] : 0x0BB80 (48000 Hz) +Data (HexDump) : 1D 24 02 01 01 02 10 07 40 1F 00 11 2B 00 80 3E .$......@...+..> + 00 22 56 00 C0 5D 00 44 AC 00 80 BB 00 ."V..].D..... + + ----------------- Endpoint Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x05 (Endpoint Descriptor) +bEndpointAddress : 0x84 (Direction=IN EndpointID=4) +bmAttributes : 0x05 (TransferType=Isochronous SyncType=Asynchronous EndpointType=Data) +wMaxPacketSize : 0x0192 + Bits 15..13 : 0x00 (reserved, must be zero) + Bits 12..11 : 0x00 (0 additional transactions per microframe -> allows 1..1024 bytes per packet) + Bits 10..0 : 0x192 (402 bytes per packet) +bInterval : 0x04 (4 ms) +bRefresh : 0x00 +bSynchAddress : 0x00 +Data (HexDump) : 09 05 84 05 92 01 04 00 00 ......... + + ----------- Audio Data Endpoint Descriptor ------------ +bLength : 0x07 (7 bytes) +bDescriptorType : 0x25 (Audio Endpoint Descriptor) +bDescriptorSubtype : 0x01 (General) +bmAttributes : 0x01 + D0 : Sampling Freq : 0x01 (supported) + D1 : Pitch : 0x00 (not supported) + D6..2: Reserved : 0x00 + D7 : MaxPacketsOnly : 0x00 (no) +bLockDelayUnits : 0x00 (Undefined) +wLockDelay : 0x0192 +Data (HexDump) : 07 25 01 01 00 92 01 .%..... + + ----------------- Device Qualifier Descriptor ----------------- +bLength : 0x0A (10 bytes) +bDescriptorType : 0x06 (Device_qualifier Descriptor) +bcdUSB : 0x200 (USB Version 2.00) +bDeviceClass : 0xEF (Miscellaneous) +bDeviceSubClass : 0x02 +bDeviceProtocol : 0x01 (IAD - Interface Association Descriptor) +bMaxPacketSize0 : 0x40 (64 Bytes) +bNumConfigurations : 0x01 (1 other-speed configuration) +bReserved : 0x00 +Data (HexDump) : 0A 06 00 02 EF 02 01 40 01 00 .......@.. + + ------------ Other Speed Configuration Descriptor ------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x07 (Other_speed_configuration Descriptor) +wTotalLength : 0x0040 (64 bytes) +bNumInterfaces : 0x01 (1 Interface) +bConfigurationValue : 0x01 (Configuration 1) +iConfiguration : 0x00 (No String Descriptor) +bmAttributes : 0x80 + D7: Reserved, set 1 : 0x01 + D6: Self Powered : 0x00 (no) + D5: Remote Wakeup : 0x00 (no) + D4..0: Reserved, set 0 : 0x00 +MaxPower : 0xFA (500 mA) +Data (HexDump) : 09 07 40 00 01 01 00 80 FA 08 0B 00 01 0E 03 00 ..@............. + 01 09 04 00 00 00 0E 01 00 01 0C 24 01 00 01 26 ...........$...& + 00 80 8D 5B 00 00 09 24 03 05 01 01 00 02 00 11 ...[...$........ + 24 02 01 01 02 00 00 00 00 00 00 00 00 02 00 00 $............... + + ------------------- IAD Descriptor -------------------- +bLength : 0x08 (8 bytes) +bDescriptorType : 0x0B +bFirstInterface : 0x00 +bInterfaceCount : 0x01 +*!*ERROR bInterfaceCount must be greater than 1 +bFunctionClass : 0x0E (Video) +bFunctionSubClass : 0x03 (Video Interface Collection) +bFunctionProtocol : 0x00 (PC_PROTOCOL_UNDEFINED protocol) +iFunction : 0x01 (String Descriptor 1) + Language 0x0409 : "H264 USB Camera" +Data (HexDump) : 08 0B 00 01 0E 03 00 01 ........ + + ---------------- Interface Descriptor ----------------- +bLength : 0x09 (9 bytes) +bDescriptorType : 0x04 (Interface Descriptor) +bInterfaceNumber : 0x00 +bAlternateSetting : 0x00 +bNumEndpoints : 0x00 (Default Control Pipe only) +bInterfaceClass : 0x0E (Video) +bInterfaceSubClass : 0x01 (Video Control) +bInterfaceProtocol : 0x00 +iInterface : 0x01 (String Descriptor 1) + Language 0x0409 : "H264 USB Camera" +Data (HexDump) : 09 04 00 00 00 0E 01 00 01 ......... + + ------- Video Control Interface Header Descriptor ----- +bLength : 0x0C (12 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x01 (Video Control Header) +bcdUVC : 0x0100 (UVC Version 1.00) +wTotalLength : 0x0026 (38 bytes) +dwClockFreq : 0x005B8D80 (6 MHz) +bInCollection : 0x00 (0 VideoStreaming interface) +Data (HexDump) : 0C 24 01 00 01 26 00 80 8D 5B 00 00 .$...&...[.. + + ------- Video Control Output Terminal Descriptor ------ +bLength : 0x09 (9 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x03 (Output Terminal) +bTerminalID : 0x05 +wTerminalType : 0x0101 (TT_STREAMING) +bAssocTerminal : 0x00 (Not associated with an Input Terminal) +bSourceID : 0x02 +iTerminal : 0x00 +Data (HexDump) : 09 24 03 05 01 01 00 02 00 .$....... + + -------- Video Control Input Terminal Descriptor ------ +bLength : 0x11 (17 bytes) +bDescriptorType : 0x24 (Video Control Interface) +bDescriptorSubtype : 0x02 (Input Terminal) +bTerminalID : 0x01 +wTerminalType : 0x0201 (ITT_CAMERA) +bAssocTerminal : 0x00 (Not associated with an Output Terminal) +iTerminal : 0x00 +Camera Input Terminal Data: +wObjectiveFocalLengthMin : 0x0000 +wObjectiveFocalLengthMax : 0x0000 +wOcularFocalLength : 0x0000 +bControlSize : 0x02 +bmControls : 0x00, 0x00 + D0 : 0 no - Scanning Mode + D1 : 0 no - Auto-Exposure Mode + D2 : 0 no - Auto-Exposure Priority + D3 : 0 no - Exposure Time (Absolute) + D4 : 0 no - Exposure Time (Relative) + D5 : 0 no - Focus (Absolute) + D6 : 0 no - Focus (Relative) + D7 : 0 no - Iris (Absolute) + D8 : 0 no - Iris (Relative) + D9 : 0 no - Zoom (Absolute) + D10 : 0 no - Zoom (Relative) + D11 : 0 no - Pan (Absolute) + D12 : 0 no - Pan (Relative) + D13 : 0 no - Roll (Absolute) + D14 : 0 no - Roll (Relative) + D15 : 0 no - Tilt (Absolute) +Data (HexDump) : 11 24 02 01 01 02 00 00 00 00 00 00 00 00 02 00 .$.............. + 00 . + + -------------------- String Descriptors ------------------- + ------ String Descriptor 0 ------ +bLength : 0x04 (4 bytes) +bDescriptorType : 0x03 (String Descriptor) +Language ID[0] : 0x0409 (English - United States) +Data (HexDump) : 04 03 09 04 .... + ------ String Descriptor 1 ------ +bLength : 0x20 (32 bytes) +bDescriptorType : 0x03 (String Descriptor) +Language 0x0409 : "H264 USB Camera" +Data (HexDump) : 20 03 48 00 32 00 36 00 34 00 20 00 55 00 53 00 .H.2.6.4. .U.S. + 42 00 20 00 43 00 61 00 6D 00 65 00 72 00 61 00 B. .C.a.m.e.r.a. + ------ String Descriptor 2 ------ +bLength : 0x20 (32 bytes) +bDescriptorType : 0x03 (String Descriptor) +Language 0x0409 : "H264 USB Camera" +Data (HexDump) : 20 03 48 00 32 00 36 00 34 00 20 00 55 00 53 00 .H.2.6.4. .U.S. + 42 00 20 00 43 00 61 00 6D 00 65 00 72 00 61 00 B. .C.a.m.e.r.a. + ------ String Descriptor 3 ------ +bLength : 0x16 (22 bytes) +bDescriptorType : 0x03 (String Descriptor) +Language 0x0409 : "2020032801" +Data (HexDump) : 16 03 32 00 30 00 32 00 30 00 30 00 33 00 32 00 ..2.0.2.0.0.3.2. + 38 00 30 00 31 00 8.0.1. + ------ String Descriptor 5 ------ +bLength : 0x16 (22 bytes) +bDescriptorType : 0x03 (String Descriptor) +Language 0x0409 : "USB Camera" +Data (HexDump) : 16 03 55 00 53 00 42 00 20 00 43 00 61 00 6D 00 ..U.S.B. .C.a.m. + 65 00 72 00 61 00 e.r.a. diff --git a/host/class/uvc/usb_host_uvc_2/uvc_descriptors.c b/host/class/uvc/usb_host_uvc_2/uvc_descriptors.c new file mode 100644 index 00000000..2d813f8a --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/uvc_descriptors.c @@ -0,0 +1,336 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include // For guid format parsing +#include "esp_log.h" +#include "usb/usb_helpers.h" +#include "uvc_types_priv.h" +#include "uvc_descriptors_priv.h" + +static const char *TAG = "UVC-desc"; + +/** + * @brief Print UVC specific descriptor in human readable form + * + * This is a callback function that is called from USB Host library, + * when it wants to print full configuration descriptor to stdout. + * + * @param[in] _desc UVC specific descriptor + */ +static void uvc_print_desc(const usb_standard_desc_t *_desc) +{ + //@todo + ESP_LOGI(TAG, "%s NOT IMPLEMENTED YET", __func__); +} + +void uvc_host_desc_print(uvc_host_stream_hdl_t stream_hdl) +{ + assert(stream_hdl); + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + + const usb_device_desc_t *device_desc; + const usb_config_desc_t *config_desc; + ESP_ERROR_CHECK_WITHOUT_ABORT(usb_host_get_device_descriptor(uvc_stream->dev_hdl, &device_desc)); + ESP_ERROR_CHECK_WITHOUT_ABORT(usb_host_get_active_config_descriptor(uvc_stream->dev_hdl, &config_desc)); + usb_print_device_descriptor(device_desc); + usb_print_config_descriptor(config_desc, &uvc_print_desc); +} + +esp_err_t uvc_desc_get_streaming_intf_and_ep( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + const usb_intf_desc_t **intf_desc_ret, + const usb_ep_desc_t **ep_desc_ret) +{ + const usb_config_desc_t *cfg_desc; + const usb_intf_desc_t *intf_desc = NULL; + const usb_ep_desc_t *ep_desc = NULL; + int offset = 0; + usb_device_handle_t usb_dev = uvc_stream->dev_hdl; + + usb_host_get_active_config_descriptor(usb_dev, &cfg_desc); + UVC_CHECK(cfg_desc, ESP_ERR_NOT_FOUND); + const uint8_t num_of_alternate = usb_parse_interface_number_of_alternate(cfg_desc, bInterfaceNumber); + uint16_t last_mps = 0; // Looking for maximum MPS: init to zero + uint8_t last_mult = UINT8_MAX; // Looking for minimum: init to max + for (int i = 0; i < num_of_alternate + 1; i++) { + // Check Interface desc + intf_desc = usb_parse_interface_descriptor(cfg_desc, bInterfaceNumber, i, &offset); + UVC_CHECK(intf_desc, ESP_ERR_NOT_FOUND); + UVC_CHECK(intf_desc->bInterfaceClass == USB_CLASS_VIDEO, ESP_ERR_NOT_FOUND); + UVC_CHECK(intf_desc->bInterfaceSubClass == UVC_SC_VIDEOSTREAMING, ESP_ERR_NOT_FOUND); + if (intf_desc->bNumEndpoints == 0 && i == 0) { + continue; // This is Alternate setting 0 for ISOC cameras. + } + UVC_CHECK(intf_desc->bNumEndpoints == 1, ESP_ERR_NOT_FOUND); // Only 1 endpoint is expected + + // Check EP desc + ep_desc = usb_parse_endpoint_descriptor_by_index(intf_desc, 0, cfg_desc->wTotalLength, &offset); + UVC_CHECK(ep_desc, ESP_ERR_NOT_FOUND); + + // Here we look for an interface that offers the largest MPS with minimum multiple transactions in a microframe + const uint16_t current_mps = USB_EP_DESC_GET_MPS(ep_desc); + const uint8_t current_mult = USB_EP_DESC_GET_MULT(ep_desc); + if (current_mps >= last_mps && current_mult <= last_mult) { + last_mps = current_mps; + last_mult = current_mult; + *ep_desc_ret = ep_desc; + *intf_desc_ret = intf_desc; + } else { + break; + } + } + + return ESP_OK; +} + +int uvc_desc_parse_format(const uvc_format_desc_t *format_desc) +{ + assert(format_desc); + int ret = UVC_VS_FORMAT_UNDEFINED; + + switch (format_desc->bDescriptorSubType) { + case UVC_VS_DESC_SUBTYPE_FORMAT_UNCOMPRESSED: + ret = UVC_VS_FORMAT_UNCOMPRESSED; + break; + case UVC_VS_DESC_SUBTYPE_FORMAT_MJPEG: + ret = UVC_VS_FORMAT_MJPEG; + break; + case UVC_VS_DESC_SUBTYPE_FORMAT_FRAME_BASED: { + const char *guid = (const char *)(format_desc->uncompressed_frame_based.guidFormat); + // We do not check full guid, but only the first 4 characters that show human readable format + if (strncmp(guid, "H265", 4) == 0) { + ret = UVC_VS_FORMAT_H265; + } else if (strncmp(guid, "H264", 4) == 0) { + ret = UVC_VS_FORMAT_H264; + } + break; + } + case UVC_VS_DESC_SUBTYPE_UNDEFINED: + ret = UVC_VS_FORMAT_UNDEFINED; + break; + default: break; + } + return ret; +} + +static bool uvc_desc_is_format_desc(const usb_standard_desc_t *_desc) +{ + UVC_CHECK(_desc, false); + const uvc_format_desc_t *desc = (const uvc_format_desc_t *)_desc; + if ( + desc->bDescriptorType == UVC_CS_INTERFACE && + ( + desc->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_FORMAT_UNCOMPRESSED || + desc->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_FORMAT_MJPEG || + desc->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_FORMAT_MPEG2TS || + desc->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_FORMAT_DV || + desc->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_FORMAT_FRAME_BASED || + desc->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_FORMAT_STREAM_BASED + ) + ) { + return true; + } + return false; +} + +static bool uvc_desc_format_is_equal(const uvc_frame_desc_t *frame_desc, const uvc_host_stream_format_t *vs_format) +{ + UVC_CHECK(frame_desc && vs_format, false); + if (frame_desc->wWidth == vs_format->h_res && frame_desc->wHeight == vs_format->v_res) { + uint8_t bFrameIntervalType; + uint32_t dwMinFrameInterval, dwMaxFrameInterval, dwFrameIntervalStep; + const uint32_t *dwFrameInterval; + + // First parse the format specific frame descriptor + if (frame_desc->bDescriptorSubtype == UVC_VS_DESC_SUBTYPE_FRAME_FRAME_BASED) { + bFrameIntervalType = frame_desc->frame_based.bFrameIntervalType; + dwMinFrameInterval = frame_desc->frame_based.dwMinFrameInterval; + dwMaxFrameInterval = frame_desc->frame_based.dwMaxFrameInterval; + dwFrameIntervalStep = frame_desc->frame_based.dwFrameIntervalStep; + dwFrameInterval = frame_desc->frame_based.dwFrameInterval; + } else { + bFrameIntervalType = frame_desc->mjpeg_uncompressed.bFrameIntervalType; + dwMinFrameInterval = frame_desc->mjpeg_uncompressed.dwMinFrameInterval; + dwMaxFrameInterval = frame_desc->mjpeg_uncompressed.dwMaxFrameInterval; + dwFrameIntervalStep = frame_desc->mjpeg_uncompressed.dwFrameIntervalStep; + dwFrameInterval = frame_desc->mjpeg_uncompressed.dwFrameInterval; + } + + if (bFrameIntervalType == 0) { + // This stream does not support discrete Frame Interval. Check all supported intervals + uint32_t current_frame_interval = dwMinFrameInterval; + while (current_frame_interval <= dwMaxFrameInterval) { + if (vs_format->fps == UVC_DESC_DWFRAMEINTERVAL_TO_FPS(current_frame_interval)) { + return true; + } + current_frame_interval += dwFrameIntervalStep; + } + } else { + // This stream support discrete Frame Intervals. Check supported intervals + for (int i = 0; i < bFrameIntervalType; i++) { + if (vs_format->fps == UVC_DESC_DWFRAMEINTERVAL_TO_FPS(dwFrameInterval[i])) { + return true; + } + } + } + } + return false; +} + +esp_err_t uvc_desc_get_frame_format_by_format( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + const uvc_host_stream_format_t *vs_format, + const uvc_format_desc_t **format_desc_ret, + const uvc_frame_desc_t **frame_desc_ret) +{ + UVC_CHECK(uvc_stream && vs_format, ESP_ERR_INVALID_ARG); + + const uvc_vs_input_header_desc_t *input_header = uvc_desc_get_streaming_input_header(uvc_stream, bInterfaceNumber); + UVC_CHECK(input_header, ESP_ERR_NOT_FOUND); + + // Find requested Format descriptors + esp_err_t ret = ESP_ERR_NOT_FOUND; + 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)) { + const uvc_format_desc_t *this_format = (const uvc_format_desc_t *)(current_desc); + if (vs_format->format == uvc_desc_parse_format(this_format)) { + if (format_desc_ret) { + *format_desc_ret = this_format; + } + // 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))) { + uvc_frame_desc_t *this_frame = (uvc_frame_desc_t *)current_desc; + if (uvc_desc_format_is_equal(this_frame, vs_format)) { + if (frame_desc_ret) { + *frame_desc_ret = this_frame; + } + ret = ESP_OK; + break; + } + } + break; + } + } + } + return ret; +} + +bool uvc_desc_is_format_supported( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + const uvc_host_stream_format_t *vs_format) +{ + return (ESP_OK == uvc_desc_get_frame_format_by_format(uvc_stream, bInterfaceNumber, vs_format, NULL, NULL)); +} + +const uvc_vs_input_header_desc_t *uvc_desc_get_streaming_input_header(uvc_stream_t *uvc_stream, uint8_t bInterfaceNumber) +{ + UVC_CHECK(uvc_stream, NULL); + UVC_CHECK(uvc_stream->dev_hdl, NULL); + + // First get Configuration descriptor of this device + const usb_config_desc_t *cfg_desc; + usb_device_handle_t usb_dev = uvc_stream->dev_hdl; + usb_host_get_active_config_descriptor(usb_dev, &cfg_desc); + UVC_CHECK(cfg_desc, NULL); + + // Find Interface with alternate settings = 0. All Video Streaming descriptors should be after this descriptor. + int offset = 0; + const usb_intf_desc_t *intf_desc = usb_parse_interface_descriptor(cfg_desc, bInterfaceNumber, 0, &offset); + UVC_CHECK(intf_desc, NULL); + UVC_CHECK(intf_desc->bInterfaceClass == USB_CLASS_VIDEO, NULL); + UVC_CHECK(intf_desc->bInterfaceSubClass == UVC_SC_VIDEOSTREAMING, NULL); + + // Find Video Input Header Descriptor + const usb_standard_desc_t *std_desc = usb_parse_next_descriptor_of_type((const usb_standard_desc_t *)intf_desc, cfg_desc->wTotalLength, UVC_CS_INTERFACE, &offset); + const uvc_vs_input_header_desc_t *input_header = (uvc_vs_input_header_desc_t *)std_desc; + UVC_CHECK(input_header, NULL); + UVC_CHECK(input_header->bDescriptorSubType == UVC_VS_DESC_SUBTYPE_INPUT_HEADER, NULL); + return input_header; +} + +const uvc_vc_header_desc_t *uvc_desc_get_control_interface_header(uvc_stream_t *uvc_stream, unsigned uvc_idx) +{ + UVC_CHECK(uvc_stream, NULL); + UVC_CHECK(uvc_stream->dev_hdl, NULL); + + // First get Configuration descriptor of this device + const uvc_vc_header_desc_t *header_desc_ret = NULL; + const usb_config_desc_t *cfg_desc; + usb_device_handle_t usb_dev = uvc_stream->dev_hdl; + usb_host_get_active_config_descriptor(usb_dev, &cfg_desc); + UVC_CHECK(cfg_desc, NULL); + + // Find IAD UVC descriptor with desired index + int offset = 0; + int uvc_iad_idx = 0; + const usb_standard_desc_t *current_desc = (const usb_standard_desc_t *)cfg_desc; + while ((current_desc = usb_parse_next_descriptor_of_type(current_desc, cfg_desc->wTotalLength, USB_B_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, &offset))) { + const usb_iad_desc_t *iad_desc = (usb_iad_desc_t *)current_desc; + if (iad_desc->bFunctionClass == USB_CLASS_VIDEO && iad_desc->bFunctionSubClass == UVC_SC_VIDEO_INTERFACE_COLLECTION) { + if (uvc_idx == uvc_iad_idx) { + // This is the IAD that we are looking for. Find its first Video Control interface header descriptor + header_desc_ret = (const uvc_vc_header_desc_t *)usb_parse_next_descriptor_of_type(current_desc, cfg_desc->wTotalLength, UVC_CS_INTERFACE, &offset); + UVC_CHECK(header_desc_ret->bDescriptorSubType == UVC_VC_DESC_SUBTYPE_HEADER, NULL); + break; + } else { + // The user requires next UVC function + uvc_iad_idx++; + } + } + } + return header_desc_ret; +} + +esp_err_t uvc_desc_get_frame_format_by_index( + uvc_stream_t *uvc_stream, + uint8_t bInterfaceNumber, + uint8_t bFormatIndex, + uint8_t bFrameIndex, + const uvc_format_desc_t **format_desc_ret, + const uvc_frame_desc_t **frame_desc_ret) +{ + ESP_LOGV(TAG, "Looking for format %d frame %d", bFormatIndex, bFrameIndex); + UVC_CHECK(bFormatIndex > 0, ESP_ERR_INVALID_ARG); // Formats are indexed from 1 + UVC_CHECK(bFrameIndex > 0, ESP_ERR_INVALID_ARG); // Frames are indexed from 1 + UVC_CHECK(format_desc_ret && frame_desc_ret, ESP_ERR_INVALID_ARG); + + const uvc_vs_input_header_desc_t *input_header = uvc_desc_get_streaming_input_header(uvc_stream, bInterfaceNumber); + UVC_CHECK(input_header, ESP_ERR_NOT_FOUND); + UVC_CHECK(input_header->bNumFormats >= bFormatIndex, ESP_ERR_NOT_FOUND); + + // Find requested Format and Frame descriptors + esp_err_t ret = ESP_ERR_NOT_FOUND; + 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))) { + const uvc_format_desc_t *this_format = (const uvc_format_desc_t *)current_desc; + if (uvc_desc_is_format_desc(current_desc)) { + if (this_format->bFormatIndex == bFormatIndex) { + UVC_CHECK(this_format->bNumFrameDescriptors >= bFrameIndex, ESP_ERR_NOT_FOUND); + *format_desc_ret = (const uvc_format_desc_t *)this_format; + // 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))) { + uvc_frame_desc_t *this_frame = (uvc_frame_desc_t *)current_desc; + if (this_frame->bFrameIndex == bFrameIndex) { + *frame_desc_ret = this_frame; + ret = ESP_OK; + break; + } + } + break; + } + } + } + return ret; +} diff --git a/host/class/uvc/usb_host_uvc_2/uvc_frame.c b/host/class/uvc/usb_host_uvc_2/uvc_frame.c new file mode 100644 index 00000000..e89f6f15 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/uvc_frame.c @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include // For memcpy + +#include "esp_check.h" +#include "usb/uvc_host.h" +#include "uvc_frame_priv.h" +#include "uvc_types_priv.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" + +static const char *TAG = "UVC-frame"; + +esp_err_t uvc_host_frame_return(uvc_host_stream_hdl_t stream_hdl, uvc_frame_t *frame) +{ + UVC_CHECK(stream_hdl && frame, ESP_ERR_INVALID_ARG); + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + uvc_frame_reset(frame); + BaseType_t result = xQueueSend(uvc_stream->empty_fb_queue, &frame, 0); + UVC_CHECK(pdPASS == result, ESP_FAIL); + return ESP_OK; +} + +esp_err_t uvc_frame_allocate(uvc_stream_t *uvc_stream, int nb_of_fb, size_t fb_size) +{ + UVC_CHECK(uvc_stream, ESP_ERR_INVALID_ARG); + esp_err_t ret; + + // We will be passing the frame buffers by reference + uvc_stream->empty_fb_queue = xQueueCreate(nb_of_fb, sizeof(uvc_frame_t *)); + UVC_CHECK(uvc_stream->empty_fb_queue, ESP_ERR_NO_MEM); + for (int i = 0; i < nb_of_fb; i++) { + // Allocate the frame buffer + uvc_frame_t *this_fb = malloc(sizeof(uvc_frame_t)); + ESP_GOTO_ON_FALSE(this_fb, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for frame buffers %d", fb_size); + uint8_t *this_data = malloc(fb_size); + if (!this_data) { + free(this_fb); + ret = ESP_ERR_NO_MEM; + ESP_LOGE(TAG, "Not enough memory for frame buffers %d", fb_size); + goto err; + } + + // Set members to default + this_fb->data = this_data; + this_fb->data_buffer_len = fb_size; + this_fb->data_len = 0; + + // Add the frame to Queue of empty frames + const BaseType_t result = xQueueSend(uvc_stream->empty_fb_queue, &this_fb, 0); + assert(pdPASS == result); + } + return ESP_OK; + +err: + uvc_frame_free(uvc_stream); + return ret; +} + +void uvc_frame_free(uvc_stream_t *uvc_stream) +{ + if (!uvc_stream || !uvc_stream->empty_fb_queue) { + return; + } + + // Free all Frame Buffers and the Queue itself + uvc_frame_t *this_fb; + while (xQueueReceive(uvc_stream->empty_fb_queue, &this_fb, 0) == pdPASS) { + free(this_fb->data); + free(this_fb); + } + vQueueDelete(uvc_stream->empty_fb_queue); + uvc_stream->empty_fb_queue = NULL; +} + +bool uvc_frame_are_all_returned(uvc_stream_t *uvc_stream) +{ + UVC_CHECK(uvc_stream, false); + UVC_CHECK(uvc_stream->empty_fb_queue, false); + + // In case the user returns 'false' from uvc_host_frame_callback_t, he must return the frame buffers with uvc_host_frame_return() + // Here we check whether all allocated frame buffers are in the 'empty_fb_queue' + const UBaseType_t uxSpacesAvailable = uxQueueSpacesAvailable(uvc_stream->empty_fb_queue); + return (uxSpacesAvailable == 0); +} + +uvc_frame_t *uvc_frame_get_empty(uvc_stream_t *uvc_stream) +{ + UVC_CHECK(uvc_stream, NULL); + uvc_frame_t *this_fb; + if (xQueueReceive(uvc_stream->empty_fb_queue, &this_fb, 0) == pdPASS) { + return this_fb; + } else { + return NULL; + } +} + +esp_err_t uvc_frame_add_data(uvc_frame_t *frame, const uint8_t *data, size_t data_len) +{ + UVC_CHECK(frame && data, ESP_ERR_INVALID_ARG); + UVC_CHECK(frame->data_len + data_len <= frame->data_buffer_len, ESP_ERR_INVALID_SIZE); + + memcpy(frame->data + frame->data_len, data, data_len); + frame->data_len += data_len; + return ESP_OK; +} + +void uvc_frame_reset(uvc_frame_t *frame) +{ + assert(frame); + frame->data_len = 0; +} diff --git a/host/class/uvc/usb_host_uvc_2/uvc_host.c b/host/class/uvc/usb_host_uvc_2/uvc_host.c new file mode 100644 index 00000000..423f2e62 --- /dev/null +++ b/host/class/uvc/usb_host_uvc_2/uvc_host.c @@ -0,0 +1,1074 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "esp_log.h" +#include "esp_check.h" +#include "esp_system.h" + +#include "usb/usb_host.h" +#include "usb/uvc_host.h" +#include "uvc_control.h" +#include "uvc_types_priv.h" +#include "uvc_frame_priv.h" +#include "uvc_descriptors_priv.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/queue.h" +#include "freertos/event_groups.h" + +static const char *TAG = "uvc"; + +// UVC spinlock +static portMUX_TYPE uvc_lock = portMUX_INITIALIZER_UNLOCKED; +#define UVC_ENTER_CRITICAL() portENTER_CRITICAL(&uvc_lock) +#define UVC_EXIT_CRITICAL() portEXIT_CRITICAL(&uvc_lock) + +// UVC events +#define UVC_TEARDOWN BIT0 +#define UVC_TEARDOWN_COMPLETE BIT1 + +// Transfer callbacks +static void ctrl_xfer_cb(usb_transfer_t *transfer); +static void in_xfer_cb(usb_transfer_t *transfer); + +// UVC driver object +typedef struct { + usb_host_client_handle_t usb_client_hdl; /*!< USB Host handle reused for all UVC devices in the system */ + SemaphoreHandle_t open_close_mutex; /*!< Protects list of opened devices from concurrent access */ + EventGroupHandle_t driver_status; /*!< Holds status of the driver */ + usb_transfer_t *ctrl_transfer; /*!< CTRL (endpoint 0) transfer */ + SemaphoreHandle_t ctrl_mutex; /*!< CTRL mutex */ + SLIST_HEAD(list_dev, uvc_host_stream_s) uvc_stream_list; /*!< List of open streams */ +} uvc_obj_t; + +static uvc_obj_t *p_uvc_obj = NULL; + +/** + * @brief USB Host Client event callback + * + * Handling of USB device connection/disconnection to/from USB device tree. + * + * @param[in] event_msg Event message type + * @param[in] arg Caller's argument (not used in this driver) + */ +static void usb_event_cb(const usb_host_client_event_msg_t *event_msg, void *arg) +{ + switch (event_msg->event) { + case USB_HOST_CLIENT_EVENT_NEW_DEV: + ESP_LOGD(TAG, "New device connected"); + break; + case USB_HOST_CLIENT_EVENT_DEV_GONE: { + ESP_LOGD(TAG, "Device suddenly disconnected"); + // Find UVC pseudo-devices associated with this USB device and close them + uvc_stream_t *uvc_stream; + uvc_stream_t *tusb_stream; + // We are using 'SAFE' version of 'SLIST_FOREACH' which enables user to close the disconnected device in the callback + SLIST_FOREACH_SAFE(uvc_stream, &p_uvc_obj->uvc_stream_list, list_entry, tusb_stream) { + if (uvc_stream->dev_hdl == event_msg->dev_gone.dev_hdl && uvc_stream->stream_cb) { + // The suddenly disconnected device was opened by this driver: inform user about this + const uvc_host_stream_event_data_t disconn_event = { + .type = UVC_HOST_DEVICE_DISCONNECTED, + .data.stream_hdl = (uvc_host_stream_hdl_t) uvc_stream, + }; + uvc_stream->stream_cb(&disconn_event, uvc_stream->cb_arg); + } + } + break; + } + default: + assert(false); + break; + } +} + +//@todo revise this section according to MSC and HID drivers +/** + * @brief UVC driver handling task + * + * USB host client registration and deregistration is handled here. + * + * @param[in] arg User's argument. Handle of a task that started this task. + */ +static void uvc_client_task(void *arg) +{ + vTaskSuspend(NULL); // Task will be resumed from uvc_host_install() + uvc_obj_t *uvc_obj = p_uvc_obj; // Make local copy of the driver's handle + assert(uvc_obj->usb_client_hdl); + + // Start handling client's events + while (1) { + usb_host_client_handle_events(uvc_obj->usb_client_hdl, portMAX_DELAY); + EventBits_t events = xEventGroupGetBits(uvc_obj->driver_status); + if (events & UVC_TEARDOWN) { + break; + } + } + + ESP_LOGD(TAG, "Deregistering client"); + ESP_ERROR_CHECK(usb_host_client_deregister(uvc_obj->usb_client_hdl)); + xEventGroupSetBits(uvc_obj->driver_status, UVC_TEARDOWN_COMPLETE); + vTaskDelete(NULL); +} + +/** + * @brief Free USB transfers used by this device + * + * @note There can be no transfers in flight, at the moment of calling this function. + * @param[in] uvc_stream Pointer to UVC stream + */ +static void uvc_transfers_free(uvc_stream_t *uvc_stream) +{ + for (unsigned i = 0; i < uvc_stream->num_of_xfers; i++) { + usb_host_transfer_free(uvc_stream->xfers[i]); + } + free(uvc_stream->xfers); +} + +/** + * @brief Allocate UVC transfers + * + * @param[in] uvc_stream Pointer to UVC stream + * @param[in] num_of_transfers Number of USB transfers allocated for this stream + * @param[in] transfer_size Size of 1 USB transfer + * @param[in] ep_desc Descriptor of the streaming endpoint + * @return + * - ESP_OK: Success + * - ESP_ERR_NO_MEM: Not enough memory for transfers and semaphores allocation + * - ESP_ERR_INVALID_ARG: Transfer size or Max packet size are invalid + */ +static esp_err_t uvc_transfers_allocate(uvc_stream_t *uvc_stream, unsigned num_of_transfers, size_t transfer_size, const usb_ep_desc_t *ep_desc) +{ + UVC_CHECK(ep_desc, ESP_ERR_INVALID_ARG); + esp_err_t ret = ESP_OK; + uint16_t max_packet_size = USB_EP_DESC_GET_MPS(ep_desc); + max_packet_size *= (USB_EP_DESC_GET_MULT(ep_desc) + 1); // We multiply MPS by number of transactions in microframe + + UVC_CHECK(max_packet_size <= transfer_size, ESP_ERR_INVALID_ARG); + UVC_CHECK(max_packet_size > 0, ESP_ERR_INVALID_ARG); + + // Allocate array of transfers + uvc_stream->num_of_xfers = num_of_transfers; + uvc_stream->xfers = malloc(num_of_transfers * sizeof(usb_transfer_t *)); + UVC_CHECK(uvc_stream->xfers, ESP_ERR_NO_MEM); + + bool is_bulk = false; + unsigned num_isoc_packets = 0; + if (ep_desc->bmAttributes == 0x02) { + /*!< For bulk transfers, set num_isoc_packets to 0. */ + is_bulk = true; + } else { + num_isoc_packets = transfer_size / max_packet_size; + } + + // Divide the transfer data buffer into ISOC packets + ESP_LOGD(TAG, "Allocating %d USB ISOC transfers, each has %d ISOC packets per %d bytes.", num_of_transfers, num_isoc_packets, max_packet_size); + + for (unsigned i = 0; i < num_of_transfers; i++) { + ESP_GOTO_ON_ERROR( + usb_host_transfer_alloc(transfer_size, num_isoc_packets, &uvc_stream->xfers[i]), + err, TAG, "Could not allocate USB transfers"); + usb_transfer_t *this_transfer = uvc_stream->xfers[i]; + this_transfer->device_handle = uvc_stream->dev_hdl; + this_transfer->callback = in_xfer_cb; + this_transfer->context = uvc_stream; + this_transfer->timeout_ms = 1000; + if (!is_bulk) { + this_transfer->num_bytes = num_isoc_packets * max_packet_size; + } else { + this_transfer->num_bytes = transfer_size; + } + this_transfer->bEndpointAddress = ep_desc->bEndpointAddress; + if (!is_bulk) { + for (unsigned j = 0; j < num_isoc_packets; j++) { + this_transfer->isoc_packet_desc[j].num_bytes = max_packet_size; + } + } + } + return ESP_OK; + +err: + uvc_transfers_free(uvc_stream); + return ret; +} + +/** + * @brief Helper function that releases resources claimed by UVC device + * + * Close underlying USB device, free device driver memory + * + * @note All interfaces claimed by this device must be release before calling this function + * @param uvc_stream UVC stream handle to be removed + */ +static void uvc_device_remove(uvc_stream_t *uvc_stream) +{ + assert(uvc_stream); + uvc_transfers_free(uvc_stream); + uvc_frame_free(uvc_stream); + // We don't check the error code of usb_host_device_close, as the close might fail, if someone else is still using the device (not all interfaces are released) + usb_host_device_close(p_uvc_obj->usb_client_hdl, uvc_stream->dev_hdl); // Gracefully continue on error + free(uvc_stream); +} + +/** + * @brief Open USB device with requested VID/PID + * + * This function has two regular return paths: + * 1. USB device with matching VID/PID is already opened by this driver: allocate new UVC device on top of the already opened USB device. + * 2. USB device with matching VID/PID is NOT opened by this driver yet: poll USB connected devices until it is found. + * + * @note This function will block for timeout_ms, if the device is not enumerated at the moment of calling this function. + * @param[in] vid Vendor ID + * @param[in] pid Product ID + * @param[in] timeout Connection timeout in FreeRTOS ticks + * @param[out] dev UVC device + * @return esp_err_t + */ +static esp_err_t uvc_find_and_open_usb_device(uint16_t vid, uint16_t pid, TickType_t timeout, uvc_stream_t **dev) +{ + assert(p_uvc_obj); + assert(dev); + + *dev = calloc(1, sizeof(uvc_stream_t)); + if (*dev == NULL) { + return ESP_ERR_NO_MEM; + } + + // First, check list of already opened UVC devices + ESP_LOGD(TAG, "Checking list of opened USB devices"); + uvc_stream_t *uvc_stream; + SLIST_FOREACH(uvc_stream, &p_uvc_obj->uvc_stream_list, list_entry) { + const usb_device_desc_t *device_desc; + ESP_ERROR_CHECK(usb_host_get_device_descriptor(uvc_stream->dev_hdl, &device_desc)); + if (device_desc->idVendor == vid && device_desc->idProduct == pid) { + // Return path 1: + (*dev)->dev_hdl = uvc_stream->dev_hdl; + return ESP_OK; + } + } + + // Second, poll connected devices until new device is connected or timeout + TickType_t timeout_ticks = timeout; + TimeOut_t connection_timeout; + vTaskSetTimeOutState(&connection_timeout); + + ESP_LOGD(TAG, "Checking list of connected USB devices"); + do { + uint8_t dev_addr_list[10]; + int num_of_devices; + ESP_ERROR_CHECK(usb_host_device_addr_list_fill(sizeof(dev_addr_list), dev_addr_list, &num_of_devices)); + + // Go through device address list and find the one we are looking for + for (int i = 0; i < num_of_devices; i++) { + usb_device_handle_t current_device; + // Open USB device + if (usb_host_device_open(p_uvc_obj->usb_client_hdl, dev_addr_list[i], ¤t_device) != ESP_OK) { + continue; // In case we failed to open this device, continue with next one in the list + } + assert(current_device); + const usb_device_desc_t *device_desc; + ESP_ERROR_CHECK(usb_host_get_device_descriptor(current_device, &device_desc)); + if (device_desc->idVendor == vid && device_desc->idProduct == pid) { + // Return path 2: + (*dev)->dev_hdl = current_device; + return ESP_OK; + } + usb_host_device_close(p_uvc_obj->usb_client_hdl, current_device); + } + vTaskDelay(pdMS_TO_TICKS(50)); + } while (xTaskCheckForTimeOut(&connection_timeout, &timeout_ticks) == pdFALSE); + + // Timeout was reached, clean-up + free(*dev); + *dev = NULL; + return ESP_ERR_NOT_FOUND; +} + +static esp_err_t uvc_find_streaming_intf(uvc_stream_t *uvc_stream, uint8_t uvc_index, const uvc_host_stream_format_t *vs_format) +{ + UVC_CHECK(uvc_stream && vs_format, ESP_ERR_INVALID_ARG); + + // Find UVC USB function with desired index + const uvc_vc_header_desc_t *vc_header_desc = uvc_desc_get_control_interface_header(uvc_stream, uvc_index); + ESP_RETURN_ON_FALSE(vc_header_desc, ESP_ERR_NOT_FOUND, TAG, "Could not find UVC function with index %d", uvc_index); + uvc_stream->bcdUVC = vc_header_desc->bcdUVC; + + // Find video streaming interface that offers the requested format + bool format_found = false; + for (int streaming_if = 0; streaming_if < vc_header_desc->bInCollection; streaming_if++) { + uint8_t current_bInterfaceNumber = vc_header_desc->baInterfaceNr[streaming_if]; + if (uvc_desc_is_format_supported(uvc_stream, current_bInterfaceNumber, vs_format)) { + uvc_stream->bInterfaceNumber = current_bInterfaceNumber; + format_found = true; + break; + } + } + ESP_RETURN_ON_FALSE(format_found, ESP_ERR_NOT_FOUND, TAG, "Could not find frame format %dx%d@%dFPS", vs_format->h_res, vs_format->v_res, vs_format->fps); + return ESP_OK; +} + +/** + * @brief + * + * @param[in] uvc_stream UVC stream handle + * @param[out] ep_desc_ret Pointer of associated streaming endpoint + * @return esp_err_t + */ +static esp_err_t uvc_claim_interface(uvc_stream_t *uvc_stream, const usb_ep_desc_t **ep_desc_ret) +{ + UVC_CHECK(p_uvc_obj && uvc_stream, ESP_ERR_INVALID_STATE); + + const usb_intf_desc_t *intf_desc; + const usb_ep_desc_t *ep_desc; + ESP_RETURN_ON_ERROR( + uvc_desc_get_streaming_intf_and_ep(uvc_stream, uvc_stream->bInterfaceNumber, &intf_desc, &ep_desc), + TAG, "Could not find Streaming interface %d", uvc_stream->bInterfaceNumber); + + // Save all required parameters + uvc_stream->bAlternateSetting = intf_desc->bAlternateSetting; + *ep_desc_ret = ep_desc; + + return usb_host_interface_claim(p_uvc_obj->usb_client_hdl, uvc_stream->dev_hdl, intf_desc->bInterfaceNumber, intf_desc->bAlternateSetting); +} + +esp_err_t uvc_host_install(const uvc_host_driver_config_t *driver_config) +{ + UVC_CHECK(!p_uvc_obj, ESP_ERR_INVALID_STATE); + UVC_CHECK(driver_config, ESP_ERR_INVALID_ARG); + + // Allocate all we need for this driver + esp_err_t ret; + uvc_obj_t *uvc_obj = heap_caps_calloc(1, sizeof(uvc_obj_t), MALLOC_CAP_DEFAULT); + EventGroupHandle_t driver_status = xEventGroupCreate(); + SemaphoreHandle_t mutex = xSemaphoreCreateMutex(); + SemaphoreHandle_t ctrl_mutex = xSemaphoreCreateMutex(); + SemaphoreHandle_t ctrl_sem = xSemaphoreCreateBinary(); + usb_transfer_t *ctrl_xfer = NULL; + usb_host_transfer_alloc(64, 0, &ctrl_xfer); // Worst case HS MPS + TaskHandle_t driver_task_h = NULL; + xTaskCreatePinnedToCore( + uvc_client_task, "USB-UVC", driver_config->driver_task_stack_size, NULL, + driver_config->driver_task_priority, &driver_task_h, driver_config->xCoreID); + + if (uvc_obj == NULL || driver_task_h == NULL || driver_status == NULL || + mutex == NULL || ctrl_mutex == NULL || ctrl_xfer == NULL || ctrl_sem == NULL) { + ret = ESP_ERR_NO_MEM; + goto err; + } + + // Register USB Host client + usb_host_client_handle_t usb_client = NULL; + const usb_host_client_config_t client_config = { + .is_synchronous = false, + .max_num_event_msg = 3, + .async.client_event_callback = usb_event_cb, + .async.callback_arg = NULL + }; + ESP_GOTO_ON_ERROR(usb_host_client_register(&client_config, &usb_client), err, TAG, "Failed to register USB host client"); + + // Initialize UVC driver structure + SLIST_INIT(&(uvc_obj->uvc_stream_list)); + uvc_obj->driver_status = driver_status; + uvc_obj->open_close_mutex = mutex; + uvc_obj->usb_client_hdl = usb_client; + uvc_obj->ctrl_mutex = ctrl_mutex; + uvc_obj->ctrl_transfer = ctrl_xfer; + uvc_obj->ctrl_transfer->context = ctrl_sem; + uvc_obj->ctrl_transfer->bEndpointAddress = 0; + uvc_obj->ctrl_transfer->timeout_ms = 5000; + uvc_obj->ctrl_transfer->callback = ctrl_xfer_cb; + + // Between 1st call of this function and following section, another task might try to install this driver: + // Make sure that there is only one instance of this driver in the system + UVC_ENTER_CRITICAL(); + if (p_uvc_obj) { + // Already created + ret = ESP_ERR_INVALID_STATE; + UVC_EXIT_CRITICAL(); + goto client_err; + } else { + p_uvc_obj = uvc_obj; + } + UVC_EXIT_CRITICAL(); + + // Everything OK: Start UVC-Driver task and return + vTaskResume(driver_task_h); + return ESP_OK; + +client_err: + usb_host_client_deregister(usb_client); +err: // Clean-up + free(uvc_obj); + if (driver_status) { + vEventGroupDelete(driver_status); + } + if (driver_task_h) { + vTaskDelete(driver_task_h); + } + if (mutex) { + vSemaphoreDelete(mutex); + } + if (ctrl_mutex) { + vSemaphoreDelete(ctrl_mutex); + } + if (ctrl_xfer) { + usb_host_transfer_free(ctrl_xfer); + } + if (ctrl_sem) { + vSemaphoreDelete(ctrl_sem); + } + return ret; +} + +esp_err_t uvc_host_uninstall() +{ + esp_err_t ret; + + UVC_ENTER_CRITICAL(); + UVC_CHECK_FROM_CRIT(p_uvc_obj, ESP_ERR_INVALID_STATE); + uvc_obj_t *uvc_obj = p_uvc_obj; // Save Driver's handle to temporary handle + UVC_EXIT_CRITICAL(); + + xSemaphoreTake(p_uvc_obj->open_close_mutex, portMAX_DELAY); // Wait for all open/close calls to finish + xSemaphoreTake(p_uvc_obj->ctrl_mutex, portMAX_DELAY); // Wait for all CTRL transfers to finish + + UVC_ENTER_CRITICAL(); + if (SLIST_EMPTY(&p_uvc_obj->uvc_stream_list)) { // Check that device list is empty (all devices closed) + p_uvc_obj = NULL; // NULL static driver pointer: No open/close calls form this point + } else { + ret = ESP_ERR_INVALID_STATE; + UVC_EXIT_CRITICAL(); + goto unblock; + } + UVC_EXIT_CRITICAL(); + + // Signal to UVC task to stop, unblock it and wait for its deletion + xEventGroupSetBits(uvc_obj->driver_status, UVC_TEARDOWN); + usb_host_client_unblock(uvc_obj->usb_client_hdl); + ESP_GOTO_ON_FALSE( + xEventGroupWaitBits(uvc_obj->driver_status, UVC_TEARDOWN_COMPLETE, pdFALSE, pdFALSE, pdMS_TO_TICKS(100)), + ESP_ERR_NOT_FINISHED, unblock, TAG,); + + // Free remaining resources and return + vEventGroupDelete(uvc_obj->driver_status); + xSemaphoreGive(uvc_obj->open_close_mutex); + vSemaphoreDelete(uvc_obj->open_close_mutex); + xSemaphoreGive(uvc_obj->ctrl_mutex); + vSemaphoreDelete(uvc_obj->ctrl_mutex); + vSemaphoreDelete(uvc_obj->ctrl_transfer->context); + usb_host_transfer_free(uvc_obj->ctrl_transfer); + free(uvc_obj); + return ESP_OK; + +unblock: + xSemaphoreGive(uvc_obj->open_close_mutex); + xSemaphoreGive(uvc_obj->ctrl_mutex); + return ret; +} + +esp_err_t uvc_host_stream_open(const uvc_host_stream_config_t *stream_config, int timeout, uvc_host_stream_hdl_t *stream_hdl_ret) +{ + esp_err_t ret; + UVC_CHECK(p_uvc_obj, ESP_ERR_INVALID_STATE); + UVC_CHECK(stream_config, ESP_ERR_INVALID_ARG); + UVC_CHECK(stream_hdl_ret, ESP_ERR_INVALID_ARG); + + xSemaphoreTake(p_uvc_obj->open_close_mutex, portMAX_DELAY); + // Find underlying USB device + uvc_stream_t *uvc_stream; + ESP_GOTO_ON_ERROR( + uvc_find_and_open_usb_device(stream_config->usb.vid, stream_config->usb.pid, timeout, &uvc_stream), + not_found, TAG, "USB device with VID: 0x%04X, PID: 0x%04X not found", stream_config->usb.vid, stream_config->usb.pid); + + // Find the streaming interface + ESP_GOTO_ON_ERROR( + uvc_find_streaming_intf(uvc_stream, stream_config->usb.uvc_function_index, &stream_config->vs_format), + err, TAG, "Could not find streaming interface"); + + // Negotiate the frame format + uvc_vs_ctrl_t vs_result; + ESP_GOTO_ON_ERROR( + uvc_host_stream_control_negotiate(uvc_stream, &stream_config->vs_format, &vs_result), + err, TAG, "Failed to negotiate requested Video Stream format"); + + // Claim Video Streaming interface + const usb_ep_desc_t *ep_desc; + ESP_GOTO_ON_ERROR( + uvc_claim_interface(uvc_stream, &ep_desc), + claim_err, TAG, "Could not claim Streaming interface"); + ESP_LOGD(TAG, "Claimed interface index %d with MPS %d", uvc_stream->bInterfaceNumber, USB_EP_DESC_GET_MPS(ep_desc)); + + // Allocate USB transfers + ESP_GOTO_ON_ERROR( + uvc_transfers_allocate(uvc_stream, stream_config->advanced.number_of_urbs, stream_config->advanced.urb_size, ep_desc), + err, TAG,); + + // Allocate Frame buffers + size_t frame_buffer_size; + if (stream_config->advanced.frame_size != 0) { + frame_buffer_size = stream_config->advanced.frame_size; // If user provided custom frame size, use it + } else { + frame_buffer_size = vs_result.dwMaxVideoFrameSize; // Use value from frame format negotiation + }; + + ESP_GOTO_ON_ERROR( + uvc_frame_allocate(uvc_stream, stream_config->advanced.number_of_frame_buffers, frame_buffer_size), + err, TAG,); + + // Save info + memcpy((uvc_host_stream_format_t *)&uvc_stream->vs_format, &stream_config->vs_format, sizeof(uvc_host_stream_format_t)); + uvc_stream->stream_cb = stream_config->event_cb; + uvc_stream->frame_cb = stream_config->frame_cb; + uvc_stream->cb_arg = stream_config->user_arg; + + // Everything OK, add the device into list + UVC_ENTER_CRITICAL(); + SLIST_INSERT_HEAD(&p_uvc_obj->uvc_stream_list, uvc_stream, list_entry); + UVC_EXIT_CRITICAL(); + *stream_hdl_ret = (uvc_host_stream_hdl_t)uvc_stream; + xSemaphoreGive(p_uvc_obj->open_close_mutex); + return ESP_OK; + +err: + usb_host_interface_release(p_uvc_obj->usb_client_hdl, uvc_stream->dev_hdl, uvc_stream->bInterfaceNumber); +claim_err: + uvc_device_remove(uvc_stream); +not_found: + xSemaphoreGive(p_uvc_obj->open_close_mutex); + *stream_hdl_ret = NULL; + return ret; +} + +esp_err_t uvc_host_stream_close(uvc_host_stream_hdl_t stream_hdl) +{ + UVC_CHECK(p_uvc_obj, ESP_ERR_INVALID_STATE); + UVC_CHECK(stream_hdl, ESP_ERR_INVALID_ARG); + + esp_err_t ret = ESP_OK; + xSemaphoreTake(p_uvc_obj->open_close_mutex, portMAX_DELAY); + + // Make sure that the device is in the devices list (that it is not already closed) + uvc_stream_t *uvc_stream; + bool device_found = false; + UVC_ENTER_CRITICAL(); + SLIST_FOREACH(uvc_stream, &p_uvc_obj->uvc_stream_list, list_entry) { + if (uvc_stream == (uvc_stream_t *)stream_hdl) { + device_found = true; + break; + } + } + + // Device was not found in the uvc_stream_list; it was already closed, return OK + if (!device_found) { + goto exit_critical; + } + + if (uvc_stream->streaming) { + ret = ESP_ERR_INVALID_STATE; + goto exit_critical; + } + uvc_stream->stream_cb = NULL; // No user callbacks from this point + uvc_stream->frame_cb = NULL; + UVC_EXIT_CRITICAL(); + + if (!uvc_frame_are_all_returned(uvc_stream)) { + ret = ESP_ERR_INVALID_STATE; + goto exit; + } + + // Release all interfaces + ESP_ERROR_CHECK(usb_host_interface_release(p_uvc_obj->usb_client_hdl, uvc_stream->dev_hdl, uvc_stream->bInterfaceNumber)); + + UVC_ENTER_CRITICAL(); + SLIST_REMOVE(&p_uvc_obj->uvc_stream_list, uvc_stream, uvc_host_stream_s, list_entry); + UVC_EXIT_CRITICAL(); + + uvc_device_remove(uvc_stream); + + UVC_ENTER_CRITICAL(); +exit_critical: + UVC_EXIT_CRITICAL(); +exit: + xSemaphoreGive(p_uvc_obj->open_close_mutex); + return ret; +} + +static esp_err_t uvc_set_interface(uvc_host_stream_hdl_t stream_hdl, bool stream_on) +{ + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + return uvc_host_usb_ctrl( + stream_hdl, + USB_BM_REQUEST_TYPE_DIR_OUT | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_INTERFACE, + USB_B_REQUEST_SET_INTERFACE, + stream_on ? uvc_stream->bAlternateSetting : 0, + uvc_stream->bInterfaceNumber, + 0, + NULL); +} + +esp_err_t uvc_host_stream_start(uvc_host_stream_hdl_t stream_hdl) +{ + UVC_CHECK(stream_hdl, ESP_ERR_INVALID_ARG); + UVC_CHECK(stream_hdl->streaming == false, ESP_ERR_INVALID_STATE); + + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + + // 1. Negotiate the frame format + // @see USB UVC specification ver 1.5, figure 4-1 + uvc_vs_ctrl_t vs_result; + ESP_RETURN_ON_ERROR( + uvc_host_stream_control_negotiate(uvc_stream, &uvc_stream->vs_format, &vs_result), + TAG, "Failed to negotiate requested Video Stream format"); + vTaskDelay(pdMS_TO_TICKS(10)); // Some cameras need delay between format Commit and SetInterface + + // 2. Send command to the camera to start streaming + ESP_RETURN_ON_ERROR( + uvc_set_interface(stream_hdl, true), + TAG, "Could not Set Interface %d-%d", uvc_stream->bInterfaceNumber, uvc_stream->bAlternateSetting); + + // 3. Unpause: Submit all URBs + // ESP_RETURN_ON_ERROR( + // uvc_host_stream_unpause(stream_hdl), + // TAG, "Could not unpause the stream"); + + return ESP_OK; +} + +esp_err_t uvc_host_stream_stop(uvc_host_stream_hdl_t stream_hdl) +{ + UVC_CHECK(stream_hdl, ESP_ERR_INVALID_ARG); + + //ESP_RETURN_ON_ERROR(uvc_host_stream_pause(stream_hdl), TAG, "Could not pause the stream"); + return uvc_set_interface(stream_hdl, false); +} + +esp_err_t uvc_host_stream_pause(uvc_host_stream_hdl_t stream_hdl) +{ + UVC_CHECK(stream_hdl, ESP_ERR_INVALID_ARG); + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + uvc_frame_t *current_frame; + + // We do not cancel the ongoing transfers here, it is not supported by USB Host Library + // By setting uvc_stream->streaming = false; no frame callbacks will be called and the transfer can gracefully finish + UVC_ENTER_CRITICAL(); + UVC_CHECK_FROM_CRIT(uvc_stream->streaming, ESP_ERR_INVALID_STATE); + uvc_stream->streaming = false; + current_frame = uvc_stream->current_frame; + uvc_stream->current_frame = NULL; + UVC_EXIT_CRITICAL(); + + if (current_frame) { + uvc_host_frame_return(uvc_stream, current_frame); + } + + //@todo this is not a clean solution + vTaskDelay(pdMS_TO_TICKS(50)); // Wait for all transfers to finish + return ESP_OK; +} + +esp_err_t uvc_host_stream_unpause(uvc_host_stream_hdl_t stream_hdl) +{ + UVC_CHECK(stream_hdl, ESP_ERR_INVALID_ARG); + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + esp_err_t ret = ESP_OK; + + UVC_ENTER_CRITICAL(); + UVC_CHECK_FROM_CRIT(!uvc_stream->streaming, ESP_ERR_INVALID_STATE); + uvc_stream->streaming = true; + UVC_EXIT_CRITICAL(); + + for (int i = 0; i < uvc_stream->num_of_xfers; i++) { + ESP_GOTO_ON_ERROR( + usb_host_transfer_submit(uvc_stream->xfers[i]), + stop_stream, TAG, "Could not submit transfer %d", i); + } + return ret; + +stop_stream: + uvc_host_stream_pause(stream_hdl); + return ret; +} + +static void _uvc_complete_transfer(uvc_stream_t *uvc_stream) +{ + bool return_frame = true; // In case streaming is stopped ATM, we must return the frame + + // Check if the user did not stop the stream in the meantime + UVC_ENTER_CRITICAL(); + uvc_frame_t *this_frame = uvc_stream->current_frame; + uvc_stream->current_frame = NULL; // Stop writing more data to this frame + const bool invoke_fb_callback = (uvc_stream->streaming && uvc_stream->frame_cb && this_frame); + uvc_host_frame_callback_t fb_callback = uvc_stream->frame_cb; + UVC_EXIT_CRITICAL(); + if (invoke_fb_callback) { + memcpy((uvc_host_stream_format_t *)&this_frame->vs_format, &uvc_stream->vs_format, sizeof(uvc_host_stream_format_t)); + return_frame = fb_callback(this_frame, uvc_stream->cb_arg); + } + if (return_frame) { + uvc_stream->hold_last_scr = uvc_stream->last_scr; + uvc_stream->hold_pts = uvc_stream->pts; + uvc_stream->hold_seq = uvc_stream->seq; + + // The user has processed the frame in his callback, return it back to empty queue + uvc_host_frame_return(uvc_stream, this_frame); + } + uvc_stream->seq++; + uvc_stream->got_bytes = 0; + uvc_stream->last_scr = 0; + uvc_stream->pts = 0; +} + +static void _uvc_process_bulk_payload(uvc_stream_t *strm, size_t req_len, uint8_t *payload, size_t payload_len) +{ +/** Converts an unaligned four-byte little-endian integer into an int32 */ +#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24)) +/** Converts an unaligned two-byte little-endian integer into an int16 */ +#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8)) + + size_t header_len = 0; + size_t variable_offset = 0; + uint8_t header_info = 0; + size_t data_len = 0; + + /*!< last packet */ + uint8_t flag_lstp = 0; + /*!< zero pachet */ + uint8_t flag_zlp = 0; + /*!< flag transfer not complete */ + uint8_t flag_rsb = 0; + + if (payload_len == req_len) { + //transfer not complete + flag_rsb = 1; + } else if (payload_len == 0) { + flag_zlp = 1; + ESP_LOGD(TAG, "payload_len == 0"); + } else if (payload_len < req_len) { + flag_lstp = 1; + } else { + flag_lstp = 1; + } + + /********************* processing header *******************/ + if (!flag_zlp) { + ESP_LOGD(TAG, "zlp=%d, lstp=%d, req_len=%d, payload_len=%d, first=0x%02x, second=0x%02x", flag_zlp, flag_lstp, req_len, payload_len, payload[0], payload_len > 1 ? payload[1] : 0); + // make sure this is a header, judge from header length and bit field + // For SCR, PTS, some vendors not set bit, but also offer 12 Bytes header. so we just check SET condition + if (payload_len >= payload[0] + && (payload[0] == 12 || (payload[0] == 2 && !(payload[1] & 0x0C)) || (payload[0] == 6 && !(payload[1] & 0x08))) + && !(payload[1] & 0x30) +// #ifdef CONFIG_UVC_CHECK_HEADER_EOH +#if 1 + /* EOH bit, when set, indicates the end of the BFH fields + * Most camera set this bit to 1 in each header, but some vendors may not set it. + */ + && (payload[1] & 0x80) +#endif + && (!strm->reassembling) + ) { + header_len = payload[0]; + data_len = payload_len - header_len; + /* checking the end-of-header */ + variable_offset = 2; + header_info = payload[1]; + + ESP_LOGD(TAG, "header=%u info=0x%02x, payload_len = %u, last_pts = %"PRIu32" , last_scr = %"PRIu32"", header_len, header_info, payload_len, strm->pts, strm->last_scr); + if (flag_rsb || flag_lstp) { + ESP_LOGD(TAG, "reassembling start ..."); + strm->reassembling = 1; + strm->current_frame = uvc_frame_get_empty(strm); + if (strm->current_frame == NULL) { + // There is no free frame buffer now, skipping this frame + strm->skip_current_frame = true; + strm->reassembling = 0; + ESP_LOGD(TAG, "no free frame buffer, skipping this frame"); + } + } + /* ERR bit defined in Stream Header*/ + if (header_info & 0x40) { + ESP_LOGD(TAG, "bad packet: error bit set"); + strm->reassembling = 0; + uvc_frame_reset(strm->current_frame); + return; + } + } else if (strm->reassembling) { + ESP_LOGD(TAG, "reassembling %u + %u", strm->current_frame->data_len, payload_len); + data_len = payload_len; + } else { + if (payload_len > 1) { +// #ifdef CONFIG_UVC_CHECK_HEADER_EOH +#if 1 + /* Give warning if EOH check enable, but camera not have*/ + if (!(payload[1] & 0x80)) { + ESP_LOGD(TAG, "bogus packet: EOH bit not set"); + } +#endif + ESP_LOGD(TAG, "bogus packet: len = %u %02x %02x...%02x %02x\n", payload_len, payload[0], payload[1], payload[payload_len - 2], payload[payload_len - 1]); + } + return; + } + } + + if (header_len >= 2) { + if (strm->fid != (header_info & 1) && strm->current_frame->data_len != 0) { + /* The frame ID bit was flipped, but we have image data sitting + around from prior transfers. This means the camera didn't send + an EOF for the last transfer of the previous frame. */ + ESP_LOGD(TAG, "SWAP NO EOF %d", strm->current_frame->data_len); + _uvc_complete_transfer(strm); + } + + strm->fid = header_info & 1; + if (header_info & (1 << 2)) { + strm->pts = DW_TO_INT(payload + variable_offset); + variable_offset += 4; + } + + if (header_info & (1 << 3)) { + strm->last_scr = DW_TO_INT(payload + variable_offset); + variable_offset += 6; + } + } + + /********************* processing data *****************/ + if (data_len >= 1) { + if (payload_len > 1) { + ESP_LOGD(TAG, "uvc payload = %02x %02x...%02x %02x\n", payload[header_len], payload[header_len + 1], payload[payload_len - 2], payload[payload_len - 1]); + } + esp_err_t ret = uvc_frame_add_data(strm->current_frame, payload + header_len, data_len); + if (ret != ESP_OK) { + ESP_LOGD(TAG, "SWAP overflow %d", strm->current_frame->data_len); + strm->skip_current_frame = true; + strm->reassembling = 0; + uvc_host_frame_return(strm, strm->current_frame); + return; + } + + } + /* Just ignore the EOF bit if using payload reassembling in bulk transfer */ + if (((header_info & (1 << 1)) ) || flag_zlp || flag_lstp) { + /* The EOF bit is set, so publish the complete frame */ + if (strm->current_frame->data_len != 0) { + _uvc_complete_transfer(strm); + } + strm->reassembling = 0; + } +} + +static void _uvc_process_isoc_payload(uvc_stream_t *strm, usb_isoc_packet_desc_t *isoc_desc, uint8_t *payload) +{ + if (isoc_desc->actual_num_bytes == 0) { + return; + } + + // Check for start of new frame + const uvc_payload_header_t *payload_header = (const uvc_payload_header_t *)payload; + const bool start_of_frame = (strm->current_frame_id != payload_header->bmHeaderInfo.frame_id); + if (start_of_frame) { + // We detected start of new frame. Update Frame ID and start fetching this frame + strm->current_frame_id = payload_header->bmHeaderInfo.frame_id; + strm->skip_current_frame = false; + + // Get free frame buffer for this new frame + UVC_ENTER_CRITICAL(); + bool need_new_frame = (strm->streaming && !strm->current_frame); + if (need_new_frame) { + UVC_EXIT_CRITICAL(); + strm->current_frame = uvc_frame_get_empty(strm); + if (strm->current_frame == NULL) { + // There is no free frame buffer now, skipping this frame + strm->skip_current_frame = true; + return; + } + } else { + // We received SoF but current_frame is not NULL: We missed EoF - reset the frame buffer + uvc_frame_reset(strm->current_frame); + UVC_EXIT_CRITICAL(); + } + } else if (strm->skip_current_frame) { + // Previous packets indicated we must skip this frame + return; + } + + // Check for empty data + if (isoc_desc->actual_num_bytes <= payload_header->bHeaderLength) { + return; + } + + // Check for error flag + if (payload_header->bmHeaderInfo.error) { + strm->skip_current_frame = true; + return; + } + + // Add received data to frame buffer + const uint8_t *payload_data = payload + payload_header->bHeaderLength; + const size_t payload_data_len = isoc_desc->actual_num_bytes - payload_header->bHeaderLength; + esp_err_t ret = uvc_frame_add_data(strm->current_frame, payload_data, payload_data_len); + if (ret != ESP_OK) { + strm->skip_current_frame = true; + return; + } + + // End of Frame. Pass the frame to user + if (payload_header->bmHeaderInfo.end_of_frame) { + bool return_frame = true; // In case streaming is stopped ATM, we must return the frame + + // Check if the user did not stop the stream in the meantime + UVC_ENTER_CRITICAL(); + uvc_frame_t *this_frame = strm->current_frame; + strm->current_frame = NULL; // Stop writing more data to this frame + const bool invoke_fb_callback = (strm->streaming && strm->frame_cb && this_frame); + uvc_host_frame_callback_t fb_callback = strm->frame_cb; + UVC_EXIT_CRITICAL(); + if (invoke_fb_callback) { + memcpy((uvc_host_stream_format_t *)&this_frame->vs_format, &strm->vs_format, sizeof(uvc_host_stream_format_t)); + return_frame = fb_callback(this_frame, strm->cb_arg); + } + if (return_frame) { + // The user has processed the frame in his callback, return it back to empty queue + uvc_host_frame_return(strm, this_frame); + } + } +} + +static void in_xfer_cb(usb_transfer_t *transfer) +{ + ESP_LOGD(TAG, "in xfer cb"); + uvc_stream_t *uvc_stream = (uvc_stream_t *)transfer->context; + + UVC_ENTER_CRITICAL(); + bool streaming_on = uvc_stream->streaming; + UVC_EXIT_CRITICAL(); + if (!streaming_on) { + return; // If the streaming was turned off, we don't have to do anything + } + + if (transfer->num_isoc_packets == 0) { + /* This is a bulk mode transfer, so it just has one payload transfer */ + switch (transfer->status) { + case USB_TRANSFER_STATUS_COMPLETED: + _uvc_process_bulk_payload(uvc_stream, transfer->data_buffer_size, transfer->data_buffer, transfer->actual_num_bytes); + break; + case USB_TRANSFER_STATUS_STALL: + case USB_TRANSFER_STATUS_TIMED_OUT: + case USB_TRANSFER_STATUS_OVERFLOW: + ESP_LOGE(TAG, "usb err %d", transfer->status); + default: + break; + } + } else { + uint8_t *payload = transfer->data_buffer; + for (int i = 0; i < transfer->num_isoc_packets; i++) { + usb_isoc_packet_desc_t *isoc_desc = &transfer->isoc_packet_desc[i]; + + // Check USB status + switch (isoc_desc->status) { + case USB_TRANSFER_STATUS_COMPLETED: + _uvc_process_isoc_payload(uvc_stream, isoc_desc, payload); + break; + case USB_TRANSFER_STATUS_NO_DEVICE: + case USB_TRANSFER_STATUS_CANCELED: + return; // No need to process the rest + case USB_TRANSFER_STATUS_ERROR: + case USB_TRANSFER_STATUS_OVERFLOW: + case USB_TRANSFER_STATUS_STALL: + ESP_LOGW(TAG, "usb err %d", isoc_desc->status); + uvc_stream->skip_current_frame = true; + break; // Data corrupted + case USB_TRANSFER_STATUS_TIMED_OUT: + case USB_TRANSFER_STATUS_SKIPPED: + break; // Skipped and timed out ISOC transfers are not an issue + default: + assert(false); + } + + payload += isoc_desc->num_bytes; + continue; + } + } + + UVC_ENTER_CRITICAL(); + streaming_on = uvc_stream->streaming; + UVC_EXIT_CRITICAL(); + if (streaming_on) { + usb_host_transfer_submit(transfer); // Restart the transfer + } +} + +static void ctrl_xfer_cb(usb_transfer_t *transfer) +{ + ESP_LOGD(TAG, "ctrl xfer cb"); + assert(transfer->context); + xSemaphoreGive((SemaphoreHandle_t)transfer->context); +} + +esp_err_t uvc_host_usb_ctrl(uvc_host_stream_hdl_t stream_hdl, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength, uint8_t *data) +{ + UVC_CHECK(stream_hdl, ESP_ERR_INVALID_ARG); + uvc_stream_t *uvc_stream = (uvc_stream_t *)stream_hdl; + if (wLength > 0) { + UVC_CHECK(data, ESP_ERR_INVALID_ARG); + } + UVC_CHECK(p_uvc_obj->ctrl_transfer->data_buffer_size >= wLength, ESP_ERR_INVALID_SIZE); + + esp_err_t ret; + + // Take Mutex and fill the CTRL request + BaseType_t taken = xSemaphoreTake(p_uvc_obj->ctrl_mutex, pdMS_TO_TICKS(5000)); + if (!taken) { + return ESP_ERR_TIMEOUT; + } + usb_setup_packet_t *req = (usb_setup_packet_t *)(p_uvc_obj->ctrl_transfer->data_buffer); + uint8_t *start_of_data = (uint8_t *)req + sizeof(usb_setup_packet_t); + req->bmRequestType = bmRequestType; + req->bRequest = bRequest; + req->wValue = wValue; + req->wIndex = wIndex; + req->wLength = wLength; + + // Bind the transfer and the device + p_uvc_obj->ctrl_transfer->device_handle = uvc_stream->dev_hdl; + p_uvc_obj->ctrl_transfer->num_bytes = wLength + sizeof(usb_setup_packet_t); + + // For IN transfers we must transfer data ownership to the driver + const bool in_transfer = bmRequestType & USB_BM_REQUEST_TYPE_DIR_IN; + if (!in_transfer) { + memcpy(start_of_data, data, wLength); + } + + ESP_GOTO_ON_ERROR( + usb_host_transfer_submit_control(p_uvc_obj->usb_client_hdl, p_uvc_obj->ctrl_transfer), + reset_ep0, TAG, "CTRL transfer failed"); + + taken = xSemaphoreTake((SemaphoreHandle_t)p_uvc_obj->ctrl_transfer->context, pdMS_TO_TICKS(5000)); // This is a fixed timeout. Every device should be able to respond to CTRL transfer in 5 seconds + ESP_GOTO_ON_FALSE(taken, ESP_ERR_TIMEOUT, reset_ep0, TAG, "CTRL timeout"); + ESP_GOTO_ON_FALSE(p_uvc_obj->ctrl_transfer->status == USB_TRANSFER_STATUS_COMPLETED, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Control transfer error"); + ESP_GOTO_ON_FALSE(p_uvc_obj->ctrl_transfer->actual_num_bytes == p_uvc_obj->ctrl_transfer->num_bytes, ESP_ERR_INVALID_RESPONSE, unblock, TAG, "Incorrect number of bytes transferred"); + + // For OUT transfers, we must transfer data ownership to user + if (in_transfer) { + memcpy(data, start_of_data, wLength); + } + ret = ESP_OK; + +reset_ep0: + // Transfer was not finished, error in USB LIB. Reset the endpoint 0 + usb_host_endpoint_halt(uvc_stream->dev_hdl, 0); + usb_host_endpoint_flush(uvc_stream->dev_hdl, 0); + usb_host_endpoint_clear(uvc_stream->dev_hdl, 0); + +unblock: + xSemaphoreGive(p_uvc_obj->ctrl_mutex); + return ret; +}