diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c index 4a124c537..abc97a377 100644 --- a/examples/device/video_capture/src/main.c +++ b/examples/device/video_capture/src/main.c @@ -35,9 +35,6 @@ // MACRO CONSTANT TYPEDEF PROTYPES //--------------------------------------------------------------------+ -#define FRAME_WIDTH 128 -#define FRAME_HEIGHT 96 - /* Blink pattern * - 250 ms : device not mounted * - 1000 ms : device mounted @@ -115,11 +112,10 @@ static unsigned tx_busy = 0; void video_task(void) { static unsigned start_ms = 0; - static unsigned interval_ms = 100; - static unsigned frame_num = 0; + static unsigned interval_ms = 1000 / FRAME_RATE; static unsigned already_sent = 0; - if (!tud_video_n_streaming(0)) { + if (!tud_video_n_streaming(0, 0)) { already_sent = 0; current_frame = 0; return; @@ -128,7 +124,7 @@ void video_task(void) if (!already_sent) { already_sent = 1; start_ms = board_millis(); - tud_video_n_frame_xfer(0, 0, (void*)frames[current_frame], 128 * 96 * 12/8); + tud_video_n_frame_xfer(0, 0, (void*)frames[current_frame], FRAME_WIDTH * FRAME_HEIGHT * 12/8); } unsigned cur = board_millis(); @@ -136,18 +132,12 @@ void video_task(void) if (tx_busy) return; start_ms += interval_ms; - tud_video_n_frame_xfer(0, 0, (void*)frames[current_frame], 128 * 96 * 12/8); - - ++frame_num; - if (frame_num % 3) { - interval_ms = 66; - } else { - interval_ms = 67; - } + tud_video_n_frame_xfer(0, 0, (void*)frames[current_frame], FRAME_WIDTH * FRAME_HEIGHT * 12/8); } -int tud_video_frame_xfer_complete_cb(unsigned itf) +int tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { + (void)ctl_idx; (void)stm_idx; tx_busy = 0; /* flip buffer */ ++current_frame; diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h index 3958b85b6..fb194c55a 100644 --- a/examples/device/video_capture/src/tusb_config.h +++ b/examples/device/video_capture/src/tusb_config.h @@ -90,8 +90,10 @@ #define CFG_TUD_ENDPOINT0_SIZE 64 #endif +// video streaming endpoint size #define CFG_TUD_VIDEO_EP_BUFSIZE 256 +// The number of video streaming interfaces #define CFG_TUD_VIDEO_STREAMING 1 //------------- CLASS -------------// @@ -100,9 +102,19 @@ #define CFG_TUD_HID 0 #define CFG_TUD_MIDI 0 #define CFG_TUD_AUDIO 0 +// The number of video control interfaces #define CFG_TUD_VIDEO 1 #define CFG_TUD_VENDOR 0 + +//-------------------------------------------------------------------- +// APPLICATION CONFIGURATION +//-------------------------------------------------------------------- +#define FRAME_WIDTH 128 +#define FRAME_HEIGHT 96 +#define FRAME_RATE 10 + + #ifdef __cplusplus } #endif diff --git a/examples/device/video_capture/src/usb_descriptors.c b/examples/device/video_capture/src/usb_descriptors.c index 096ac4373..a9fa2c043 100644 --- a/examples/device/video_capture/src/usb_descriptors.c +++ b/examples/device/video_capture/src/usb_descriptors.c @@ -104,7 +104,7 @@ uint8_t const desc_fs_configuration[] = // Config number, interface count, string index, total length, attribute, power in mA TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0, 500), // IAD for Video Control - TUD_VIDEO_CAPTURE_DESCRIPTOR(4, EPNUM_VIDEO_IN, 128, 96, 10, 256) + TUD_VIDEO_CAPTURE_DESCRIPTOR(4, EPNUM_VIDEO_IN, FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE, CFG_TUD_VIDEO_EP_BUFSIZE) }; // Invoked when received GET CONFIGURATION DESCRIPTOR diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c index ad7057d30..d01bbb146 100644 --- a/src/class/video/video_device.c +++ b/src/class/video/video_device.c @@ -38,11 +38,6 @@ //--------------------------------------------------------------------+ // MACRO CONSTANT TYPEDEF //--------------------------------------------------------------------+ -typedef struct { - uint8_t num; - uint8_t alt; -} itf_setting_t; - typedef struct { tusb_desc_interface_t std; tusb_desc_cs_video_ctl_itf_hdr_t ctl; @@ -65,49 +60,41 @@ typedef struct TU_ATTR_PACKED { uint8_t bEntityId; } tusb_desc_cs_video_entity_itf_t; +/* video streaming interface */ typedef struct TU_ATTR_PACKED { - void const *beg; /* The head of the first video control interface descriptor */ - uint16_t length; /* Byte length of the video control interface descriptors */ - uint16_t offset; /* offset bytes for the current video control interface descripter */ - uint8_t error_code; /* error code set by previous transaction */ - uint8_t power_mode; /* current power mode */ -} videod_control_interface_t; - -typedef struct TU_ATTR_PACKED { - uint8_t index_vc; /* index of video control interface */ - uint8_t error_code;/* error code for video control/streaming interface. 0:control 1:streaming 2:streaming */ + uint8_t index_vc; /* index of bound video control interface */ + uint8_t index_vs; /* index from the video control interface */ + uint8_t error_code;/* error code */ struct { uint16_t beg; /* Offset of the beggining of video streaming interface descriptor */ uint16_t end; /* Offset of the end of video streaming interface descriptor */ uint16_t cur; /* Offset of the current settings */ - uint16_t ep[2]; /* Offset of endpoint descriptors */ + uint16_t ep[2]; /* Offset of endpoint descriptors. 0: streaming, 1: still capture */ } desc; - uint8_t *buffer; /* frame buffer. assume linear buffer. no support for stride access */ - uint32_t bufsize; /* frame buffer */ + uint32_t bufsize; /* frame buffer size */ uint32_t offset; /* offset for the next payload transfer */ uint32_t max_payload_transfer_size; - uint8_t ep_buf[CFG_TUD_VIDEO_EP_BUFSIZE]; + /*------------- From this point, data is not cleared by bus reset -------------*/ + CFG_TUSB_MEM_ALIGN uint8_t ep_buf[CFG_TUD_VIDEO_EP_BUFSIZE]; /* EP transfer buffer for streaming */ } videod_streaming_interface_t; +/* video control interface */ typedef struct TU_ATTR_PACKED { void const *beg; /* The head of the first video control interface descriptor */ uint16_t len; /* Byte length of the descriptors */ uint16_t cur; /* offset for current video control interface */ uint8_t stm[CFG_TUD_VIDEO_STREAMING]; /* Indices of streaming interface */ - uint8_t error_code; /* error code for video control/streaming interface. */ + uint8_t error_code; /* error code */ uint8_t power_mode; /*------------- From this point, data is not cleared by bus reset -------------*/ - - // Endpoint Transfer buffer - // CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE]; - // CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE]; - uint8_t ctl_buf[64]; + // + // CFG_TUSB_MEM_ALIGN uint8_t ctl_buf[64]; /* EP transfer buffer for control */ } videod_interface_t; -#define ITF_MEM_RESET_SIZE offsetof(videod_interface_t, ctl_buf) +#define ITF_STM_MEM_RESET_SIZE offsetof(videod_streaming_interface_t, ep_buf) //--------------------------------------------------------------------+ // INTERNAL OBJECT & FUNCTION DECLARATION @@ -121,14 +108,14 @@ static video_probe_and_commit_control_t const def_stm_settings = { .bmHint = 0, .bFormatIndex = 1, .bFrameIndex = 1, - .dwFrameInterval = (10000000/10), + .dwFrameInterval = (10000000/FRAME_RATE), .wKeyFrameRate = 1, .wPFrameRate = 0, .wCompQuality = 1, /* 1 to 10000 */ .wCompWindowSize = 1, /* Maybe it is match to GOP size */ .wDelay = 240, /* milliseconds */ - .dwMaxVideoFrameSize = 128 * 96 * 12 / 8, - .dwMaxPayloadTransferSize = ((128 * 96 * 12 / 8 * 10) + 999) / 1000 + 2, + .dwMaxVideoFrameSize = FRAME_WIDTH * FRAME_HEIGHT * 12 / 8, + .dwMaxPayloadTransferSize = ((FRAME_WIDTH * FRAME_HEIGHT * 12 / 8 * 10) + 999) / 1000 + 2, .dwClockFrequency = 27000000, /* same as MPEG-2 system time clock */ .bmFramingInfo = 0x3, .bPreferedVersion = 1, @@ -142,22 +129,33 @@ static video_probe_and_commit_control_t const def_stm_settings = { .bmLayoutPerStream = 0 }; +/* return bInterfaceNumber */ static inline uint8_t _desc_itfnum(void const *desc) { return ((uint8_t const*)desc)[2]; } +/* return bEndpointAddress */ static inline uint8_t _desc_ep_addr(void const *desc) { return ((uint8_t const*)desc)[2]; } +static videod_streaming_interface_t* _get_instance_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) +{ + videod_interface_t *ctl = &_videod_itf[ctl_idx]; + if (!ctl->beg) return NULL; + videod_streaming_interface_t *stm = &_videod_streaming_itf[ctl->stm[stm_idx]]; + if (!stm->desc.beg) return NULL; + return stm; +} + static tusb_desc_vc_itf_t const* _get_desc_vc(videod_interface_t const *self) { return (tusb_desc_vc_itf_t const *)(self->beg + self->cur); } -static tusb_desc_vs_itf_t const *_get_desc_vs(videod_streaming_interface_t const *self) +static tusb_desc_vs_itf_t const* _get_desc_vs(videod_streaming_interface_t const *self) { if (!self->desc.cur) return NULL; void const *desc = _videod_itf[self->index_vc].beg; @@ -230,7 +228,7 @@ static void const* _find_desc_itf(void const *beg, void const *end, uint_fast8_t static void const* _find_desc_ep(void const *beg, void const *end) { for (void const *cur = beg; cur < end; cur = tu_desc_next(cur)) { - uint8_t desc_type = tu_desc_type(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; } @@ -429,7 +427,8 @@ static int handle_video_ctl_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r if (!tud_control_xfer(rhport, request, &self->power_mode, sizeof(self->power_mode))) return VIDEO_UNKNOWN; } else if (stage == CONTROL_STAGE_ACK) { - if (tud_video_power_mode_cb) return tud_video_power_mode_cb(itf, self->power_mode); + if (tud_video_power_mode_cb) + return tud_video_power_mode_cb(itf, self->power_mode); } return VIDEO_NO_ERROR; case VIDEO_REQUEST_GET_CUR: @@ -553,7 +552,8 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r return VIDEO_UNKNOWN; } else if (stage == CONTROL_STAGE_ACK) { if (tud_video_probe_set_cb) - return tud_video_probe_set_cb(itf, (video_probe_and_commit_control_t const*)self->ep_buf); + return tud_video_probe_set_cb(self->index_vc, self->index_vs, + (video_probe_and_commit_control_t const*)self->ep_buf); } return VIDEO_NO_ERROR; case VIDEO_REQUEST_GET_CUR: @@ -601,7 +601,8 @@ static int handle_video_stm_cs_req(uint8_t rhport, uint8_t stage, tusb_control_r return VIDEO_UNKNOWN; } else if (stage == CONTROL_STAGE_ACK) { if (tud_video_commit_set_cb) - return tud_video_commit_set_cb(itf, (video_probe_and_commit_control_t const*)self->ep_buf); + return tud_video_commit_set_cb(self->index_vc, self->index_vs, + (video_probe_and_commit_control_t const*)self->ep_buf); } return VIDEO_NO_ERROR; case VIDEO_REQUEST_GET_CUR: @@ -654,33 +655,36 @@ static int handle_video_stm_req(uint8_t rhport, uint8_t stage, tusb_control_requ // APPLICATION API //--------------------------------------------------------------------+ -/* itf control interface number */ -bool tud_video_n_connected(uint8_t itf) +/* ctl_idx instance number of video control interface */ +bool tud_video_n_connected(uint_fast8_t ctl_idx) { - (void)itf; - // DTR (bit 0) active is considered as connected - return tud_ready(); -} - -/* itf streaming interface number */ -bool tud_video_n_streaming(uint8_t itf) -{ - videod_streaming_interface_t *stm = &_videod_streaming_itf[itf]; - if (stm->desc.ep[0]) + TU_ASSERT(ctl_idx < CFG_TUD_VIDEO); + videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, 0); + if (stm) return true; return false; } -bool tud_video_n_frame_xfer(uint8_t itf, uint32_t pts, void *buffer, size_t bufsize) +/* ctl_idx instance number of video control interface + * stm_idx index number of streaming interface as per video control */ +bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx) { - (void)pts; + TU_ASSERT(ctl_idx < CFG_TUD_VIDEO); + TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING); + videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx); + if (!stm || !stm->desc.ep[0]) + return false; + return true; +} +bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize) +{ + TU_ASSERT(ctl_idx < CFG_TUD_VIDEO); + TU_ASSERT(stm_idx < CFG_TUD_VIDEO_STREAMING); if (!buffer || !buffer) return false; - if (!tud_video_n_streaming(itf)) - return false; - videod_streaming_interface_t *stm = &_videod_streaming_itf[itf]; - if (stm->buffer) + videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx); + if (!stm || !stm->desc.ep[0] || stm->buffer) return false; /* find EP address */ @@ -713,26 +717,26 @@ bool tud_video_n_frame_xfer(uint8_t itf, uint32_t pts, void *buffer, size_t bufs //--------------------------------------------------------------------+ void videod_init(void) { - tu_memclr(_videod_itf, sizeof(_videod_itf)); - - for (unsigned i = 0; i < CFG_TUD_VIDEO; ++i) - { - // videod_interface_t* p_video = &_videod_itf[i]; - - // TODO + for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO; ++i) { + videod_interface_t* ctl = &_videod_itf[i]; + tu_memclr(ctl, sizeof(*ctl)); + } + for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { + videod_streaming_interface_t *stm = &_videod_streaming_itf[i]; + tu_memclr(stm, ITF_STM_MEM_RESET_SIZE); } } void videod_reset(uint8_t rhport) { (void) rhport; - - for (unsigned i = 0; i < CFG_TUD_VIDEO; ++i) - { - videod_interface_t* p_video = &_videod_itf[i]; - - // TODO - tu_memclr(p_video, ITF_MEM_RESET_SIZE); + for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO; ++i) { + videod_interface_t* ctl = &_videod_itf[i]; + tu_memclr(ctl, sizeof(*ctl)); + } + for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { + videod_streaming_interface_t *stm = &_videod_streaming_itf[i]; + tu_memclr(stm, ITF_STM_MEM_RESET_SIZE); } } @@ -744,14 +748,14 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin /* Find available interface */ videod_interface_t *self = NULL; - uint_fast8_t itf; - for (itf = 0; itf < CFG_TUD_VIDEO; ++itf) { - if (_videod_itf[itf].beg) + uint_fast8_t ctl_idx; + for (ctl_idx = 0; ctl_idx < CFG_TUD_VIDEO; ++ctl_idx) { + if (_videod_itf[ctl_idx].beg) continue; - self = &_videod_itf[itf]; + self = &_videod_itf[ctl_idx]; break; } - TU_ASSERT(itf < CFG_TUD_VIDEO, 0); + TU_ASSERT(ctl_idx < CFG_TUD_VIDEO, 0); void const *end = (void const*)itf_desc + max_len; self->beg = itf_desc; @@ -760,21 +764,22 @@ uint16_t videod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin if (!_open_vc_itf(rhport, self, 0)) return 0; tusb_desc_vc_itf_t const *vc = _get_desc_vc(self); - uint_fast8_t bInCollection = vc->ctl.bInCollection; + uint_fast8_t bInCollection = vc->ctl.bInCollection; /* Find the end of the video interface descriptor */ void const *cur = _next_desc_itf(itf_desc, end); - for (uint_fast8_t i = 0; i < bInCollection; ++i) { + for (uint_fast8_t stm_idx = 0; stm_idx < bInCollection; ++stm_idx) { videod_streaming_interface_t *stm = NULL; /* find free streaming interface handle */ - for (uint_fast8_t j = 0; j < TU_ARRAY_SIZE(_videod_streaming_itf); ++j) { + for (uint_fast8_t i = 0; i < CFG_TUD_VIDEO_STREAMING; ++i) { if (_videod_streaming_itf[i].desc.beg) continue; stm = &_videod_streaming_itf[i]; - self->stm[i] = j; + self->stm[stm_idx] = i; break; } TU_ASSERT(stm, 0); - stm->index_vc = itf; + stm->index_vc = ctl_idx; + stm->index_vs = stm_idx; stm->desc.beg = (uintptr_t)cur - (uintptr_t)itf_desc; cur = _next_desc_itf(cur, end); stm->desc.end = (uintptr_t)cur - (uintptr_t)itf_desc; @@ -832,12 +837,14 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 /* find streaming handle */ uint_fast8_t itf; + videod_interface_t *ctl; videod_streaming_interface_t *stm; for (itf = 0; itf < CFG_TUD_VIDEO_STREAMING; ++itf) { stm = &_videod_streaming_itf[itf]; uint_fast16_t const ep_ofs = stm->desc.ep[0]; if (!ep_ofs) continue; - void const *desc = _videod_itf[stm->index_vc].beg; + ctl = &_videod_itf[stm->index_vc]; + void const *desc = ctl->beg; if (ep_addr == _desc_ep_addr(desc + ep_ofs)) break; } @@ -852,7 +859,7 @@ bool videod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint3 stm->bufsize = 0; stm->offset = 0; if (tud_video_frame_xfer_complete_cb) - tud_video_frame_xfer_complete_cb(itf); + tud_video_frame_xfer_complete_cb(stm->index_vc, stm->index_vs); } return true; } diff --git a/src/class/video/video_device.h b/src/class/video/video_device.h index acf4c4d2b..e6f53fed8 100644 --- a/src/class/video/video_device.h +++ b/src/class/video/video_device.h @@ -39,15 +39,19 @@ extern "C" { // Application API (Multiple Ports) // CFG_TUD_VIDEO > 1 //--------------------------------------------------------------------+ -bool tud_video_n_streaming(uint8_t itf); +/* ctl_idx instance number of control interface + * stm_idx instance number of streaming interface */ +bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx); -/* itf instance number of streaming interface */ -bool tud_video_n_frame_xfer(uint8_t itf, uint32_t pts, void *buffer, size_t bufsize); +/* ctl_idx instance number of control interface + * stm_idx instance number of streaming interface */ +bool tud_video_n_frame_xfer(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, void *buffer, size_t bufsize); /*------------- Optional callbacks -------------*/ -/* itf instance number of streaming interface */ -TU_ATTR_WEAK int tud_video_frame_xfer_complete_cb(unsigned itf); +/* ctl_idx instance number of control interface + * stm_idx instance number of streaming interface */ +TU_ATTR_WEAK int tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx); //--------------------------------------------------------------------+ // Application Callback API (weak is optional) @@ -55,13 +59,13 @@ TU_ATTR_WEAK int tud_video_frame_xfer_complete_cb(unsigned itf); /* Invoked when SET POWER_MODE request received to * @return video_error_code_t */ -TU_ATTR_WEAK int tud_video_power_mode_cb(unsigned itf, uint8_t power_mod); +TU_ATTR_WEAK int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod); /* @return video_error_code_t */ -TU_ATTR_WEAK int tud_video_probe_set_cb(unsigned itf, video_probe_and_commit_control_t const *settings); +TU_ATTR_WEAK int tud_video_probe_set_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, video_probe_and_commit_control_t const *settings); /* @return video_error_code_t */ -TU_ATTR_WEAK int tud_video_commit_set_cb(unsigned itf, video_probe_and_commit_control_t const *settings); +TU_ATTR_WEAK int tud_video_commit_set_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, video_probe_and_commit_control_t const *settings); //--------------------------------------------------------------------+ // INTERNAL USBD-CLASS DRIVER API //--------------------------------------------------------------------+