From e80714740ceec841ff4964fe555870cf3ee4f6df Mon Sep 17 00:00:00 2001 From: kkitayam <45088311+kkitayam@users.noreply.github.com> Date: Tue, 12 Oct 2021 23:29:58 +0900 Subject: Change images to MJPEG --- src/class/video/video.h | 79 +++++++++++++++++++ src/class/video/video_device.c | 175 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 233 insertions(+), 21 deletions(-) (limited to 'src/class') diff --git a/src/class/video/video.h b/src/class/video/video.h index 844746546..e8227ea60 100644 --- a/src/class/video/video.h +++ b/src/class/video/video.h @@ -302,6 +302,45 @@ typedef struct TU_ATTR_PACKED { uint8_t bCopyProtect; } tusb_desc_cs_video_fmt_uncompressed_t; +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + uint8_t bmFlags; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; +} tusb_desc_cs_video_fmt_mjpeg_t; + +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint32_t dwMaxVideoFrameBufferSize; /* deprecated */ + uint8_t bFormatType; +} tusb_desc_cs_video_fmt_dv_t; + +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + uint8_t bNumFrameDescriptors; + uint8_t guidFormat[16]; + uint8_t bBitsPerPixel; + uint8_t bDefaultFrameIndex; + uint8_t bAspectRatioX; + uint8_t bAspectRatioY; + uint8_t bmInterlaceFlags; + uint8_t bCopyProtect; + uint8_t bVaribaleSize; +} tusb_desc_cs_video_fmt_frame_based_t; + typedef struct TU_ATTR_PACKED { uint8_t bLength; uint8_t bDescriptorType; @@ -318,6 +357,24 @@ typedef struct TU_ATTR_PACKED { uint32_t dwFrameInterval[]; } tusb_desc_cs_video_frm_uncompressed_t; +typedef tusb_desc_cs_video_frm_uncompressed_t tusb_desc_cs_video_frm_mjpeg_t; + +typedef struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFrameIndex; + uint8_t bmCapabilities; + uint16_t wWidth; + uint16_t wHeight; + uint32_t dwMinBitRate; + uint32_t dwMaxBitRate; + uint32_t dwDefaultFrameInterval; + uint8_t bFrameIntervalType; + uint32_t dwBytesPerLine; + uint32_t dwFrameInterval[]; +} tusb_desc_cs_video_frm_frame_based_t; + //--------------------------------------------------------------------+ // Requests //--------------------------------------------------------------------+ @@ -378,8 +435,11 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c #define TUD_VIDEO_DESC_CS_VS_IN_LEN 13 #define TUD_VIDEO_DESC_CS_VS_OUT_LEN 9 #define TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN 27 +#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN 11 #define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN 38 #define TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_DISC_LEN 26 +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN 38 +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN 26 #define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN 6 /* 2.2 compression formats */ @@ -462,6 +522,25 @@ TU_VERIFY_STATIC( sizeof(video_probe_and_commit_control_t) == 48, "size is not c _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__ +/* Motion-JPEG 3.1.1 Table 3-1 */ +#define TUD_VIDEO_DESC_CS_VS_FMT_MJPEG(_fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp) \ + TUD_VIDEO_DESC_CS_VS_FMT_MJPEG_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_MJPEG, \ + _fmtidx, _numfrmdesc, _fixed_sz, _frmidx, _asrx, _asry, _interlace, _cp + +/* Motion-JPEG 3.1.1 Table 3-2 and 3-3 */ +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, _minfrminterval, _maxfrminterval, _frmintervalstep) \ + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_CONT_LEN, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_MJPEG, \ + _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ + U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), 0, \ + U32_TO_U8S_LE(_minfrminterval), U32_TO_U8S_LE(_maxfrminterval), U32_TO_U8S_LE(_frmintervalstep) + +/* Motion-JPEG 3.1.1 Table 3-2 and 3-4 */ +#define TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC(_frmidx, _cap, _width, _height, _minbr, _maxbr, _maxfrmbufsz, _frminterval, ...) \ + TUD_VIDEO_DESC_CS_VS_FRM_MJPEG_DISC_LEN + (TU_ARGS_NUM(__VA_ARGS__)) * 4, \ + TUSB_DESC_CS_INTERFACE, VIDEO_CS_VS_INTERFACE_FRAME_MJPEG, \ + _frmidx, _cap, U16_TO_U8S_LE(_width), U16_TO_U8S_LE(_height), U32_TO_U8S_LE(_minbr), U32_TO_U8S_LE(_maxbr), \ + U32_TO_U8S_LE(_maxfrmbufsz), U32_TO_U8S_LE(_frminterval), (TU_ARGS_NUM(__VA_ARGS__)), __VA_ARGS__ + /* 3.9.2.6 */ #define TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(_color, _trns, _mat) \ TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN, \ diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index 7636db5fe..39e2e9aac 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -29,6 +29,8 @@ #if (CFG_TUD_ENABLED && CFG_TUD_VIDEO && CFG_TUD_VIDEO_STREAMING) +#include + #include "device/usbd.h" #include "device/usbd_pvt.h" @@ -59,6 +61,30 @@ typedef struct TU_ATTR_PACKED { uint8_t bEntityId; } tusb_desc_cs_video_entity_itf_t; +typedef union { + struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFormatIndex; + }; + tusb_desc_cs_video_fmt_uncompressed_t uncompressed; + tusb_desc_cs_video_fmt_mjpeg_t mjpeg; + tusb_desc_cs_video_fmt_frame_based_t frame_based; +} tusb_desc_cs_video_fmt_t; + +typedef union { + struct TU_ATTR_PACKED { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bDescriptorSubType; + uint8_t bFrameIndex; + }; + tusb_desc_cs_video_frm_uncompressed_t uncompressed; + tusb_desc_cs_video_frm_mjpeg_t mjpeg; + tusb_desc_cs_video_frm_frame_based_t frame_based; +} tusb_desc_cs_video_frm_t; + /* video streaming interface */ typedef struct TU_ATTR_PACKED { uint8_t index_vc; /* index of bound video control interface */ @@ -167,6 +193,72 @@ static void const* _find_desc(void const *beg, void const *end, uint_fast8_t des return cur; } +/** Find the first descriptor of a given types + * + * @param[in] beg The head of descriptor byte array. + * @param[in] end The tail of descriptor byte array. + * @param[in] ... The target descriptor types. The last type must be 0. + * + * @return The pointer for interface descriptor. + * @retval end did not found interface descriptor */ +static void const* _find_desc_n(void const *beg, void const *end, ...) +{ + uint_fast8_t target = 0; + void const *cur; + for (cur = beg; cur < end && !target; cur = tu_desc_next(cur)) { + uint_fast8_t actual = tu_desc_type(cur); + va_list ap; + va_start(ap, end); + do { + target = va_arg(ap, unsigned); + } while (target && target != actual); + va_end(ap); + } + return cur; +} + +/** Find the first descriptor specified by the arguments + * + * @param[in] beg The head of descriptor byte array. + * @param[in] end The tail of descriptor byte array. + * @param[in] sub_types The target bDescriptorSubtype list. The last elemnt must be 0. + * + * @return The pointer for interface descriptor. + * @retval end did not found interface descriptor */ +static void const* _find_desc_cs(void const *beg, void const *end, uint8_t const *sub_types) +{ + uint_fast8_t target = 0; + void const *cur; + for (cur = beg; cur < end && !target; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { + uint_fast8_t actual = ((uint8_t const *)cur)[2]; + uint8_t const *p = sub_types; + do { + target = *p++; + } while (target && target != actual); + } + return cur; +} + +/** Find the first descriptor specified by the arguments + * + * @param[in] beg The head of descriptor byte array. + * @param[in] end The tail of descriptor byte array. + * @param[in] desc_type The target descriptor type + * @param[in] element_0 The target element following the desc_type + * + * @return The pointer for interface descriptor. + * @retval end did not found interface descriptor */ +static void const* _find_desc_2(void const *beg, void const *end, + uint_fast8_t desc_type, uint_fast8_t element_0) +{ + for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, desc_type)) { + uint8_t const *p = (uint8_t const *)cur; + if (p[2] == element_0) return cur; + cur = tu_desc_next(cur); + } + return end; +} + /** Find the first descriptor specified by the arguments * * @param[in] beg The head of descriptor byte array. @@ -235,14 +327,19 @@ static inline void const* _find_desc_itf(void const *beg, void const *end, uint_ * @retval end did not found endpoint descriptor */ static void const* _find_desc_ep(void const *beg, void const *end) { - for (void const *cur = beg; cur < end; cur = tu_desc_next(cur)) { - uint_fast8_t desc_type = tu_desc_type(cur); - if (TUSB_DESC_ENDPOINT == desc_type) return cur; - if (TUSB_DESC_INTERFACE == desc_type) break; - } + void const *cur = _find_desc_n(beg, end, TUSB_DESC_ENDPOINT, TUSB_DESC_INTERFACE, 0); + if ((cur < end) && (TUSB_DESC_ENDPOINT == tu_desc_type(cur))) + return cur; return end; } +/** Return the end of the video control descriptor. */ +static inline void const* _end_of_control_descriptor(void const *desc) +{ + tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const *)desc; + return desc + vc->std.bLength + vc->ctl.wTotalLength; +} + /** Find the first entity descriptor with the entity ID * specified by the argument belonging to the current video control descriptor. * @@ -253,10 +350,8 @@ static void const* _find_desc_ep(void const *beg, void const *end) * @retval end did not found interface descriptor */ static void const* _find_desc_entity(void const *desc, uint_fast8_t entityid) { - tusb_desc_vc_itf_t const *vc = (tusb_desc_vc_itf_t const*)desc; - void const *beg = vc; - void const *end = beg + vc->std.bLength + vc->ctl.wTotalLength; - for (void const *cur = beg; cur < end; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { + void const *end = _end_of_control_descriptor(desc); + for (void const *cur = desc; cur < end; cur = _find_desc(cur, end, TUSB_DESC_CS_INTERFACE)) { tusb_desc_cs_video_entity_itf_t const *itf = (tusb_desc_cs_video_entity_itf_t const *)cur; if ((VIDEO_CS_ITF_VC_INPUT_TERMINAL <= itf->bDescriptorSubtype && itf->bDescriptorSubtype < VIDEO_CS_ITF_VC_MAX) @@ -276,17 +371,38 @@ static inline void const* _end_of_streaming_descriptor(void const *desc) } /** Find the first format descriptor with the specified format number. */ -static inline tusb_desc_cs_video_fmt_uncompressed_t const *_find_desc_format(void const *beg, void const *end, uint_fast8_t fmtnum) +static inline void const *_find_desc_format(void const *beg, void const *end, uint_fast8_t fmtnum) { - return (tusb_desc_cs_video_fmt_uncompressed_t const*) - _find_desc_3(beg, end, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, fmtnum); + static uint8_t const sub_types[] = { + VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED, + VIDEO_CS_ITF_VS_FORMAT_MJPEG, + VIDEO_CS_ITF_VS_FORMAT_DV, + VIDEO_CS_ITF_VS_FORMAT_FRAME_BASED, + 0 + }; + while (true) { + uint8_t const *cur = _find_desc_cs(beg, end, sub_types); + if ((end == cur) || (fmtnum == cur[3])) + return cur; + cur = tu_desc_next(cur); + } } /** Find the first frame descriptor with the specified format number. */ -static inline tusb_desc_cs_video_frm_uncompressed_t const *_find_desc_frame(void const *beg, void const *end, uint_fast8_t frmnum) +static inline void const *_find_desc_frame(void const *beg, void const *end, uint_fast8_t frmnum) { - return (tusb_desc_cs_video_frm_uncompressed_t const*) - _find_desc_3(beg, end, TUSB_DESC_CS_INTERFACE, VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, frmnum); + static uint8_t const sub_types[] = { + VIDEO_CS_ITF_VS_FRAME_UNCOMPRESSED, + VIDEO_CS_ITF_VS_FRAME_MJPEG, + VIDEO_CS_ITF_VS_FRAME_FRAME_BASED, + 0 + }; + while (true) { + uint8_t const *cur = _find_desc_cs(beg, end, sub_types); + if ((end == cur) || (frmnum == ((uint8_t const *)cur)[3])) + return cur; + cur = tu_desc_next(cur); + } } /** Set uniquely determined values to variables that have not been set @@ -307,7 +423,6 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm /* Set the parameters determined by the format */ param->wKeyFrameRate = 1; param->wPFrameRate = 0; - param->wCompQuality = 1; /* 1 to 10000 */ param->wCompWindowSize = 1; /* GOP size? */ param->wDelay = 0; /* milliseconds */ param->dwClockFrequency = 27000000; /* same as MPEG-2 system time clock */ @@ -319,8 +434,18 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm param->bBitDepthLuma = 8; void const *end = _end_of_streaming_descriptor(vs); - tusb_desc_cs_video_fmt_uncompressed_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); + tusb_desc_cs_video_fmt_t const *fmt = _find_desc_format(tu_desc_next(vs), end, fmtnum); TU_ASSERT(fmt != end); + + switch (fmt->bDescriptorSubtype) { + case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED: + param->wCompQuality = 1; /* 1 to 10000 */ + break; + case VIDEO_CS_ITF_VS_FORMAT_MJPEG; + break; + default: return false; + } + uint_fast8_t frmnum = param->bFrameIndex; TU_ASSERT(frmnum <= fmt->bNumFrameDescriptors); if (!frmnum) { @@ -328,13 +453,21 @@ static bool _update_streaming_parameters(videod_streaming_interface_t const *stm frmnum = 1; param->bFrameIndex = 1; } - tusb_desc_cs_video_frm_uncompressed_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); + tusb_desc_cs_video_frm_t const *frm = _find_desc_frame(tu_desc_next(fmt), end, frmnum); TU_ASSERT(frm != end); /* Set the parameters determined by the frame */ uint_fast32_t frame_size = param->dwMaxVideoFrameSize; if (!frame_size) { - frame_size = (uint_fast32_t)frm->wWidth * frm->wHeight * fmt->bBitsPerPixel / 8; + switch (fmt->bDescriptorSubtype) { + case VIDEO_CS_ITF_VS_FORMAT_UNCOMPRESSED: + frame_size = (uint_fast32_t)frm->uncompressed.wWidth * frm->uncompressed.wHeight * fmt->uncompressed.bBitsPerPixel / 8; + break; + case VIDEO_CS_ITF_VS_FORMAT_MJPEG; + frame_size = (uint_fast32_t)frm->mjpeg.wWidth * frm->mjpeg.wHeight * 16 / 8; /* YUV422 */ + break; + default: break; + } param->dwMaxVideoFrameSize = frame_size; } @@ -487,7 +620,7 @@ static bool _close_vc_itf(uint8_t rhport, videod_interface_t *self) /* The next descriptor after the class-specific VC interface header descriptor. */ void const *cur = (void const*)vc + vc->std.bLength + vc->ctl.bLength; /* The end of the video control interface descriptor. */ - void const *end = (void const*)vc + vc->std.bLength + vc->ctl.wTotalLength; + void const *end = _end_of_control_descriptor(vc); if (vc->std.bNumEndpoints) { /* Find the notification endpoint descriptor. */ cur = _find_desc(cur, end, TUSB_DESC_ENDPOINT); @@ -519,7 +652,7 @@ static bool _open_vc_itf(uint8_t rhport, videod_interface_t *self, uint_fast8_t TU_ASSERT(vc->ctl.bInCollection <= CFG_TUD_VIDEO_STREAMING); /* Update to point the end of the video control interface descriptor. */ - end = cur + vc->std.bLength + vc->ctl.wTotalLength; + end = _end_of_control_descriptor(cur); /* Advance to the next descriptor after the class-specific VC interface header descriptor. */ cur += vc->std.bLength + vc->ctl.bLength; TU_LOG2(" bNumEndpoints %d\n", vc->std.bNumEndpoints); -- cgit v1.3.1