Merge pull request #2538 from kkitayam/add_uvc_2ch_example
Add an example of 2ch video capture.
This commit is contained in:
		| @@ -28,4 +28,5 @@ family_add_subdirectory(net_lwip_webserver) | ||||
| family_add_subdirectory(uac2_headset) | ||||
| family_add_subdirectory(usbtmc) | ||||
| family_add_subdirectory(video_capture) | ||||
| family_add_subdirectory(video_capture_2ch) | ||||
| family_add_subdirectory(webusb_serial) | ||||
|   | ||||
| @@ -56,8 +56,8 @@ char const* string_desc_arr[] = { | ||||
|     "TinyUSB",                     // 1: Manufacturer | ||||
|     "TinyUSB Device",              // 2: Product | ||||
|     NULL,                          // 3: Serials will use unique ID if possible | ||||
|     "TinyUSB UVC Control",         // 4: UVC Interface | ||||
|     "TinyUSB UVC Streaming",       // 5: UVC Interface | ||||
|     "UVC Control",                 // 4: UVC Interface | ||||
|     "UVC Streaming",               // 5: UVC Interface | ||||
| }; | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
|   | ||||
							
								
								
									
										39
									
								
								examples/device/video_capture_2ch/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								examples/device/video_capture_2ch/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| cmake_minimum_required(VERSION 3.17) | ||||
|  | ||||
| include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake) | ||||
|  | ||||
| # gets PROJECT name for the example (e.g. <BOARD>-<DIR_NAME>) | ||||
| family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR}) | ||||
|  | ||||
| project(${PROJECT} C CXX ASM) | ||||
|  | ||||
| # Checks this example is valid for the family and initializes the project | ||||
| family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}) | ||||
|  | ||||
| # Espressif has its own cmake build system | ||||
| if(FAMILY STREQUAL "espressif") | ||||
|   return() | ||||
| endif() | ||||
|  | ||||
| add_executable(${PROJECT}) | ||||
|  | ||||
| if (FORCE_READONLY) | ||||
| target_compile_definitions(${PROJECT} PRIVATE | ||||
|   CFG_EXAMPLE_VIDEO_READONLY | ||||
| ) | ||||
| endif() | ||||
|  | ||||
| # Example source | ||||
| target_sources(${PROJECT} PUBLIC | ||||
|   ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c | ||||
|   ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c | ||||
| ) | ||||
|  | ||||
| # Example include | ||||
| target_include_directories(${PROJECT} PUBLIC | ||||
|   ${CMAKE_CURRENT_SOURCE_DIR}/src | ||||
| ) | ||||
|  | ||||
| # Configure compilation flags and libraries for the example without RTOS. | ||||
| # See the corresponding function in hw/bsp/FAMILY/family.cmake for details. | ||||
| family_configure_device_example(${PROJECT} noos) | ||||
							
								
								
									
										18
									
								
								examples/device/video_capture_2ch/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								examples/device/video_capture_2ch/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| include ../../build_system/make/make.mk | ||||
|  | ||||
| ifeq ($(DISABLE_MJPEG),1) | ||||
| CFLAGS += -DCFG_EXAMPLE_VIDEO_DISABLE_MJPEG | ||||
| endif | ||||
| ifeq ($(FORCE_READONLY),1) | ||||
| CFLAGS += -DCFG_EXAMPLE_VIDEO_READONLY | ||||
| endif | ||||
|  | ||||
| INC += \ | ||||
| 	src \ | ||||
| 	$(TOP)/hw \ | ||||
|  | ||||
| # Example source | ||||
| EXAMPLE_SOURCE += $(wildcard src/*.c) | ||||
| SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE)) | ||||
|  | ||||
| include ../../build_system/make/rules.mk | ||||
							
								
								
									
										13
									
								
								examples/device/video_capture_2ch/skip.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								examples/device/video_capture_2ch/skip.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| mcu:MSP430x5xx | ||||
| mcu:NUC121 | ||||
| mcu:SAMD11 | ||||
| mcu:GD32VF103 | ||||
| mcu:CH32V307 | ||||
| family:espressif | ||||
| board:curiosity_nano | ||||
| board:kuiic | ||||
| board:frdm_k32l2b | ||||
| board:lpcxpresso11u68 | ||||
| board:stm32f303disco | ||||
| board:stm32l412nucleo | ||||
| board:ek_tm4c123gxl | ||||
							
								
								
									
										4
									
								
								examples/device/video_capture_2ch/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								examples/device/video_capture_2ch/src/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| # This file is for ESP-IDF only | ||||
| idf_component_register(SRCS "main.c" "usb_descriptors.c" | ||||
|                     INCLUDE_DIRS "." | ||||
|                     REQUIRES boards tinyusb_src) | ||||
							
								
								
									
										1934
									
								
								examples/device/video_capture_2ch/src/images.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1934
									
								
								examples/device/video_capture_2ch/src/images.h
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										359
									
								
								examples/device/video_capture_2ch/src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								examples/device/video_capture_2ch/src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,359 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <stdlib.h> | ||||
| #include <stdio.h> | ||||
| #include <string.h> | ||||
|  | ||||
| #include "bsp/board_api.h" | ||||
| #include "tusb.h" | ||||
| #include "usb_descriptors.h" | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // MACRO CONSTANT TYPEDEF PROTYPES | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| /* Blink pattern | ||||
|  * - 250 ms  : device not mounted | ||||
|  * - 1000 ms : device mounted | ||||
|  * - 2500 ms : device is suspended | ||||
|  */ | ||||
| enum { | ||||
|   BLINK_NOT_MOUNTED = 250, | ||||
|   BLINK_MOUNTED = 1000, | ||||
|   BLINK_SUSPENDED = 2500, | ||||
| }; | ||||
|  | ||||
| static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
|  | ||||
| void led_blinking_task(void* param); | ||||
| void usb_device_task(void *param); | ||||
| void video_task(void* param); | ||||
|  | ||||
| #if CFG_TUSB_OS == OPT_OS_FREERTOS | ||||
| void freertos_init_task(void); | ||||
| #endif | ||||
|  | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Main | ||||
| //--------------------------------------------------------------------+ | ||||
| int main(void) { | ||||
|   board_init(); | ||||
|  | ||||
|   // If using FreeRTOS: create blinky, tinyusb device, video task | ||||
| #if CFG_TUSB_OS == OPT_OS_FREERTOS | ||||
|   freertos_init_task(); | ||||
| #else | ||||
|   // init device stack on configured roothub port | ||||
|   tud_init(BOARD_TUD_RHPORT); | ||||
|  | ||||
|   if (board_init_after_tusb) { | ||||
|     board_init_after_tusb(); | ||||
|   } | ||||
|  | ||||
|   while (1) { | ||||
|     tud_task(); // tinyusb device task | ||||
|     led_blinking_task(NULL); | ||||
|     video_task(NULL); | ||||
|   } | ||||
| #endif | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Device callbacks | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // Invoked when device is mounted | ||||
| void tud_mount_cb(void) { | ||||
|   blink_interval_ms = BLINK_MOUNTED; | ||||
| } | ||||
|  | ||||
| // Invoked when device is unmounted | ||||
| void tud_umount_cb(void) { | ||||
|   blink_interval_ms = BLINK_NOT_MOUNTED; | ||||
| } | ||||
|  | ||||
| // Invoked when usb bus is suspended | ||||
| // remote_wakeup_en : if host allow us  to perform remote wakeup | ||||
| // Within 7ms, device must draw an average of current less than 2.5 mA from bus | ||||
| void tud_suspend_cb(bool remote_wakeup_en) { | ||||
|   (void) remote_wakeup_en; | ||||
|   blink_interval_ms = BLINK_SUSPENDED; | ||||
| } | ||||
|  | ||||
| // Invoked when usb bus is resumed | ||||
| void tud_resume_cb(void) { | ||||
|   blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // USB Video | ||||
| //--------------------------------------------------------------------+ | ||||
| #define FRAMEBUF_SIZE (FRAME_WIDTH * FRAME_HEIGHT * 16 / 8) | ||||
|  | ||||
| static unsigned frame_num[CFG_TUD_VIDEO_STREAMING] = {1}; | ||||
| static unsigned tx_busy = 0; | ||||
| static unsigned interval_ms[CFG_TUD_VIDEO_STREAMING] = {1000 / FRAME_RATE}; | ||||
|  | ||||
| // For mcus that does not have enough SRAM for frame buffer, we use fixed frame data. | ||||
| // To further reduce the size, we use MJPEG format instead of YUY2. | ||||
| #include "images.h" | ||||
|  | ||||
| static struct { | ||||
|   uint32_t       size; | ||||
|   uint8_t const *buffer; | ||||
| } const framebuf_mjpeg[] = { | ||||
|     {sizeof(color_bar_0_jpg), color_bar_0_jpg}, | ||||
|     {sizeof(color_bar_1_jpg), color_bar_1_jpg}, | ||||
|     {sizeof(color_bar_2_jpg), color_bar_2_jpg}, | ||||
|     {sizeof(color_bar_3_jpg), color_bar_3_jpg}, | ||||
|     {sizeof(color_bar_4_jpg), color_bar_4_jpg}, | ||||
|     {sizeof(color_bar_5_jpg), color_bar_5_jpg}, | ||||
|     {sizeof(color_bar_6_jpg), color_bar_6_jpg}, | ||||
|     {sizeof(color_bar_7_jpg), color_bar_7_jpg}, | ||||
| }; | ||||
|  | ||||
| #if !defined(CFG_EXAMPLE_VIDEO_READONLY) | ||||
| // YUY2 frame buffer | ||||
| static uint8_t framebuf_yuy2[FRAMEBUF_SIZE]; | ||||
|  | ||||
| static void fill_color_bar(uint8_t* buffer, unsigned start_position) { | ||||
|   /* EBU color bars: https://stackoverflow.com/questions/6939422 */ | ||||
|   static uint8_t const bar_color[8][4] = { | ||||
|       /*  Y,   U,   Y,   V */ | ||||
|       { 235, 128, 235, 128}, /* 100% White */ | ||||
|       { 219,  16, 219, 138}, /* Yellow */ | ||||
|       { 188, 154, 188,  16}, /* Cyan */ | ||||
|       { 173,  42, 173,  26}, /* Green */ | ||||
|       {  78, 214,  78, 230}, /* Magenta */ | ||||
|       {  63, 102,  63, 240}, /* Red */ | ||||
|       {  32, 240,  32, 118}, /* Blue */ | ||||
|       {  16, 128,  16, 128}, /* Black */ | ||||
|   }; | ||||
|   uint8_t* p; | ||||
|  | ||||
|   /* Generate the 1st line */ | ||||
|   uint8_t* end = &buffer[FRAME_WIDTH * 2]; | ||||
|   unsigned idx = (FRAME_WIDTH / 2 - 1) - (start_position % (FRAME_WIDTH / 2)); | ||||
|   p = &buffer[idx * 4]; | ||||
|   for (unsigned i = 0; i < 8; ++i) { | ||||
|     for (int j = 0; j < FRAME_WIDTH / (2 * 8); ++j) { | ||||
|       memcpy(p, &bar_color[i], 4); | ||||
|       p += 4; | ||||
|       if (end <= p) { | ||||
|         p = buffer; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /* Duplicate the 1st line to the others */ | ||||
|   p = &buffer[FRAME_WIDTH * 2]; | ||||
|   for (unsigned i = 1; i < FRAME_HEIGHT; ++i) { | ||||
|     memcpy(p, buffer, FRAME_WIDTH * 2); | ||||
|     p += FRAME_WIDTH * 2; | ||||
|   } | ||||
| } | ||||
| #endif | ||||
|  | ||||
| size_t get_framebuf(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, size_t fnum, void **fb) { | ||||
|   uint32_t idx = ctl_idx + stm_idx; | ||||
|  | ||||
|   if (idx == 0) { | ||||
|     // stream 0 use uncompressed YUY2 frame | ||||
|     #if defined(CFG_EXAMPLE_VIDEO_READONLY) | ||||
|     *fb = (void*)(uintptr_t ) framebuf_yuy2_readonly[(fnum % (FRAME_WIDTH / 2)) * 4]; | ||||
|     #else | ||||
|     fill_color_bar(framebuf_yuy2, frame_num[idx]); | ||||
|     *fb = framebuf_yuy2; | ||||
|     #endif | ||||
|  | ||||
|     return FRAMEBUF_SIZE; | ||||
|   }else { | ||||
|     // stream 1 use MJPEG frame | ||||
|     size_t const bar_id = fnum & 0x7; | ||||
|  | ||||
|     *fb = (void*)(uintptr_t) framebuf_mjpeg[bar_id].buffer; | ||||
|     return framebuf_mjpeg[bar_id].size; | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { | ||||
|   static unsigned start_ms[CFG_TUD_VIDEO_STREAMING] = {0, }; | ||||
|   static unsigned already_sent = 0; | ||||
|  | ||||
|   uint32_t idx = ctl_idx + stm_idx; | ||||
|   if (!tud_video_n_streaming(ctl_idx, stm_idx)) { | ||||
|     already_sent &= ~(1u << idx); | ||||
|     frame_num[idx] = 0; | ||||
|     return; | ||||
|   } | ||||
|   void* fp; | ||||
|   size_t fb_size; | ||||
|  | ||||
|   if (!(already_sent & (1u << idx))) { | ||||
|     already_sent |= 1u << idx; | ||||
|     tx_busy |= 1u << idx; | ||||
|     start_ms[idx] = board_millis(); | ||||
|  | ||||
|     fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp); | ||||
|     tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size); | ||||
|   } | ||||
|  | ||||
|   unsigned cur = board_millis(); | ||||
|   if (cur - start_ms[idx] < interval_ms[idx]) return; // not enough time | ||||
|   if (tx_busy & (1u << idx)) return; | ||||
|   start_ms[idx] += interval_ms[idx]; | ||||
|   tx_busy |= 1u << idx; | ||||
|  | ||||
|   fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp); | ||||
|   tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size); | ||||
| } | ||||
|  | ||||
|  | ||||
| void video_task(void* param) { | ||||
|   (void) param; | ||||
|  | ||||
|   while(1) { | ||||
|     video_send_frame(0, 0); | ||||
|     video_send_frame(1, 0); | ||||
|  | ||||
|     #if CFG_TUSB_OS == OPT_OS_FREERTOS | ||||
|     vTaskDelay(interval_ms[0] / portTICK_PERIOD_MS); | ||||
|     #else | ||||
|     return; | ||||
|     #endif | ||||
|   } | ||||
| } | ||||
|  | ||||
| void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { | ||||
|   uint32_t idx = ctl_idx + stm_idx; | ||||
|   tx_busy &= ~(1u << idx); | ||||
|   /* flip buffer */ | ||||
|   ++frame_num[idx]; | ||||
| } | ||||
|  | ||||
| int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, | ||||
|                         video_probe_and_commit_control_t const* parameters) { | ||||
|   uint32_t idx = ctl_idx + stm_idx; | ||||
|   /* convert unit to ms from 100 ns */ | ||||
|   interval_ms[idx] = parameters->dwFrameInterval / 10000; | ||||
|   return VIDEO_ERROR_NONE; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Blinking Task | ||||
| //--------------------------------------------------------------------+ | ||||
| void led_blinking_task(void* param) { | ||||
|   (void) param; | ||||
|   static uint32_t start_ms = 0; | ||||
|   static bool led_state = false; | ||||
|  | ||||
|   while (1) { | ||||
|     #if CFG_TUSB_OS == OPT_OS_FREERTOS | ||||
|     vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS); | ||||
|     #else | ||||
|     if (board_millis() - start_ms < blink_interval_ms) return; // not enough time | ||||
|     #endif | ||||
|  | ||||
|     start_ms += blink_interval_ms; | ||||
|     board_led_write(led_state); | ||||
|     led_state = 1 - led_state; // toggle | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // FreeRTOS | ||||
| //--------------------------------------------------------------------+ | ||||
| #if CFG_TUSB_OS == OPT_OS_FREERTOS | ||||
|  | ||||
| #define BLINKY_STACK_SIZE   configMINIMAL_STACK_SIZE | ||||
| #define VIDEO_STACK_SIZE    (configMINIMAL_STACK_SIZE*4) | ||||
|  | ||||
| #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) | ||||
|   #define USBD_STACK_SIZE     4096 | ||||
|   int main(void); | ||||
|   void app_main(void) { | ||||
|     main(); | ||||
|   } | ||||
| #else | ||||
|   // Increase stack size when debug log is enabled | ||||
|   #define USBD_STACK_SIZE    (3*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1) | ||||
| #endif | ||||
|  | ||||
| // static task | ||||
| #if configSUPPORT_STATIC_ALLOCATION | ||||
| StackType_t blinky_stack[BLINKY_STACK_SIZE]; | ||||
| StaticTask_t blinky_taskdef; | ||||
|  | ||||
| StackType_t  usb_device_stack[USBD_STACK_SIZE]; | ||||
| StaticTask_t usb_device_taskdef; | ||||
|  | ||||
| StackType_t  video_stack[VIDEO_STACK_SIZE]; | ||||
| StaticTask_t video_taskdef; | ||||
| #endif | ||||
|  | ||||
| // USB Device Driver task | ||||
| // This top level thread process all usb events and invoke callbacks | ||||
| void usb_device_task(void *param) { | ||||
|   (void) param; | ||||
|  | ||||
|   // init device stack on configured roothub port | ||||
|   // This should be called after scheduler/kernel is started. | ||||
|   // Otherwise, it could cause kernel issue since USB IRQ handler does use RTOS queue API. | ||||
|   tud_init(BOARD_TUD_RHPORT); | ||||
|  | ||||
|   if (board_init_after_tusb) { | ||||
|     board_init_after_tusb(); | ||||
|   } | ||||
|  | ||||
|   // RTOS forever loop | ||||
|   while (1) { | ||||
|     // put this thread to waiting state until there is new events | ||||
|     tud_task(); | ||||
|   } | ||||
| } | ||||
|  | ||||
| void freertos_init_task(void) { | ||||
|   #if configSUPPORT_STATIC_ALLOCATION | ||||
|   xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef); | ||||
|   xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef); | ||||
|   xTaskCreateStatic(video_task, "cdc", VIDEO_STACK_SIZE, NULL, configMAX_PRIORITIES - 2, video_stack, &video_taskdef); | ||||
|   #else | ||||
|   xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL); | ||||
|   xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL); | ||||
|   xTaskCreate(video_task, "video", VIDEO_STACK_SZIE, NULL, configMAX_PRIORITIES - 2, NULL); | ||||
|   #endif | ||||
|  | ||||
|   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3 | ||||
|   #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) | ||||
|   vTaskStartScheduler(); | ||||
|   #endif | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										120
									
								
								examples/device/video_capture_2ch/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								examples/device/video_capture_2ch/src/tusb_config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _TUSB_CONFIG_H_ | ||||
| #define _TUSB_CONFIG_H_ | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|  extern "C" { | ||||
| #endif | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Board Specific Configuration | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| // RHPort number used for device can be defined by board.mk, default to port 0 | ||||
| #ifndef BOARD_TUD_RHPORT | ||||
| #define BOARD_TUD_RHPORT      0 | ||||
| #endif | ||||
|  | ||||
| // RHPort max operational speed can defined by board.mk | ||||
| #ifndef BOARD_TUD_MAX_SPEED | ||||
| #define BOARD_TUD_MAX_SPEED   OPT_MODE_DEFAULT_SPEED | ||||
| #endif | ||||
|  | ||||
| //-------------------------------------------------------------------- | ||||
| // Common Configuration | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| // defined by compiler flags for flexibility | ||||
| #ifndef CFG_TUSB_MCU | ||||
| #error CFG_TUSB_MCU must be defined | ||||
| #endif | ||||
|  | ||||
| #ifndef CFG_TUSB_OS | ||||
| #define CFG_TUSB_OS           OPT_OS_NONE | ||||
| #endif | ||||
|  | ||||
| // Espressif IDF requires "freertos/" prefix in include path | ||||
| #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) | ||||
| #define CFG_TUSB_OS_INC_PATH  freertos/ | ||||
| #endif | ||||
|  | ||||
| #ifndef CFG_TUSB_DEBUG | ||||
| #define CFG_TUSB_DEBUG        0 | ||||
| #endif | ||||
|  | ||||
| // Enable Device stack | ||||
| #define CFG_TUD_ENABLED       1 | ||||
|  | ||||
| // Default is max speed that hardware controller could support with on-chip PHY | ||||
| #define CFG_TUD_MAX_SPEED     BOARD_TUD_MAX_SPEED | ||||
|  | ||||
| /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. | ||||
|  * Tinyusb use follows macros to declare transferring memory so that they can be put | ||||
|  * into those specific section. | ||||
|  * e.g | ||||
|  * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) | ||||
|  * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4))) | ||||
|  */ | ||||
| #ifndef CFG_TUSB_MEM_SECTION | ||||
| #define CFG_TUSB_MEM_SECTION | ||||
| #endif | ||||
|  | ||||
| #ifndef CFG_TUSB_MEM_ALIGN | ||||
| #define CFG_TUSB_MEM_ALIGN        __attribute__ ((aligned(4))) | ||||
| #endif | ||||
|  | ||||
| //-------------------------------------------------------------------- | ||||
| // DEVICE CONFIGURATION | ||||
| //-------------------------------------------------------------------- | ||||
|  | ||||
| #ifndef CFG_TUD_ENDPOINT0_SIZE | ||||
| #define CFG_TUD_ENDPOINT0_SIZE    64 | ||||
| #endif | ||||
|  | ||||
| //------------- CLASS -------------// | ||||
| // The number of video control interfaces | ||||
| #define CFG_TUD_VIDEO            2 | ||||
|  | ||||
| // The number of video streaming interfaces | ||||
| #define CFG_TUD_VIDEO_STREAMING  2 | ||||
|  | ||||
| // video streaming endpoint buffer size | ||||
| #define CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE  256 | ||||
|  | ||||
| // use bulk endpoint for streaming interface | ||||
| #define CFG_TUD_VIDEO_STREAMING_BULK 1 | ||||
|  | ||||
| //#define CFG_EXAMPLE_VIDEO_READONLY | ||||
| //#define CFG_EXAMPLE_VIDEO_DISABLE_MJPEG | ||||
|  | ||||
| #define CFG_TUD_VIDEO_LOG_LEVEL 1 | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|  } | ||||
| #endif | ||||
|  | ||||
| #endif /* _TUSB_CONFIG_H_ */ | ||||
							
								
								
									
										653
									
								
								examples/device/video_capture_2ch/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										653
									
								
								examples/device/video_capture_2ch/src/usb_descriptors.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,653 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2019 Ha Thach (tinyusb.org) | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include "bsp/board_api.h" | ||||
| #include "tusb.h" | ||||
| #include "usb_descriptors.h" | ||||
|  | ||||
| /* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. | ||||
|  * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. | ||||
|  * | ||||
|  * Auto ProductID layout's Bitmap: | ||||
|  *   [MSB]  VIDEO | AUDIO | MIDI | HID | MSC | CDC          [LSB] | ||||
|  */ | ||||
| #define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) ) | ||||
| #define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ | ||||
|     _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VIDEO, 5) | _PID_MAP(VENDOR, 6) ) | ||||
|  | ||||
| #define USB_VID   0xCafe | ||||
| #define USB_BCD   0x0200 | ||||
|  | ||||
| // String Descriptor Index | ||||
| enum { | ||||
|   STRID_LANGID = 0, | ||||
|   STRID_MANUFACTURER, | ||||
|   STRID_PRODUCT, | ||||
|   STRID_SERIAL, | ||||
|   STRID_UVC_CONTROL_1, | ||||
|   STRID_UVC_STREAMING_1, | ||||
|   STRID_UVC_CONTROL_2, | ||||
|   STRID_UVC_STREAMING_2, | ||||
| }; | ||||
|  | ||||
| // array of pointer to string descriptors | ||||
| char const* string_desc_arr[] = { | ||||
|     (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409) | ||||
|     "TinyUSB",                   // 1: Manufacturer | ||||
|     "TinyUSB Device",            // 2: Product | ||||
|     NULL,                        // 3: Serials will use unique ID if possible | ||||
|     "UVC Control 1",             // 4: UVC Interface 1 | ||||
|     "UVC Streaming 1",           // 5: UVC Interface 1 | ||||
|     "UVC Control 2",             // 6: UVC Interface 2 | ||||
|     "UVC Streaming 2",           // 7: UVC Interface 2 | ||||
|  | ||||
| }; | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Device Descriptors | ||||
| //--------------------------------------------------------------------+ | ||||
| tusb_desc_device_t const desc_device = { | ||||
|     .bLength            = sizeof(tusb_desc_device_t), | ||||
|     .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|     .bcdUSB             = USB_BCD, | ||||
|  | ||||
|     // Use Interface Association Descriptor (IAD) for Video | ||||
|     // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) | ||||
|     .bDeviceClass       = TUSB_CLASS_MISC, | ||||
|     .bDeviceSubClass    = MISC_SUBCLASS_COMMON, | ||||
|     .bDeviceProtocol    = MISC_PROTOCOL_IAD, | ||||
|  | ||||
|     .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE, | ||||
|  | ||||
|     .idVendor           = USB_VID, | ||||
|     .idProduct          = USB_PID, | ||||
|     .bcdDevice          = 0x0100, | ||||
|  | ||||
|     .iManufacturer      = STRID_MANUFACTURER, | ||||
|     .iProduct           = STRID_PRODUCT, | ||||
|     .iSerialNumber      = STRID_SERIAL, | ||||
|  | ||||
|     .bNumConfigurations = 0x01 | ||||
| }; | ||||
|  | ||||
| // Invoked when received GET DEVICE DESCRIPTOR | ||||
| // Application return pointer to descriptor | ||||
| uint8_t const* tud_descriptor_device_cb(void) { | ||||
|   return (uint8_t const*) &desc_device; | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // Configuration Descriptor | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| /* Time stamp base clock. It is a deprecated parameter. */ | ||||
| #define UVC_CLOCK_FREQUENCY    27000000 | ||||
|  | ||||
| /* video capture path */ | ||||
| #define UVC_ENTITY_CAP_INPUT_TERMINAL  0x01 | ||||
| #define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02 | ||||
|  | ||||
| enum { | ||||
|   ITF_NUM_VIDEO_CONTROL_1, | ||||
|   ITF_NUM_VIDEO_STREAMING_1, | ||||
|   ITF_NUM_VIDEO_CONTROL_2, | ||||
|   ITF_NUM_VIDEO_STREAMING_2, | ||||
|   ITF_NUM_TOTAL | ||||
| }; | ||||
|  | ||||
| #define EPNUM_VIDEO_IN_1    0x81 | ||||
| #define EPNUM_VIDEO_IN_2    0x82 | ||||
|  | ||||
| #if defined(CFG_EXAMPLE_VIDEO_READONLY) && !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG) | ||||
|   #define USE_MJPEG 1 | ||||
| #else | ||||
|   #define USE_MJPEG 0 | ||||
| #endif | ||||
|  | ||||
| #define USE_ISO_STREAMING (!CFG_TUD_VIDEO_STREAMING_BULK) | ||||
|  | ||||
| typedef struct TU_ATTR_PACKED { | ||||
|   tusb_desc_interface_t itf; | ||||
|   tusb_desc_video_control_header_1itf_t header; | ||||
|   tusb_desc_video_control_camera_terminal_t camera_terminal; | ||||
|   tusb_desc_video_control_output_terminal_t output_terminal; | ||||
| } uvc_control_desc_t; | ||||
|  | ||||
| /* Windows support YUY2 and NV12 | ||||
|  * https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/usb-video-class-driver-overview */ | ||||
|  | ||||
| typedef struct TU_ATTR_PACKED { | ||||
|   tusb_desc_interface_t itf; | ||||
|   tusb_desc_video_streaming_input_header_1byte_t header; | ||||
|   tusb_desc_video_format_uncompressed_t format; | ||||
|   tusb_desc_video_frame_uncompressed_continuous_t frame; | ||||
|   tusb_desc_video_streaming_color_matching_t color; | ||||
|  | ||||
| #if USE_ISO_STREAMING | ||||
|   // For ISO streaming, USB spec requires to alternate interface | ||||
|   tusb_desc_interface_t itf_alt; | ||||
| #endif | ||||
|  | ||||
|   tusb_desc_endpoint_t ep; | ||||
| } uvc_streaming_yuy2_desc_t; | ||||
|  | ||||
| typedef struct TU_ATTR_PACKED { | ||||
|   tusb_desc_interface_t itf; | ||||
|   tusb_desc_video_streaming_input_header_1byte_t header; | ||||
|   tusb_desc_video_format_mjpeg_t format; | ||||
|   tusb_desc_video_frame_mjpeg_continuous_t frame; | ||||
|   tusb_desc_video_streaming_color_matching_t color; | ||||
|  | ||||
| #if USE_ISO_STREAMING | ||||
|   // For ISO streaming, USB spec requires to alternate interface | ||||
|   tusb_desc_interface_t itf_alt; | ||||
| #endif | ||||
|  | ||||
|   tusb_desc_endpoint_t ep; | ||||
| } uvc_streaming_mpeg_desc_t; | ||||
|  | ||||
| typedef struct TU_ATTR_PACKED { | ||||
|   tusb_desc_configuration_t config; | ||||
|  | ||||
|   struct TU_ATTR_PACKED { | ||||
|     tusb_desc_interface_assoc_t iad; | ||||
|     uvc_control_desc_t video_control; | ||||
|     uvc_streaming_yuy2_desc_t video_streaming; | ||||
|   } uvc_yuy2; | ||||
|  | ||||
|   struct TU_ATTR_PACKED { | ||||
|     tusb_desc_interface_assoc_t iad; | ||||
|     uvc_control_desc_t video_control; | ||||
|     uvc_streaming_mpeg_desc_t video_streaming; | ||||
|   } uvc_mpeg; | ||||
| } uvc_cfg_desc_t; | ||||
|  | ||||
| const uvc_cfg_desc_t desc_fs_configuration = { | ||||
|     .config = { | ||||
|         .bLength = sizeof(tusb_desc_configuration_t), | ||||
|         .bDescriptorType = TUSB_DESC_CONFIGURATION, | ||||
|  | ||||
|         .wTotalLength = sizeof(uvc_cfg_desc_t), | ||||
|         .bNumInterfaces = ITF_NUM_TOTAL, | ||||
|         .bConfigurationValue = 1, | ||||
|         .iConfiguration = 0, | ||||
|         .bmAttributes =  TU_BIT(7), | ||||
|         .bMaxPower = 100 / 2 | ||||
|     }, | ||||
|     //------------- Stream 0: YUY2 -------------// | ||||
|     .uvc_yuy2 = { | ||||
|         .iad = { | ||||
|             .bLength = sizeof(tusb_desc_interface_assoc_t), | ||||
|             .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION, | ||||
|  | ||||
|             .bFirstInterface = ITF_NUM_VIDEO_CONTROL_1, | ||||
|             .bInterfaceCount = 2, | ||||
|             .bFunctionClass = TUSB_CLASS_VIDEO, | ||||
|             .bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION, | ||||
|             .bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED, | ||||
|             .iFunction = 0 | ||||
|         }, | ||||
|         .video_control = { | ||||
|             .itf = { | ||||
|                 .bLength = sizeof(tusb_desc_interface_t), | ||||
|                 .bDescriptorType = TUSB_DESC_INTERFACE, | ||||
|  | ||||
|                 .bInterfaceNumber = ITF_NUM_VIDEO_CONTROL_1, | ||||
|                 .bAlternateSetting = 0, | ||||
|                 .bNumEndpoints = 0, | ||||
|                 .bInterfaceClass = TUSB_CLASS_VIDEO, | ||||
|                 .bInterfaceSubClass = VIDEO_SUBCLASS_CONTROL, | ||||
|                 .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, | ||||
|                 .iInterface = STRID_UVC_CONTROL_1 | ||||
|             }, | ||||
|             .header = { | ||||
|                 .bLength = sizeof(tusb_desc_video_control_header_1itf_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VC_HEADER, | ||||
|  | ||||
|                 .bcdUVC = VIDEO_BCD_1_50, | ||||
|                 .wTotalLength = sizeof(uvc_control_desc_t) - sizeof(tusb_desc_interface_t), // CS VC descriptors only | ||||
|                 .dwClockFrequency = UVC_CLOCK_FREQUENCY, | ||||
|                 .bInCollection = 1, | ||||
|                 .baInterfaceNr = {ITF_NUM_VIDEO_STREAMING_1} | ||||
|             }, | ||||
|             .camera_terminal = { | ||||
|                 .bLength = sizeof(tusb_desc_video_control_camera_terminal_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL, | ||||
|  | ||||
|                 .bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL, | ||||
|                 .wTerminalType = VIDEO_ITT_CAMERA, | ||||
|                 .bAssocTerminal = 0, | ||||
|                 .iTerminal = 0, | ||||
|                 .wObjectiveFocalLengthMin = 0, | ||||
|                 .wObjectiveFocalLengthMax = 0, | ||||
|                 .wOcularFocalLength = 0, | ||||
|                 .bControlSize = 3, | ||||
|                 .bmControls = {0, 0, 0} | ||||
|             }, | ||||
|             .output_terminal = { | ||||
|                 .bLength = sizeof(tusb_desc_video_control_output_terminal_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VC_OUTPUT_TERMINAL, | ||||
|  | ||||
|                 .bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL, | ||||
|                 .wTerminalType = VIDEO_TT_STREAMING, | ||||
|                 .bAssocTerminal = 0, | ||||
|                 .bSourceID = UVC_ENTITY_CAP_INPUT_TERMINAL, | ||||
|                 .iTerminal = 0 | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         .video_streaming = { | ||||
|             .itf = { | ||||
|                 .bLength = sizeof(tusb_desc_interface_t), | ||||
|                 .bDescriptorType = TUSB_DESC_INTERFACE, | ||||
|  | ||||
|                 .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_1, | ||||
|                 .bAlternateSetting = 0, | ||||
|                 .bNumEndpoints = CFG_TUD_VIDEO_STREAMING_BULK, // bulk 1, iso 0 | ||||
|                 .bInterfaceClass = TUSB_CLASS_VIDEO, | ||||
|                 .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING, | ||||
|                 .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, | ||||
|                 .iInterface = STRID_UVC_STREAMING_1 | ||||
|             }, | ||||
|             .header = { | ||||
|                 .bLength = sizeof(tusb_desc_video_streaming_input_header_1byte_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_INPUT_HEADER, | ||||
|  | ||||
|                 .bNumFormats = 1, | ||||
|                 .wTotalLength = sizeof(uvc_streaming_yuy2_desc_t) - sizeof(tusb_desc_interface_t) | ||||
|                                 - sizeof(tusb_desc_endpoint_t) - | ||||
|                                 (USE_ISO_STREAMING ? sizeof(tusb_desc_interface_t) : 0), // CS VS descriptors only | ||||
|                 .bEndpointAddress = EPNUM_VIDEO_IN_1, | ||||
|                 .bmInfo = 0, | ||||
|                 .bTerminalLink = UVC_ENTITY_CAP_OUTPUT_TERMINAL, | ||||
|                 .bStillCaptureMethod = 0, | ||||
|                 .bTriggerSupport = 0, | ||||
|                 .bTriggerUsage = 0, | ||||
|                 .bControlSize = 1, | ||||
|                 .bmaControls = {0} | ||||
|             }, | ||||
|             .format = { | ||||
|                 .bLength = sizeof(tusb_desc_video_format_uncompressed_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, | ||||
|                 .bFormatIndex = 1, // 1-based index | ||||
|                 .bNumFrameDescriptors = 1, | ||||
|                 .guidFormat = {TUD_VIDEO_GUID_YUY2}, | ||||
|                 .bBitsPerPixel = 16, | ||||
|                 .bDefaultFrameIndex = 1, | ||||
|                 .bAspectRatioX = 0, | ||||
|                 .bAspectRatioY = 0, | ||||
|                 .bmInterlaceFlags = 0, | ||||
|                 .bCopyProtect = 0 | ||||
|             }, | ||||
|             .frame = { | ||||
|                 .bLength = sizeof(tusb_desc_video_frame_uncompressed_continuous_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, | ||||
|                 .bFrameIndex = 1, // 1-based index | ||||
|                 .bmCapabilities = 0, | ||||
|                 .wWidth = FRAME_WIDTH, | ||||
|                 .wHeight = FRAME_HEIGHT, | ||||
|                 .dwMinBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * 1, | ||||
|                 .dwMaxBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * FRAME_RATE, | ||||
|                 .dwMaxVideoFrameBufferSize = FRAME_WIDTH * FRAME_HEIGHT * 16 / 8, | ||||
|                 .dwDefaultFrameInterval = 10000000 / FRAME_RATE, | ||||
|                 .bFrameIntervalType = 0, // continuous | ||||
|                 .dwFrameInterval = { | ||||
|                     10000000 / FRAME_RATE, // min | ||||
|                     10000000, // max | ||||
|                     10000000 / FRAME_RATE // step | ||||
|                 } | ||||
|             }, | ||||
|             .color = { | ||||
|                 .bLength = sizeof(tusb_desc_video_streaming_color_matching_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_COLORFORMAT, | ||||
|  | ||||
|                 .bColorPrimaries = VIDEO_COLOR_PRIMARIES_BT709, | ||||
|                 .bTransferCharacteristics = VIDEO_COLOR_XFER_CH_BT709, | ||||
|                 .bMatrixCoefficients = VIDEO_COLOR_COEF_SMPTE170M | ||||
|             }, | ||||
|  | ||||
| #if USE_ISO_STREAMING | ||||
|             .itf_alt = { | ||||
|                 .bLength = sizeof(tusb_desc_interface_t), | ||||
|                 .bDescriptorType = TUSB_DESC_INTERFACE, | ||||
|  | ||||
|                 .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_1, | ||||
|                 .bAlternateSetting = 1, | ||||
|                 .bNumEndpoints = 1, | ||||
|                 .bInterfaceClass = TUSB_CLASS_VIDEO, | ||||
|                 .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING, | ||||
|                 .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, | ||||
|                 .iInterface = STRID_UVC_STREAMING_1 | ||||
|             }, | ||||
| #endif | ||||
|             .ep = { | ||||
|                 .bLength = sizeof(tusb_desc_endpoint_t), | ||||
|                 .bDescriptorType = TUSB_DESC_ENDPOINT, | ||||
|  | ||||
|                 .bEndpointAddress = EPNUM_VIDEO_IN_1, | ||||
|                 .bmAttributes = { | ||||
|                     .xfer = CFG_TUD_VIDEO_STREAMING_BULK ? TUSB_XFER_BULK : TUSB_XFER_ISOCHRONOUS, | ||||
|                     .sync = CFG_TUD_VIDEO_STREAMING_BULK ? 0 : 1 // asynchronous | ||||
|                 }, | ||||
|                 .wMaxPacketSize = CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE, | ||||
|                 .bInterval = 1 | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|       //------------- Stream 1: MPEG -------------// | ||||
|     .uvc_mpeg = { | ||||
|         .iad = { | ||||
|             .bLength = sizeof(tusb_desc_interface_assoc_t), | ||||
|             .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION, | ||||
|  | ||||
|             .bFirstInterface = ITF_NUM_VIDEO_CONTROL_2, | ||||
|             .bInterfaceCount = 2, | ||||
|             .bFunctionClass = TUSB_CLASS_VIDEO, | ||||
|             .bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION, | ||||
|             .bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED, | ||||
|             .iFunction = 0 | ||||
|         }, | ||||
|  | ||||
|         .video_control = { | ||||
|             .itf = { | ||||
|                 .bLength = sizeof(tusb_desc_interface_t), | ||||
|                 .bDescriptorType = TUSB_DESC_INTERFACE, | ||||
|  | ||||
|                 .bInterfaceNumber = ITF_NUM_VIDEO_CONTROL_2, | ||||
|                 .bAlternateSetting = 0, | ||||
|                 .bNumEndpoints = 0, | ||||
|                 .bInterfaceClass = TUSB_CLASS_VIDEO, | ||||
|                 .bInterfaceSubClass = VIDEO_SUBCLASS_CONTROL, | ||||
|                 .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, | ||||
|                 .iInterface = STRID_UVC_CONTROL_2 | ||||
|             }, | ||||
|             .header = { | ||||
|                 .bLength = sizeof(tusb_desc_video_control_header_1itf_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VC_HEADER, | ||||
|  | ||||
|                 .bcdUVC = VIDEO_BCD_1_50, | ||||
|                 .wTotalLength = sizeof(uvc_control_desc_t) - sizeof(tusb_desc_interface_t), // CS VC descriptors only | ||||
|                 .dwClockFrequency = UVC_CLOCK_FREQUENCY, | ||||
|                 .bInCollection = 1, | ||||
|                 .baInterfaceNr = { ITF_NUM_VIDEO_STREAMING_2 } | ||||
|             }, | ||||
|             .camera_terminal = { | ||||
|                 .bLength = sizeof(tusb_desc_video_control_camera_terminal_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL, | ||||
|  | ||||
|                 .bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL, | ||||
|                 .wTerminalType = VIDEO_ITT_CAMERA, | ||||
|                 .bAssocTerminal = 0, | ||||
|                 .iTerminal = 0, | ||||
|                 .wObjectiveFocalLengthMin = 0, | ||||
|                 .wObjectiveFocalLengthMax = 0, | ||||
|                 .wOcularFocalLength = 0, | ||||
|                 .bControlSize = 3, | ||||
|                 .bmControls = { 0, 0, 0 } | ||||
|             }, | ||||
|             .output_terminal = { | ||||
|                 .bLength = sizeof(tusb_desc_video_control_output_terminal_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VC_OUTPUT_TERMINAL, | ||||
|  | ||||
|                 .bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL, | ||||
|                 .wTerminalType = VIDEO_TT_STREAMING, | ||||
|                 .bAssocTerminal = 0, | ||||
|                 .bSourceID = UVC_ENTITY_CAP_INPUT_TERMINAL, | ||||
|                 .iTerminal = 0 | ||||
|             } | ||||
|         }, | ||||
|  | ||||
|         .video_streaming = { | ||||
|             .itf = { | ||||
|                 .bLength = sizeof(tusb_desc_interface_t), | ||||
|                 .bDescriptorType = TUSB_DESC_INTERFACE, | ||||
|  | ||||
|                 .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_2, | ||||
|                 .bAlternateSetting = 0, | ||||
|                 .bNumEndpoints = CFG_TUD_VIDEO_STREAMING_BULK, // bulk 1, iso 0 | ||||
|                 .bInterfaceClass = TUSB_CLASS_VIDEO, | ||||
|                 .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING, | ||||
|                 .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, | ||||
|                 .iInterface = STRID_UVC_STREAMING_2 | ||||
|             }, | ||||
|             .header = { | ||||
|                 .bLength = sizeof(tusb_desc_video_streaming_input_header_1byte_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_INPUT_HEADER, | ||||
|  | ||||
|                 .bNumFormats = 1, | ||||
|                 .wTotalLength = sizeof(uvc_streaming_mpeg_desc_t) - sizeof(tusb_desc_interface_t) | ||||
|                                 - sizeof(tusb_desc_endpoint_t) - (USE_ISO_STREAMING ? sizeof(tusb_desc_interface_t) : 0) , // CS VS descriptors only | ||||
|                 .bEndpointAddress = EPNUM_VIDEO_IN_2, | ||||
|                 .bmInfo = 0, | ||||
|                 .bTerminalLink = UVC_ENTITY_CAP_OUTPUT_TERMINAL, | ||||
|                 .bStillCaptureMethod = 0, | ||||
|                 .bTriggerSupport = 0, | ||||
|                 .bTriggerUsage = 0, | ||||
|                 .bControlSize = 1, | ||||
|                 .bmaControls = { 0 } | ||||
|             }, | ||||
|             .format = { | ||||
|                 .bLength = sizeof(tusb_desc_video_format_mjpeg_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_MJPEG, | ||||
|                 .bFormatIndex = 1, // 1-based index | ||||
|                 .bNumFrameDescriptors = 1, | ||||
|                 .bmFlags = 0, | ||||
|                 .bDefaultFrameIndex = 1, | ||||
|                 .bAspectRatioX = 0, | ||||
|                 .bAspectRatioY = 0, | ||||
|                 .bmInterlaceFlags = 0, | ||||
|                 .bCopyProtect = 0 | ||||
|             }, | ||||
|             .frame = { | ||||
|                 .bLength = sizeof(tusb_desc_video_frame_mjpeg_continuous_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_MJPEG, | ||||
|                 .bFrameIndex = 1, // 1-based index | ||||
|                 .bmCapabilities = 0, | ||||
|                 .wWidth = FRAME_WIDTH, | ||||
|                 .wHeight = FRAME_HEIGHT, | ||||
|                 .dwMinBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * 1, | ||||
|                 .dwMaxBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * FRAME_RATE, | ||||
|                 .dwMaxVideoFrameBufferSize = FRAME_WIDTH * FRAME_HEIGHT * 16 / 8, | ||||
|                 .dwDefaultFrameInterval = 10000000 / FRAME_RATE, | ||||
|                 .bFrameIntervalType = 0, // continuous | ||||
|                 .dwFrameInterval = { | ||||
|                     10000000 / FRAME_RATE, // min | ||||
|                     10000000, // max | ||||
|                     10000000 / FRAME_RATE // step | ||||
|                 } | ||||
|             }, | ||||
|             .color = { | ||||
|                 .bLength = sizeof(tusb_desc_video_streaming_color_matching_t), | ||||
|                 .bDescriptorType = TUSB_DESC_CS_INTERFACE, | ||||
|                 .bDescriptorSubType = VIDEO_CS_ITF_VS_COLORFORMAT, | ||||
|  | ||||
|                 .bColorPrimaries = VIDEO_COLOR_PRIMARIES_BT709, | ||||
|                 .bTransferCharacteristics = VIDEO_COLOR_XFER_CH_BT709, | ||||
|                 .bMatrixCoefficients = VIDEO_COLOR_COEF_SMPTE170M | ||||
|             }, | ||||
|  | ||||
| #if USE_ISO_STREAMING | ||||
|             .itf_alt = { | ||||
|                 .bLength = sizeof(tusb_desc_interface_t), | ||||
|                 .bDescriptorType = TUSB_DESC_INTERFACE, | ||||
|  | ||||
|                 .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_2, | ||||
|                 .bAlternateSetting = 1, | ||||
|                 .bNumEndpoints = 1, | ||||
|                 .bInterfaceClass = TUSB_CLASS_VIDEO, | ||||
|                 .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING, | ||||
|                 .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, | ||||
|                 .iInterface = STRID_UVC_STREAMING_2 | ||||
|             }, | ||||
| #endif | ||||
|             .ep = { | ||||
|                 .bLength = sizeof(tusb_desc_endpoint_t), | ||||
|                 .bDescriptorType = TUSB_DESC_ENDPOINT, | ||||
|  | ||||
|                 .bEndpointAddress = EPNUM_VIDEO_IN_2, | ||||
|                 .bmAttributes = { | ||||
|                     .xfer = CFG_TUD_VIDEO_STREAMING_BULK ? TUSB_XFER_BULK : TUSB_XFER_ISOCHRONOUS, | ||||
|                     .sync = CFG_TUD_VIDEO_STREAMING_BULK ? 0 : 1 // asynchronous | ||||
|                 }, | ||||
|                 .wMaxPacketSize = CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE, | ||||
|                 .bInterval = 1 | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
| #if TUD_OPT_HIGH_SPEED | ||||
| uvc_cfg_desc_t desc_hs_configuration; | ||||
|  | ||||
| static uint8_t * get_hs_configuration_desc(void) { | ||||
|   static bool init = false; | ||||
|  | ||||
|   if (!init) { | ||||
|     desc_hs_configuration = desc_fs_configuration; | ||||
|     // change endpoint bulk size to 512 if bulk streaming | ||||
|     if (CFG_TUD_VIDEO_STREAMING_BULK) { | ||||
|       desc_hs_configuration.uvc_yuy2.video_streaming.ep.wMaxPacketSize = 512; | ||||
|       desc_hs_configuration.uvc_mpeg.video_streaming.ep.wMaxPacketSize = 512; | ||||
|     } | ||||
|   } | ||||
|   init = true; | ||||
|  | ||||
|   return (uint8_t *) &desc_hs_configuration; | ||||
| } | ||||
|  | ||||
| // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed | ||||
| tusb_desc_device_qualifier_t const desc_device_qualifier = { | ||||
|     .bLength            = sizeof(tusb_desc_device_t), | ||||
|     .bDescriptorType    = TUSB_DESC_DEVICE, | ||||
|     .bcdUSB             = USB_BCD, | ||||
|  | ||||
|     .bDeviceClass       = TUSB_CLASS_MISC, | ||||
|     .bDeviceSubClass    = MISC_SUBCLASS_COMMON, | ||||
|     .bDeviceProtocol    = MISC_PROTOCOL_IAD, | ||||
|  | ||||
|     .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE, | ||||
|     .bNumConfigurations = 0x01, | ||||
|     .bReserved          = 0x00 | ||||
| }; | ||||
|  | ||||
| // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete. | ||||
| // device_qualifier descriptor describes information about a high-speed capable device that would | ||||
| // change if the device were operating at the other speed. If not highspeed capable stall this request. | ||||
| uint8_t const* tud_descriptor_device_qualifier_cb(void) { | ||||
|   return (uint8_t const*) &desc_device_qualifier; | ||||
| } | ||||
|  | ||||
| // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete | ||||
| // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa | ||||
| uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) { | ||||
|   (void) index; // for multiple configurations | ||||
|   // if link speed is high return fullspeed config, and vice versa | ||||
|   if (tud_speed_get() == TUSB_SPEED_HIGH) { | ||||
|     return (uint8_t const*) &desc_fs_configuration; | ||||
|   } else { | ||||
|     return get_hs_configuration_desc(); | ||||
|   } | ||||
| } | ||||
| #endif // highspeed | ||||
|  | ||||
| // Invoked when received GET CONFIGURATION DESCRIPTOR | ||||
| // Application return pointer to descriptor | ||||
| // Descriptor contents must exist long enough for transfer to complete | ||||
| uint8_t const* tud_descriptor_configuration_cb(uint8_t index) { | ||||
|   (void) index; // for multiple configurations | ||||
|  | ||||
| #if TUD_OPT_HIGH_SPEED | ||||
|   // Although we are highspeed, host may be fullspeed. | ||||
|   if (tud_speed_get() == TUSB_SPEED_HIGH) { | ||||
|     return get_hs_configuration_desc(); | ||||
|   } else | ||||
| #endif | ||||
|   { | ||||
|     return (uint8_t const*) &desc_fs_configuration; | ||||
|   } | ||||
| } | ||||
|  | ||||
| //--------------------------------------------------------------------+ | ||||
| // String Descriptors | ||||
| //--------------------------------------------------------------------+ | ||||
|  | ||||
| static uint16_t _desc_str[32 + 1]; | ||||
|  | ||||
| // Invoked when received GET STRING DESCRIPTOR request | ||||
| // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete | ||||
| uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) { | ||||
|   (void) langid; | ||||
|   size_t chr_count; | ||||
|  | ||||
|   switch (index) { | ||||
|     case STRID_LANGID: | ||||
|       memcpy(&_desc_str[1], string_desc_arr[0], 2); | ||||
|       chr_count = 1; | ||||
|       break; | ||||
|  | ||||
|     case STRID_SERIAL: | ||||
|       chr_count = board_usb_get_serial(_desc_str + 1, 32); | ||||
|       break; | ||||
|  | ||||
|     default: | ||||
|       // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. | ||||
|       // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors | ||||
|  | ||||
|       if (index >= sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) return NULL; | ||||
|  | ||||
|       const char* str = string_desc_arr[index]; | ||||
|  | ||||
|       // Cap at max char | ||||
|       chr_count = strlen(str); | ||||
|       size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type | ||||
|       if (chr_count > max_count) chr_count = max_count; | ||||
|  | ||||
|       // Convert ASCII string into UTF-16 | ||||
|       for (size_t i = 0; i < chr_count; i++) { | ||||
|         _desc_str[1 + i] = str[i]; | ||||
|       } | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   // first byte is length (including header), second byte is string type | ||||
|   _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2)); | ||||
|  | ||||
|   return _desc_str; | ||||
| } | ||||
							
								
								
									
										238
									
								
								examples/device/video_capture_2ch/src/usb_descriptors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								examples/device/video_capture_2ch/src/usb_descriptors.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,238 @@ | ||||
| /* | ||||
|  * The MIT License (MIT) | ||||
|  * | ||||
|  * Copyright (c) 2020 Jerzy Kasenbreg | ||||
|  * Copyright (c) 2021 Koji KITAYAMA | ||||
|  * | ||||
|  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||
|  * of this software and associated documentation files (the "Software"), to deal | ||||
|  * in the Software without restriction, including without limitation the rights | ||||
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||
|  * copies of the Software, and to permit persons to whom the Software is | ||||
|  * furnished to do so, subject to the following conditions: | ||||
|  * | ||||
|  * The above copyright notice and this permission notice shall be included in | ||||
|  * all copies or substantial portions of the Software. | ||||
|  * | ||||
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||
|  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||
|  * THE SOFTWARE. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef _USB_DESCRIPTORS_H_ | ||||
| #define _USB_DESCRIPTORS_H_ | ||||
|  | ||||
| #define FRAME_WIDTH   128 | ||||
| #define FRAME_HEIGHT  96 | ||||
| #define FRAME_RATE    10 | ||||
|  | ||||
| // NOTE: descriptor template is not used but leave here as reference | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESC_UNCOMPR_LEN (\ | ||||
|     TUD_VIDEO_DESC_IAD_LEN\ | ||||
|     /* control */\ | ||||
|     + TUD_VIDEO_DESC_STD_VC_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VC_LEN + 1/*bInCollection*/)\ | ||||
|     + TUD_VIDEO_DESC_CAMERA_TERM_LEN\ | ||||
|     + TUD_VIDEO_DESC_OUTPUT_TERM_LEN\ | ||||
|     /* Interface 1, Alternate 0 */\ | ||||
|     + TUD_VIDEO_DESC_STD_VS_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VS_IN_LEN + 1/*bNumFormats x bControlSize*/)\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN\ | ||||
|     /* Interface 1, Alternate 1 */\ | ||||
|     + TUD_VIDEO_DESC_STD_VS_LEN\ | ||||
|     + 7/* Endpoint */\ | ||||
|   ) | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESC_MJPEG_LEN (\ | ||||
|     TUD_VIDEO_DESC_IAD_LEN\ | ||||
|     /* control */\ | ||||
|     + TUD_VIDEO_DESC_STD_VC_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VC_LEN + 1/*bInCollection*/)\ | ||||
|     + TUD_VIDEO_DESC_CAMERA_TERM_LEN\ | ||||
|     + TUD_VIDEO_DESC_OUTPUT_TERM_LEN\ | ||||
|     /* Interface 1, Alternate 0 */\ | ||||
|     + TUD_VIDEO_DESC_STD_VS_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VS_IN_LEN + 1/*bNumFormats x bControlSize*/)\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN\ | ||||
|     /* Interface 1, Alternate 1 */\ | ||||
|     + TUD_VIDEO_DESC_STD_VS_LEN\ | ||||
|     + 7/* Endpoint */\ | ||||
|   ) | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESC_UNCOMPR_BULK_LEN (\ | ||||
|     TUD_VIDEO_DESC_IAD_LEN\ | ||||
|     /* control */\ | ||||
|     + TUD_VIDEO_DESC_STD_VC_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VC_LEN + 1/*bInCollection*/)\ | ||||
|     + TUD_VIDEO_DESC_CAMERA_TERM_LEN\ | ||||
|     + TUD_VIDEO_DESC_OUTPUT_TERM_LEN\ | ||||
|     /* Interface 1, Alternate 0 */\ | ||||
|     + TUD_VIDEO_DESC_STD_VS_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VS_IN_LEN + 1/*bNumFormats x bControlSize*/)\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN\ | ||||
|     + 7/* Endpoint */\ | ||||
|   ) | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESC_MJPEG_BULK_LEN (\ | ||||
|     TUD_VIDEO_DESC_IAD_LEN\ | ||||
|     /* control */\ | ||||
|     + TUD_VIDEO_DESC_STD_VC_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VC_LEN + 1/*bInCollection*/)\ | ||||
|     + TUD_VIDEO_DESC_CAMERA_TERM_LEN\ | ||||
|     + TUD_VIDEO_DESC_OUTPUT_TERM_LEN\ | ||||
|     /* Interface 1, Alternate 0 */\ | ||||
|     + TUD_VIDEO_DESC_STD_VS_LEN\ | ||||
|     + (TUD_VIDEO_DESC_CS_VS_IN_LEN + 1/*bNumFormats x bControlSize*/)\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN\ | ||||
|     + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN\ | ||||
|     + 7/* Endpoint */\ | ||||
|   ) | ||||
|  | ||||
| /* Windows support YUY2 and NV12 | ||||
|  * https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/usb-video-class-driver-overview */ | ||||
|  | ||||
| #define TUD_VIDEO_DESC_CS_VS_FMT_YUY2(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \ | ||||
|   TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_YUY2, 16, _frmidx, _asrx, _asry, _interlace, _cp) | ||||
| #define TUD_VIDEO_DESC_CS_VS_FMT_NV12(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \ | ||||
|   TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_NV12, 12, _frmidx, _asrx, _asry, _interlace, _cp) | ||||
| #define TUD_VIDEO_DESC_CS_VS_FMT_M420(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \ | ||||
|   TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_M420, 12, _frmidx, _asrx, _asry, _interlace, _cp) | ||||
| #define TUD_VIDEO_DESC_CS_VS_FMT_I420(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \ | ||||
|   TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_I420, 12, _frmidx, _asrx, _asry, _interlace, _cp) | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR(_stridx, _epin, _width, _height, _fps, _epsize) \ | ||||
|   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \ | ||||
|   /* Video control 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx),                                  \ | ||||
|     /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \ | ||||
|     TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \ | ||||
|       /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \ | ||||
|       TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \ | ||||
|       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \ | ||||
|   /* Video stream alt. 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 0, _stridx), \ | ||||
|     /* Video stream header for without still image capture */ \ | ||||
|     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \ | ||||
|         /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\ | ||||
|         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \ | ||||
|         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \ | ||||
|         /*bmaControls(1)*/0), \ | ||||
|       /* Video stream format */ \ | ||||
|       TUD_VIDEO_DESC_CS_VS_FMT_YUY2(/*bFormatIndex*/1, /*bNumFrameDescriptors*/1, \ | ||||
|         /*bDefaultFrameIndex*/1, 0, 0, 0, /*bCopyProtect*/0), \ | ||||
|         /* Video stream frame format */ \ | ||||
|         TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(/*bFrameIndex */1, 0, _width, _height, \ | ||||
|             _width * _height * 16, _width * _height * 16 * _fps, \ | ||||
|             _width * _height * 16 / 8, \ | ||||
|             (10000000/_fps), (10000000/_fps), (10000000/_fps)*_fps, (10000000/_fps)), \ | ||||
|         TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \ | ||||
|   /* VS alt 1 */\ | ||||
|   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 1, 1, _stridx), \ | ||||
|     /* EP */ \ | ||||
|     TUD_VIDEO_DESC_EP_ISO(_epin, _epsize, 1) | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG(_stridx, _epin, _width, _height, _fps, _epsize) \ | ||||
|   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \ | ||||
|   /* Video control 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx),                                \ | ||||
|     /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \ | ||||
|     TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \ | ||||
|       /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \ | ||||
|       TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \ | ||||
|       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \ | ||||
|   /* Video stream alt. 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 0, _stridx), \ | ||||
|     /* Video stream header for without still image capture */ \ | ||||
|     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \ | ||||
|         /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\ | ||||
|         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \ | ||||
|         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \ | ||||
|         /*bmaControls(1)*/0), \ | ||||
|       /* Video stream format */ \ | ||||
|       TUD_VIDEO_DESC_CS_VS_FMT_MJPEG(/*bFormatIndex*/1, /*bNumFrameDescriptors*/1, \ | ||||
|         /*bmFlags*/0, /*bDefaultFrameIndex*/1, 0, 0, 0, /*bCopyProtect*/0), \ | ||||
|         /* Video stream frame format */ \ | ||||
|         TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT(/*bFrameIndex */1, 0, _width, _height, \ | ||||
|             _width * _height * 16, _width * _height * 16 * _fps, \ | ||||
|             _width * _height * 16 / 8, \ | ||||
|             (10000000/_fps), (10000000/_fps), (10000000/_fps)*_fps, (10000000/_fps)), \ | ||||
|         TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \ | ||||
|   /* VS alt 1 */\ | ||||
|   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 1, 1, _stridx), \ | ||||
|     /* EP */ \ | ||||
|     TUD_VIDEO_DESC_EP_ISO(_epin, _epsize, 1) | ||||
|  | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESCRIPTOR_UNCOMPR_BULK(_stridx, _epin, _width, _height, _fps, _epsize) \ | ||||
|   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \ | ||||
|   /* Video control 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx), \ | ||||
|     /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \ | ||||
|     TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \ | ||||
|       /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \ | ||||
|       TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \ | ||||
|       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \ | ||||
|   /* Video stream alt. 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 1, _stridx), \ | ||||
|     /* Video stream header for without still image capture */ \ | ||||
|     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \ | ||||
|         /*wTotalLength - bLength */\ | ||||
|         TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN + TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\ | ||||
|         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \ | ||||
|         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \ | ||||
|         /*bmaControls(1)*/0), \ | ||||
|       /* Video stream format */ \ | ||||
|       TUD_VIDEO_DESC_CS_VS_FMT_YUY2(/*bFormatIndex*/1, /*bNumFrameDescriptors*/1, \ | ||||
|         /*bDefaultFrameIndex*/1, 0, 0, 0, /*bCopyProtect*/0), \ | ||||
|         /* Video stream frame format */ \ | ||||
|         TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(/*bFrameIndex */1, 0, _width, _height, \ | ||||
|             _width * _height * 16, _width * _height * 16 * _fps, \ | ||||
|             _width * _height * 16 / 8, \ | ||||
|             (10000000/_fps), (10000000/_fps), (10000000/_fps)*_fps, (10000000/_fps)), \ | ||||
|         TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \ | ||||
|         TUD_VIDEO_DESC_EP_BULK(_epin, _epsize, 1) | ||||
|  | ||||
| #define TUD_VIDEO_CAPTURE_DESCRIPTOR_MJPEG_BULK(_stridx, _epin, _width, _height, _fps, _epsize) \ | ||||
|   TUD_VIDEO_DESC_IAD(ITF_NUM_VIDEO_CONTROL, /* 2 Interfaces */ 0x02, _stridx), \ | ||||
|   /* Video control 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VC(ITF_NUM_VIDEO_CONTROL, 0, _stridx),                                     \ | ||||
|     /* Header: UVC 1.5, length of followed descs, clock (deprecated), streaming interfaces */ \ | ||||
|     TUD_VIDEO_DESC_CS_VC(0x0150, TUD_VIDEO_DESC_CAMERA_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, UVC_CLOCK_FREQUENCY, ITF_NUM_VIDEO_STREAMING), \ | ||||
|       /* Camera Terminal: ID, bAssocTerminal, iTerminal, focal min, max, length, bmControl */ \ | ||||
|       TUD_VIDEO_DESC_CAMERA_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, 0, 0, 0, 0, 0, 0), \ | ||||
|       TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, UVC_ENTITY_CAP_INPUT_TERMINAL, 0), \ | ||||
|   /* Video stream alt. 0 */ \ | ||||
|   TUD_VIDEO_DESC_STD_VS(ITF_NUM_VIDEO_STREAMING, 0, 1, _stridx), \ | ||||
|     /* Video stream header for without still image capture */ \ | ||||
|     TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \ | ||||
|         /*wTotalLength - bLength */ TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN + TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\ | ||||
|         _epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \ | ||||
|         /*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \ | ||||
|         /*bmaControls(1)*/0), \ | ||||
|       /* Video stream format */ \ | ||||
|       TUD_VIDEO_DESC_CS_VS_FMT_MJPEG(/*bFormatIndex*/1, /*bNumFrameDescriptors*/1, \ | ||||
|         /*bmFlags*/0, /*bDefaultFrameIndex*/1, 0, 0, 0, /*bCopyProtect*/0), \ | ||||
|         /* Video stream frame format */ \ | ||||
|         TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT(/*bFrameIndex */1, 0, _width, _height, \ | ||||
|             _width * _height * 16, _width * _height * 16 * _fps, \ | ||||
|             _width * _height * 16 / 8, \ | ||||
|             (10000000/_fps), (10000000/_fps), (10000000/_fps)*_fps, (10000000/_fps)), \ | ||||
|         TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \ | ||||
|         /* EP */ \ | ||||
|         TUD_VIDEO_DESC_EP_BULK(_epin, _epsize, 1) | ||||
|  | ||||
|  | ||||
| #endif | ||||
		Reference in New Issue
	
	Block a user
	 Ha Thach
					Ha Thach