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
4 changed files with 390 additions and 411 deletions

View File

@@ -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
};
//--------------------------------------------------------------------+

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,
};
#else
#endif
#if 1
// 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[] = {
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,
@@ -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,
0xcc, 0x63, 0x68, 0xad, 0x8d, 0x84, 0xa2, 0xb3, 0x18, 0x94, 0x56, 0xa6, 0x47, 0xff, 0xd9
};
#endif

View File

@@ -116,31 +116,27 @@ 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};
#ifdef CFG_EXAMPLE_VIDEO_READONLY
// 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"
#if !defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
static struct {
uint32_t size;
uint8_t const *buffer;
} const frames[] = {
{color_bar_0_jpg_len, color_bar_0_jpg},
{color_bar_1_jpg_len, color_bar_1_jpg},
{color_bar_2_jpg_len, color_bar_2_jpg},
{color_bar_3_jpg_len, color_bar_3_jpg},
{color_bar_4_jpg_len, color_bar_4_jpg},
{color_bar_5_jpg_len, color_bar_5_jpg},
{color_bar_6_jpg_len, color_bar_6_jpg},
{color_bar_7_jpg_len, color_bar_7_jpg},
} 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},
};
#endif
#else
// 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) {
/* 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) {
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;
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();
#ifdef CFG_EXAMPLE_VIDEO_READONLY
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
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
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();
@@ -215,17 +224,8 @@ void video_send_frame(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) {
start_ms[idx] += interval_ms[idx];
tx_busy |= 1u << idx;
#ifdef CFG_EXAMPLE_VIDEO_READONLY
#if defined(CFG_EXAMPLE_VIDEO_DISABLE_MJPEG)
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
fb_size = get_framebuf(ctl_idx, stm_idx, frame_num[idx], &fp);
tud_video_n_frame_xfer(ctl_idx, stm_idx, fp, fb_size);
}

View File

@@ -58,10 +58,11 @@ char const* string_desc_arr[] = {
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
NULL, // 3: Serials will use unique ID if possible
"TinyUSB UVC Control 1", // 4: UVC Interface 1
"TinyUSB UVC Streaming 1", // 5: UVC Interface 1
"TinyUSB UVC Control 2", // 6: UVC Interface 2
"TinyUSB UVC Streaming 2", // 7: UVC Interface 2
"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
};
//--------------------------------------------------------------------+
@@ -140,15 +141,8 @@ typedef struct TU_ATTR_PACKED {
typedef struct TU_ATTR_PACKED {
tusb_desc_interface_t itf;
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_frame_uncompressed_continuous_t frame;
#endif
tusb_desc_video_streaming_color_matching_t color;
#if USE_ISO_STREAMING
@@ -157,15 +151,37 @@ typedef struct TU_ATTR_PACKED {
#endif
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 {
tusb_desc_configuration_t config;
struct TU_ATTR_PACKED {
tusb_desc_interface_assoc_t iad;
uvc_control_desc_t video_control;
uvc_streaming_desc_t video_streaming;
} uvc[2];
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 = {
@@ -180,8 +196,8 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.bmAttributes = TU_BIT(7),
.bMaxPower = 100 / 2
},
.uvc = {
{
//------------- Stream 0: YUY2 -------------//
.uvc_yuy2 = {
.iad = {
.bLength = sizeof(tusb_desc_interface_assoc_t),
.bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION,
@@ -193,7 +209,6 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.bFunctionProtocol = VIDEO_ITF_PROTOCOL_UNDEFINED,
.iFunction = 0
},
.video_control = {
.itf = {
.bLength = sizeof(tusb_desc_interface_t),
@@ -265,8 +280,9 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.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
.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,
@@ -277,14 +293,6 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.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,
@@ -292,7 +300,6 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.bNumFrameDescriptors = 1,
.guidFormat = {TUD_VIDEO_GUID_YUY2},
.bBitsPerPixel = 16,
#endif
.bDefaultFrameIndex = 1,
.bAspectRatioX = 0,
.bAspectRatioY = 0,
@@ -300,15 +307,9 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.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,
@@ -348,7 +349,6 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.iInterface = STRID_UVC_STREAMING_1
},
#endif
.ep = {
.bLength = sizeof(tusb_desc_endpoint_t),
.bDescriptorType = TUSB_DESC_ENDPOINT,
@@ -363,7 +363,8 @@ const uvc_cfg_desc_t desc_fs_configuration = {
}
}
},
{
//------------- Stream 1: MPEG -------------//
.uvc_mpeg = {
.iad = {
.bLength = sizeof(tusb_desc_interface_assoc_t),
.bDescriptorType = TUSB_DESC_INTERFACE_ASSOCIATION,
@@ -447,7 +448,7 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.bDescriptorSubType = VIDEO_CS_ITF_VS_INPUT_HEADER,
.bNumFormats = 1,
.wTotalLength = sizeof(uvc_streaming_desc_t) - sizeof(tusb_desc_interface_t)
.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,
@@ -459,22 +460,12 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.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,
@@ -482,15 +473,9 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.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,
@@ -530,7 +515,6 @@ const uvc_cfg_desc_t desc_fs_configuration = {
.iInterface = STRID_UVC_STREAMING_2
},
#endif
.ep = {
.bLength = sizeof(tusb_desc_endpoint_t),
.bDescriptorType = TUSB_DESC_ENDPOINT,
@@ -545,7 +529,6 @@ const uvc_cfg_desc_t desc_fs_configuration = {
}
}
}
}
};
#if TUD_OPT_HIGH_SPEED
@@ -558,8 +541,8 @@ static uint8_t * get_hs_configuration_desc(void) {
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[0].video_streaming.ep.wMaxPacketSize = 512;
desc_hs_configuration.uvc[1].video_streaming.ep.wMaxPacketSize = 512;
desc_hs_configuration.uvc_yuy2.video_streaming.ep.wMaxPacketSize = 512;
desc_hs_configuration.uvc_mpeg.video_streaming.ep.wMaxPacketSize = 512;
}
}
init = true;