update uvc 2ch to stream0 use yuy2, stream1 use mpeg

this help reduce sram requirement for example, also provide different format (uncompressed & mpeg)
This commit is contained in:
hathach 2024-04-05 00:55:48 +07:00
parent 34870d3481
commit 34737f9c60
No known key found for this signature in database
GPG Key ID: 26FAB84F615C3C52
4 changed files with 390 additions and 411 deletions

View File

@ -56,8 +56,8 @@ char const* string_desc_arr[] = {
"TinyUSB", // 1: Manufacturer "TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product "TinyUSB Device", // 2: Product
NULL, // 3: Serials will use unique ID if possible NULL, // 3: Serials will use unique ID if possible
"TinyUSB UVC Control", // 4: UVC Interface "UVC Control", // 4: UVC Interface
"TinyUSB UVC Streaming", // 5: UVC Interface "UVC Streaming", // 5: UVC Interface
}; };
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+

View File

@ -1652,18 +1652,11 @@ static const unsigned char frame_buffer[128 * (96 + 1) * 2] = {
0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80,
}; };
#else #endif
#if 1
// mpeg compressed data (not CFG_EXAMPLE_VIDEO_DISABLE_MJPEG) // mpeg compressed data (not CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
#define color_bar_0_jpg_len 511
#define color_bar_1_jpg_len 512
#define color_bar_2_jpg_len 511
#define color_bar_3_jpg_len 511
#define color_bar_4_jpg_len 511
#define color_bar_5_jpg_len 512
#define color_bar_6_jpg_len 511
#define color_bar_7_jpg_len 511
unsigned char color_bar_0_jpg[] = { unsigned char color_bar_0_jpg[] = {
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@ -1936,4 +1929,7 @@ unsigned char color_bar_7_jpg[] = {
0x42, 0x51, 0x59, 0x8c, 0xb1, 0x45, 0x00, 0x25, 0x15, 0x98, 0xc4, 0xa2, 0xb5, 0x32, 0x12, 0x8a, 0x42, 0x51, 0x59, 0x8c, 0xb1, 0x45, 0x00, 0x25, 0x15, 0x98, 0xc4, 0xa2, 0xb5, 0x32, 0x12, 0x8a,
0xcc, 0x63, 0x68, 0xad, 0x8d, 0x84, 0xa2, 0xb3, 0x18, 0x94, 0x56, 0xa6, 0x47, 0xff, 0xd9 0xcc, 0x63, 0x68, 0xad, 0x8d, 0x84, 0xa2, 0xb3, 0x18, 0x94, 0x56, 0xa6, 0x47, 0xff, 0xd9
}; };
#endif #endif

View File

@ -116,31 +116,27 @@ static unsigned frame_num[CFG_TUD_VIDEO_STREAMING] = {1};
static unsigned tx_busy = 0; static unsigned tx_busy = 0;
static unsigned interval_ms[CFG_TUD_VIDEO_STREAMING] = {1000 / FRAME_RATE}; static unsigned interval_ms[CFG_TUD_VIDEO_STREAMING] = {1000 / FRAME_RATE};
#ifdef CFG_EXAMPLE_VIDEO_READONLY
// For mcus that does not have enough SRAM for frame buffer, we use fixed frame data. // 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. // To further reduce the size, we use MJPEG format instead of YUY2.
#include "images.h" #include "images.h"
#if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
static struct { static struct {
uint32_t size; uint32_t size;
uint8_t const *buffer; uint8_t const *buffer;
} const frames[] = { } const framebuf_mjpeg[] = {
{color_bar_0_jpg_len, color_bar_0_jpg}, {sizeof(color_bar_0_jpg), color_bar_0_jpg},
{color_bar_1_jpg_len, color_bar_1_jpg}, {sizeof(color_bar_1_jpg), color_bar_1_jpg},
{color_bar_2_jpg_len, color_bar_2_jpg}, {sizeof(color_bar_2_jpg), color_bar_2_jpg},
{color_bar_3_jpg_len, color_bar_3_jpg}, {sizeof(color_bar_3_jpg), color_bar_3_jpg},
{color_bar_4_jpg_len, color_bar_4_jpg}, {sizeof(color_bar_4_jpg), color_bar_4_jpg},
{color_bar_5_jpg_len, color_bar_5_jpg}, {sizeof(color_bar_5_jpg), color_bar_5_jpg},
{color_bar_6_jpg_len, color_bar_6_jpg}, {sizeof(color_bar_6_jpg), color_bar_6_jpg},
{color_bar_7_jpg_len, color_bar_7_jpg}, {sizeof(color_bar_7_jpg), color_bar_7_jpg},
}; };
#endif
#else
// YUY2 frame buffer // YUY2 frame buffer
static uint8_t frame_buffer[2][FRAME_WIDTH * FRAME_HEIGHT * 16 / 8]; #define FRAMEBUF_SIZE (FRAME_WIDTH * FRAME_HEIGHT * 16 / 8)
static uint8_t framebuf_yuy2[FRAMEBUF_SIZE];
static void fill_color_bar(uint8_t* buffer, unsigned start_position) { static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
/* EBU color bars: https://stackoverflow.com/questions/6939422 */ /* EBU color bars: https://stackoverflow.com/questions/6939422 */
@ -179,7 +175,26 @@ static void fill_color_bar(uint8_t* buffer, unsigned start_position) {
} }
} }
#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
fill_color_bar(framebuf_yuy2, frame_num[idx]);
*fb = framebuf_yuy2;
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) { 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 start_ms[CFG_TUD_VIDEO_STREAMING] = {0, };
@ -191,22 +206,16 @@ void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
frame_num[idx] = 0; frame_num[idx] = 0;
return; return;
} }
void* fp;
size_t fb_size;
if (!(already_sent & (1u << idx))) { if (!(already_sent & (1u << idx))) {
already_sent |= 1u << idx; already_sent |= 1u << idx;
tx_busy |= 1u << idx; tx_busy |= 1u << idx;
start_ms[idx] = board_millis(); start_ms[idx] = board_millis();
#ifdef CFG_EXAMPLE_VIDEO_READONLY
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG) fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp);
tud_video_n_frame_xfer(ctl_idx, stm_idx, (void*)(uintptr_t)&frame_buffer[(frame_num[idx] % (FRAME_WIDTH / 2)) * 4], tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size);
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
#else
tud_video_n_frame_xfer(ctl_idx, stm_idx, (void*)(uintptr_t)frames[frame_num[idx] % 8].buffer, frames[frame_num[idx] % 8].size);
#endif
#else
fill_color_bar(frame_buffer[idx], frame_num[idx]);
tud_video_n_frame_xfer(ctl_idx, stm_idx, (void*) frame_buffer[idx], FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
#endif
} }
unsigned cur = board_millis(); unsigned cur = board_millis();
@ -215,17 +224,8 @@ void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
start_ms[idx] += interval_ms[idx]; start_ms[idx] += interval_ms[idx];
tx_busy |= 1u << idx; tx_busy |= 1u << idx;
#ifdef CFG_EXAMPLE_VIDEO_READONLY fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp);
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG) tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size);
tud_video_n_frame_xfer(ctl_idx, stm_idx, (void*)(uintptr_t)&frame_buffer[(frame_num[idx] % (FRAME_WIDTH / 2)) * 4],
FRAME_WIDTH * FRAME_HEIGHT * 16/8);
#else
tud_video_n_frame_xfer(ctl_idx, stm_idx, (void*)(uintptr_t)frames[frame_num[idx] % 8].buffer, frames[frame_num[idx] % 8].size);
#endif
#else
fill_color_bar(frame_buffer[idx], frame_num[idx]);
tud_video_n_frame_xfer(ctl_idx, stm_idx, (void*) frame_buffer[idx], FRAME_WIDTH * FRAME_HEIGHT * 16 / 8);
#endif
} }

View File

@ -55,13 +55,14 @@ enum {
// array of pointer to string descriptors // array of pointer to string descriptors
char const* string_desc_arr[] = { char const* string_desc_arr[] = {
(const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409) (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer "TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product "TinyUSB Device", // 2: Product
NULL, // 3: Serials will use unique ID if possible NULL, // 3: Serials will use unique ID if possible
"TinyUSB UVC Control 1", // 4: UVC Interface 1 "UVC Control 1", // 4: UVC Interface 1
"TinyUSB UVC Streaming 1", // 5: UVC Interface 1 "UVC Streaming 1", // 5: UVC Interface 1
"TinyUSB UVC Control 2", // 6: UVC Interface 2 "UVC Control 2", // 6: UVC Interface 2
"TinyUSB UVC Streaming 2", // 7: UVC Interface 2 "UVC Streaming 2", // 7: UVC Interface 2
}; };
//--------------------------------------------------------------------+ //--------------------------------------------------------------------+
@ -140,15 +141,8 @@ typedef struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED { typedef struct TU_ATTR_PACKED {
tusb_desc_interface_t itf; tusb_desc_interface_t itf;
tusb_desc_video_streaming_input_header_1byte_t header; tusb_desc_video_streaming_input_header_1byte_t header;
#if USE_MJPEG
tusb_desc_video_format_mjpeg_t format;
tusb_desc_video_frame_mjpeg_continuous_t frame;
#else
tusb_desc_video_format_uncompressed_t format; tusb_desc_video_format_uncompressed_t format;
tusb_desc_video_frame_uncompressed_continuous_t frame; tusb_desc_video_frame_uncompressed_continuous_t frame;
#endif
tusb_desc_video_streaming_color_matching_t color; tusb_desc_video_streaming_color_matching_t color;
#if USE_ISO_STREAMING #if USE_ISO_STREAMING
@ -157,15 +151,37 @@ typedef struct TU_ATTR_PACKED {
#endif #endif
tusb_desc_endpoint_t ep; tusb_desc_endpoint_t ep;
} uvc_streaming_desc_t; } 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 { typedef struct TU_ATTR_PACKED {
tusb_desc_configuration_t config; tusb_desc_configuration_t config;
struct TU_ATTR_PACKED { struct TU_ATTR_PACKED {
tusb_desc_interface_assoc_t iad; tusb_desc_interface_assoc_t iad;
uvc_control_desc_t video_control; uvc_control_desc_t video_control;
uvc_streaming_desc_t video_streaming; uvc_streaming_yuy2_desc_t video_streaming;
} uvc[2]; } 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; } uvc_cfg_desc_t;
const uvc_cfg_desc_t desc_fs_configuration = { const uvc_cfg_desc_t desc_fs_configuration = {
@ -180,369 +196,336 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.bmAttributes = TU_BIT(7), .bmAttributes = TU_BIT(7),
.bMaxPower = 100 / 2 .bMaxPower = 100 / 2
}, },
.uvc = { //------------- Stream 0: YUY2 -------------//
{ .uvc_yuy2 = {
.iad = { .iad = {
.bLength = sizeof(tusb_desc_interface_assoc_t), .bLength = sizeof(tusb_desc_interface_assoc_t),
.bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION, .bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION,
.bFirstInterface = ITF_NUM_VIDEO_CONTROL_1, .bFirstInterface = ITF_NUM_VIDEO_CONTROL_1,
.bInterfaceCount = 2, .bInterfaceCount = 2,
.bFunctionClass = TUSB_CLASS_VIDEO, .bFunctionClass = TUSB_CLASS_VIDEO,
.bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION, .bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION,
.bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED, .bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED,
.iFunction = 0 .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,
.video_control = { .bcdUVC = VIDEO_BCD_1_50,
.itf = { .wTotalLength = sizeof(uvc_control_desc_t) - sizeof(tusb_desc_interface_t), // CS VC descriptors only
.bLength = sizeof(tusb_desc_interface_t), .dwClockFrequency = UVC_CLOCK_FREQUENCY,
.bDescriptorType = TUSB_DESC_INTERFACE, .bInCollection = 1,
.baInterfaceNr = {ITF_NUM_VIDEO_STREAMING_1}
.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
}
}, },
.camera_terminal = {
.bLength = sizeof(tusb_desc_video_control_camera_terminal_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL,
.video_streaming = { .bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL,
.itf = { .wTerminalType = VIDEO_ITT_CAMERA,
.bLength = sizeof(tusb_desc_interface_t), .bAssocTerminal = 0,
.bDescriptorType = TUSB_DESC_INTERFACE, .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,
.bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_1, .bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
.bAlternateSetting = 0, .wTerminalType = VIDEO_TT_STREAMING,
.bNumEndpoints = CFG_TUD_VIDEO_STREAMING_BULK, // bulk 1, iso 0 .bAssocTerminal = 0,
.bInterfaceClass = TUSB_CLASS_VIDEO, .bSourceID = UVC_ENTITY_CAP_INPUT_TERMINAL,
.bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING, .iTerminal = 0
.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_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 = {
#if USE_MJPEG
.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,
#else
.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,
#endif
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0
},
.frame = {
#if USE_MJPEG
.bLength = sizeof(tusb_desc_video_frame_mjpeg_continuous_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_MJPEG,
#else
.bLength = sizeof(tusb_desc_video_frame_uncompressed_continuous_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED,
#endif
.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
}
} }
}, },
{
.iad = {
.bLength = sizeof(tusb_desc_interface_assoc_t),
.bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION,
.bFirstInterface = ITF_NUM_VIDEO_CONTROL_2, .video_streaming = {
.bInterfaceCount = 2, .itf = {
.bFunctionClass = TUSB_CLASS_VIDEO, .bLength = sizeof(tusb_desc_interface_t),
.bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION, .bDescriptorType = TUSB_DESC_INTERFACE,
.bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED,
.iFunction = 0 .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,
.video_control = { .bNumFormats = 1,
.itf = { .wTotalLength = sizeof(uvc_streaming_yuy2_desc_t) - sizeof(tusb_desc_interface_t)
.bLength = sizeof(tusb_desc_interface_t), - sizeof(tusb_desc_endpoint_t) -
.bDescriptorType = TUSB_DESC_INTERFACE, (USE_ISO_STREAMING ? sizeof(tusb_desc_interface_t) : 0), // CS VS descriptors only
.bEndpointAddress = EPNUM_VIDEO_IN_1,
.bInterfaceNumber = ITF_NUM_VIDEO_CONTROL_2, .bmInfo = 0,
.bAlternateSetting = 0, .bTerminalLink = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
.bNumEndpoints = 0, .bStillCaptureMethod = 0,
.bInterfaceClass = TUSB_CLASS_VIDEO, .bTriggerSupport = 0,
.bInterfaceSubClass = VIDEO_SUBCLASS_CONTROL, .bTriggerUsage = 0,
.bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, .bControlSize = 1,
.iInterface = STRID_UVC_CONTROL_2 .bmaControls = {0}
}, },
.header = { .format = {
.bLength = sizeof(tusb_desc_video_control_header_1itf_t), .bLength = sizeof(tusb_desc_video_format_uncompressed_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE, .bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VC_HEADER, .bDescriptorSubType = VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED,
.bFormatIndex = 1, // 1-based index
.bcdUVC = VIDEO_BCD_1_50, .bNumFrameDescriptors = 1,
.wTotalLength = sizeof(uvc_control_desc_t) - sizeof(tusb_desc_interface_t), // CS VC descriptors only .guidFormat = {TUD_VIDEO_GUID_YUY2},
.dwClockFrequency = UVC_CLOCK_FREQUENCY, .bBitsPerPixel = 16,
.bInCollection = 1, .bDefaultFrameIndex = 1,
.baInterfaceNr = { ITF_NUM_VIDEO_STREAMING_2 } .bAspectRatioX = 0,
}, .bAspectRatioY = 0,
.camera_terminal = { .bmInterlaceFlags = 0,
.bLength = sizeof(tusb_desc_video_control_camera_terminal_t), .bCopyProtect = 0
.bDescriptorType = TUSB_DESC_CS_INTERFACE, },
.bDescriptorSubType = VIDEO_CS_ITF_VC_INPUT_TERMINAL, .frame = {
.bLength = sizeof(tusb_desc_video_frame_uncompressed_continuous_t),
.bTerminalID = UVC_ENTITY_CAP_INPUT_TERMINAL, .bDescriptorType = TUSB_DESC_CS_INTERFACE,
.wTerminalType = VIDEO_ITT_CAMERA, .bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED,
.bAssocTerminal = 0, .bFrameIndex = 1, // 1-based index
.iTerminal = 0, .bmCapabilities = 0,
.wObjectiveFocalLengthMin = 0, .wWidth = FRAME_WIDTH,
.wObjectiveFocalLengthMax = 0, .wHeight = FRAME_HEIGHT,
.wOcularFocalLength = 0, .dwMinBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * 1,
.bControlSize = 3, .dwMaxBitRate = FRAME_WIDTH * FRAME_HEIGHT * 16 * FRAME_RATE,
.bmControls = { 0, 0, 0 } .dwMaxVideoFrameBufferSize = FRAME_WIDTH * FRAME_HEIGHT * 16 / 8,
}, .dwDefaultFrameInterval = 10000000 / FRAME_RATE,
.output_terminal = { .bFrameIntervalType = 0, // continuous
.bLength = sizeof(tusb_desc_video_control_output_terminal_t), .dwFrameInterval = {
.bDescriptorType = TUSB_DESC_CS_INTERFACE, 10000000 / FRAME_RATE, // min
.bDescriptorSubType = VIDEO_CS_ITF_VC_OUTPUT_TERMINAL, 10000000, // max
10000000 / FRAME_RATE // step
.bTerminalID = UVC_ENTITY_CAP_OUTPUT_TERMINAL,
.wTerminalType = VIDEO_TT_STREAMING,
.bAssocTerminal = 0,
.bSourceID = UVC_ENTITY_CAP_INPUT_TERMINAL,
.iTerminal = 0
} }
}, },
.color = {
.bLength = sizeof(tusb_desc_video_streaming_color_matching_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VS_COLORFORMAT,
.video_streaming = { .bColorPrimaries = VIDEO_COLOR_PRIMARIES_BT709,
.itf = { .bTransferCharacteristics = VIDEO_COLOR_XFER_CH_BT709,
.bLength = sizeof(tusb_desc_interface_t), .bMatrixCoefficients = VIDEO_COLOR_COEF_SMPTE170M
.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_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 = {
#if USE_MJPEG
.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,
#else
.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,
#endif
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
.bmInterlaceFlags = 0,
.bCopyProtect = 0
},
.frame = {
#if USE_MJPEG
.bLength = sizeof(tusb_desc_video_frame_mjpeg_continuous_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_MJPEG,
#else
.bLength = sizeof(tusb_desc_video_frame_uncompressed_continuous_t),
.bDescriptorType = TUSB_DESC_CS_INTERFACE,
.bDescriptorSubType = VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED,
#endif
.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 #if USE_ISO_STREAMING
.itf_alt = { .itf_alt = {
.bLength = sizeof(tusb_desc_interface_t), .bLength = sizeof(tusb_desc_interface_t),
.bDescriptorType = TUSB_DESC_INTERFACE, .bDescriptorType = TUSB_DESC_INTERFACE,
.bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_2, .bInterfaceNumber = ITF_NUM_VIDEO_STREAMING_1,
.bAlternateSetting = 1, .bAlternateSetting = 1,
.bNumEndpoints = 1, .bNumEndpoints = 1,
.bInterfaceClass = TUSB_CLASS_VIDEO, .bInterfaceClass = TUSB_CLASS_VIDEO,
.bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING, .bInterfaceSubClass = VIDEO_SUBCLASS_STREAMING,
.bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15, .bInterfaceProtocol = VIDEO_ITF_PROTOCOL_15,
.iInterface = STRID_UVC_STREAMING_2 .iInterface = STRID_UVC_STREAMING_1
}, },
#endif #endif
.ep = {
.bLength = sizeof(tusb_desc_endpoint_t),
.bDescriptorType = TUSB_DESC_ENDPOINT,
.ep = { .bEndpointAddress = EPNUM_VIDEO_IN_1,
.bLength = sizeof(tusb_desc_endpoint_t), .bmAttributes = {
.bDescriptorType = TUSB_DESC_ENDPOINT, .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,
.bEndpointAddress = EPNUM_VIDEO_IN_2, .bFirstInterface = ITF_NUM_VIDEO_CONTROL_2,
.bmAttributes = { .bInterfaceCount = 2,
.xfer = CFG_TUD_VIDEO_STREAMING_BULK ? TUSB_XFER_BULK : TUSB_XFER_ISOCHRONOUS, .bFunctionClass = TUSB_CLASS_VIDEO,
.sync = CFG_TUD_VIDEO_STREAMING_BULK ? 0 : 1 // asynchronous .bFunctionSubClass = VIDEO_SUBCLASS_INTERFACE_COLLECTION,
}, .bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED,
.wMaxPacketSize = CFG_TUD_VIDEO_STREAMING_BULK ? 64 : CFG_TUD_VIDEO_STREAMING_EP_BUFSIZE, .iFunction = 0
.bInterval = 1 },
.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
} }
} }
} }
@ -558,8 +541,8 @@ static uint8_t * get_hs_configuration_desc(void) {
desc_hs_configuration = desc_fs_configuration; desc_hs_configuration = desc_fs_configuration;
// change endpoint bulk size to 512 if bulk streaming // change endpoint bulk size to 512 if bulk streaming
if (CFG_TUD_VIDEO_STREAMING_BULK) { if (CFG_TUD_VIDEO_STREAMING_BULK) {
desc_hs_configuration.uvc[0].video_streaming.ep.wMaxPacketSize = 512; desc_hs_configuration.uvc_yuy2.video_streaming.ep.wMaxPacketSize = 512;
desc_hs_configuration.uvc[1].video_streaming.ep.wMaxPacketSize = 512; desc_hs_configuration.uvc_mpeg.video_streaming.ep.wMaxPacketSize = 512;
} }
} }
init = true; init = true;