Skip to content

Commit 6790bc6

Browse files
committed
Initial UVC support [not working yet]
1 parent ed53b6c commit 6790bc6

File tree

5 files changed

+306
-1
lines changed

5 files changed

+306
-1
lines changed

CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ set(LIBRARY_SRCS
8686
libraries/USB/src/USBHIDSystemControl.cpp
8787
libraries/USB/src/USBHIDVendor.cpp
8888
libraries/USB/src/USBVendor.cpp
89+
libraries/USB/src/USBVideo.cpp
8990
libraries/WebServer/src/WebServer.cpp
9091
libraries/WebServer/src/Parsing.cpp
9192
libraries/WebServer/src/detail/mimetable.cpp

cores/esp32/esp32-hal-tinyusb.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ static void usb_device_task(void *param) {
554554
/*
555555
* PUBLIC API
556556
* */
557-
static const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "CDC", "MIDI", "CUSTOM"};
557+
static const char *tinyusb_interface_names[USB_INTERFACE_MAX] = {"MSC", "DFU", "HID", "VENDOR", "VIDEO", "CDC", "MIDI", "CUSTOM"};
558558

559559
static bool tinyusb_is_initialized = false;
560560

cores/esp32/esp32-hal-tinyusb.h

+1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ typedef enum {
8686
USB_INTERFACE_DFU,
8787
USB_INTERFACE_HID,
8888
USB_INTERFACE_VENDOR,
89+
USB_INTERFACE_VIDEO,
8990
USB_INTERFACE_CDC,
9091
USB_INTERFACE_MIDI,
9192
USB_INTERFACE_CUSTOM,

libraries/USB/src/USBVideo.cpp

+250
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
// Copyright 2015-2021 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
#include "USBVideo.h"
15+
16+
#if CONFIG_TINYUSB_VIDEO_ENABLED
17+
18+
#include "esp32-hal-tinyusb.h"
19+
20+
/* Time stamp base clock. It is a deprecated parameter. */
21+
#define UVC_CLOCK_FREQUENCY 27000000
22+
/* video capture path */
23+
#define UVC_ENTITY_CAP_INPUT_TERMINAL 0x01
24+
#define UVC_ENTITY_CAP_OUTPUT_TERMINAL 0x02
25+
26+
#define TUD_VIDEO_CAPTURE_DESC_LEN (\
27+
TUD_VIDEO_DESC_IAD_LEN\
28+
/* control */\
29+
+ TUD_VIDEO_DESC_STD_VC_LEN\
30+
+ (TUD_VIDEO_DESC_CS_VC_LEN + 1/*bInCollection*/)\
31+
+ TUD_VIDEO_DESC_INPUT_TERM_LEN\
32+
+ TUD_VIDEO_DESC_OUTPUT_TERM_LEN\
33+
/* Interface 1, Alternate 0 */\
34+
+ TUD_VIDEO_DESC_STD_VS_LEN\
35+
+ (TUD_VIDEO_DESC_CS_VS_IN_LEN + 1/*bNumFormats x bControlSize*/)\
36+
+ TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\
37+
+ TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\
38+
+ TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN\
39+
/* Interface 1, Alternate 1 */\
40+
+ TUD_VIDEO_DESC_STD_VS_LEN\
41+
+ 7/* Endpoint */\
42+
)
43+
44+
/* Windows support YUY2 and NV12
45+
* https://docs.microsoft.com/en-us/windows-hardware/drivers/stream/usb-video-class-driver-overview */
46+
47+
#define TUD_VIDEO_DESC_CS_VS_FMT_YUY2(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \
48+
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_YUY2, 16, _frmidx, _asrx, _asry, _interlace, _cp)
49+
#define TUD_VIDEO_DESC_CS_VS_FMT_NV12(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \
50+
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_NV12, 12, _frmidx, _asrx, _asry, _interlace, _cp)
51+
#define TUD_VIDEO_DESC_CS_VS_FMT_M420(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \
52+
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_M420, 12, _frmidx, _asrx, _asry, _interlace, _cp)
53+
#define TUD_VIDEO_DESC_CS_VS_FMT_I420(_fmtidx, _numfmtdesc, _frmidx, _asrx, _asry, _interlace, _cp) \
54+
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR(_fmtidx, _numfmtdesc, TUD_VIDEO_GUID_I420, 12, _frmidx, _asrx, _asry, _interlace, _cp)
55+
56+
#define TUD_VIDEO_CAPTURE_DESCRIPTOR(_itf, _stridx, _epin, _width, _height, _fps, _epsize) \
57+
TUD_VIDEO_DESC_IAD(_itf, (_itf+2), _stridx), \
58+
/* Video control 0 */ \
59+
TUD_VIDEO_DESC_STD_VC(_itf, 0, _stridx), \
60+
TUD_VIDEO_DESC_CS_VC( /* UVC 1.5*/ 0x0150, \
61+
/* wTotalLength - bLength */ \
62+
TUD_VIDEO_DESC_INPUT_TERM_LEN + TUD_VIDEO_DESC_OUTPUT_TERM_LEN, \
63+
UVC_CLOCK_FREQUENCY, 1), \
64+
TUD_VIDEO_DESC_INPUT_TERM(UVC_ENTITY_CAP_INPUT_TERMINAL, VIDEO_ETT_COMPOSITE_CONNECTOR, 0, 0), \
65+
TUD_VIDEO_DESC_OUTPUT_TERM(UVC_ENTITY_CAP_OUTPUT_TERMINAL, VIDEO_TT_STREAMING, 0, 1, 0), \
66+
/* Video stream alt. 0 */ \
67+
TUD_VIDEO_DESC_STD_VS( (_itf+1), 0, 0, 0), \
68+
/* Video stream header for without still image capture */ \
69+
TUD_VIDEO_DESC_CS_VS_INPUT( /*bNumFormats*/1, \
70+
/*wTotalLength - bLength */\
71+
TUD_VIDEO_DESC_CS_VS_FMT_UNCOMPR_LEN\
72+
+ TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT_LEN\
73+
+ TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING_LEN,\
74+
_epin, /*bmInfo*/0, /*bTerminalLink*/UVC_ENTITY_CAP_OUTPUT_TERMINAL, \
75+
/*bStillCaptureMethod*/0, /*bTriggerSupport*/0, /*bTriggerUsage*/0, \
76+
/*bmaControls(1)*/0), \
77+
/* Video stream format */ \
78+
TUD_VIDEO_DESC_CS_VS_FMT_YUY2(/*bFormatIndex*/1, /*bNumFrameDescriptors*/1, \
79+
/*bDefaultFrameIndex*/1, 0, 0, 0, /*bCopyProtect*/0), \
80+
/* Video stream frame format */ \
81+
TUD_VIDEO_DESC_CS_VS_FRM_UNCOMPR_CONT(/*bFrameIndex */1, 0, _width, _height, \
82+
_width * _height * 16, _width * _height * 16 * _fps, \
83+
_width * _height * 16, \
84+
(10000000/_fps), (10000000/_fps), 10000000, 100000), \
85+
TUD_VIDEO_DESC_CS_VS_COLOR_MATCHING(VIDEO_COLOR_PRIMARIES_BT709, VIDEO_COLOR_XFER_CH_BT709, VIDEO_COLOR_COEF_SMPTE170M), \
86+
/* VS alt 1 */\
87+
TUD_VIDEO_DESC_STD_VS((_itf+1), 1, 1, 0), \
88+
/* EP */ \
89+
TUD_VIDEO_DESC_EP_ISO(_epin, _epsize, 1)
90+
91+
92+
ESP_EVENT_DEFINE_BASE(ARDUINO_USB_VIDEO_EVENTS);
93+
esp_err_t arduino_usb_event_post(esp_event_base_t event_base, int32_t event_id, void *event_data, size_t event_data_size, TickType_t ticks_to_wait);
94+
esp_err_t arduino_usb_event_handler_register_with(esp_event_base_t event_base, int32_t event_id, esp_event_handler_t event_handler, void *event_handler_arg);
95+
96+
#define FRAME_WIDTH 80
97+
#define FRAME_HEIGHT 60
98+
#define FRAME_RATE 10
99+
100+
uint16_t tusb_video_load_descriptor(uint8_t * dst, uint8_t * itf)
101+
{
102+
uint8_t str_index = tinyusb_add_string_descriptor("TinyUSB Video");
103+
uint8_t ep_num = tinyusb_get_free_in_endpoint();
104+
TU_VERIFY (ep_num != 0);
105+
uint8_t descriptor[TUD_VIDEO_CAPTURE_DESC_LEN] = {
106+
// Interface number, string index, EP Out & IN address, EP size
107+
TUD_VIDEO_CAPTURE_DESCRIPTOR(*itf, str_index, (uint8_t)(0x80 | ep_num), FRAME_WIDTH, FRAME_HEIGHT, FRAME_RATE, 64)
108+
};
109+
*itf+=2;
110+
memcpy(dst, descriptor, TUD_VIDEO_CAPTURE_DESC_LEN);
111+
return TUD_VIDEO_CAPTURE_DESC_LEN;
112+
// size_t desc_len = sizeof(UVCConfigurationDescriptor);
113+
// memcpy(dst, UVCConfigurationDescriptor, desc_len);
114+
// return desc_len;
115+
}
116+
117+
static unsigned frame_num = 0;
118+
static unsigned tx_busy = 0;
119+
static unsigned interval_ms = 1000 / FRAME_RATE;
120+
static uint8_t frame_buffer[FRAME_WIDTH * FRAME_HEIGHT * 16 / 8];
121+
122+
static void fill_color_bar(uint8_t *buffer, unsigned start_position)
123+
{
124+
/* EBU color bars
125+
* See also https://stackoverflow.com/questions/6939422 */
126+
static uint8_t const bar_color[8][4] = {
127+
/* Y, U, Y, V */
128+
{ 235, 128, 235, 128}, /* 100% White */
129+
{ 219, 16, 219, 138}, /* Yellow */
130+
{ 188, 154, 188, 16}, /* Cyan */
131+
{ 173, 42, 173, 26}, /* Green */
132+
{ 78, 214, 78, 230}, /* Magenta */
133+
{ 63, 102, 63, 240}, /* Red */
134+
{ 32, 240, 32, 118}, /* Blue */
135+
{ 16, 128, 16, 128}, /* Black */
136+
};
137+
uint8_t *p;
138+
139+
/* Generate the 1st line */
140+
uint8_t *end = &buffer[FRAME_WIDTH * 2];
141+
unsigned idx = (FRAME_WIDTH / 2 - 1) - (start_position % (FRAME_WIDTH / 2));
142+
p = &buffer[idx * 4];
143+
for (unsigned i = 0; i < 8; ++i) {
144+
for (int j = 0; j < FRAME_WIDTH / (2 * 8); ++j) {
145+
memcpy(p, &bar_color[i], 4);
146+
p += 4;
147+
if (end <= p) {
148+
p = buffer;
149+
}
150+
}
151+
}
152+
/* Duplicate the 1st line to the others */
153+
p = &buffer[FRAME_WIDTH * 2];
154+
for (unsigned i = 1; i < FRAME_HEIGHT; ++i) {
155+
memcpy(p, buffer, FRAME_WIDTH * 2);
156+
p += FRAME_WIDTH * 2;
157+
}
158+
}
159+
160+
void video_task(void)
161+
{
162+
static unsigned start_ms = 0;
163+
static unsigned already_sent = 0;
164+
165+
if (!tud_video_n_streaming(0, 0)) {
166+
already_sent = 0;
167+
frame_num = 0;
168+
return;
169+
}
170+
171+
if (!already_sent) {
172+
already_sent = 1;
173+
start_ms = millis();
174+
fill_color_bar(frame_buffer, frame_num);
175+
tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
176+
}
177+
178+
unsigned cur = millis();
179+
if (cur - start_ms < interval_ms) return; // not enough time
180+
if (tx_busy) return;
181+
start_ms += interval_ms;
182+
fill_color_bar(frame_buffer, frame_num);
183+
tud_video_n_frame_xfer(0, 0, (void*)frame_buffer, FRAME_WIDTH * FRAME_HEIGHT * 16/8);
184+
}
185+
186+
187+
188+
/** Invoked when compeletion of a frame transfer
189+
*
190+
* @param[in] ctl_idx Destination control interface index
191+
* @param[in] stm_idx Destination streaming interface index */
192+
extern "C" void tud_video_frame_xfer_complete_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx){
193+
log_d("");
194+
tx_busy = 0;
195+
++frame_num;
196+
}
197+
198+
/** Invoked when VS_COMMIT_CONTROL(SET_CUR) request received
199+
*
200+
* @param[in] ctl_idx Destination control interface index
201+
* @param[in] stm_idx Destination streaming interface index
202+
* @param[in] parameters Video streaming parameters
203+
* @return video_error_code_t */
204+
extern "C" int tud_video_commit_cb(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, video_probe_and_commit_control_t const *parameters){
205+
log_d("[%u][%u]:", ctl_idx, stm_idx);
206+
/* convert unit to ms from 100 ns */
207+
interval_ms = parameters->dwFrameInterval / 10000;
208+
return VIDEO_ERROR_NONE;
209+
}
210+
211+
/** Invoked when SET_POWER_MODE request received
212+
*
213+
* @param[in] ctl_idx Destination control interface index
214+
* @param[in] stm_idx Destination streaming interface index
215+
* @return video_error_code_t */
216+
extern "C" int tud_video_power_mode_cb(uint_fast8_t ctl_idx, uint8_t power_mod){
217+
log_d("[%u]: %u", ctl_idx, power_mod);
218+
return VIDEO_ERROR_NONE;
219+
}
220+
221+
USBVideo::USBVideo(uint8_t ctl, uint8_t stm):_ctl(ctl), _stm(stm){
222+
tinyusb_enable_interface(USB_INTERFACE_VIDEO, TUD_VIDEO_CAPTURE_DESC_LEN, tusb_video_load_descriptor);
223+
//tinyusb_enable_interface(USB_INTERFACE_VIDEO, sizeof(UVCConfigurationDescriptor), tusb_video_load_descriptor);
224+
}
225+
226+
void USBVideo::begin(){
227+
228+
}
229+
230+
void USBVideo::end(){
231+
232+
}
233+
234+
void USBVideo::onEvent(esp_event_handler_t callback){
235+
onEvent(ARDUINO_USB_VIDEO_ANY_EVENT, callback);
236+
}
237+
238+
void USBVideo::onEvent(arduino_usb_video_event_t event, esp_event_handler_t callback){
239+
arduino_usb_event_handler_register_with(ARDUINO_USB_VIDEO_EVENTS, event, callback, this);
240+
}
241+
242+
bool USBVideo::streaming(){
243+
return tud_video_n_streaming(_ctl, _stm);
244+
}
245+
246+
bool USBVideo::sendFrame(void *buffer, size_t len){
247+
return tud_video_n_frame_xfer(_ctl, _stm, buffer, len);
248+
}
249+
250+
#endif /* CONFIG_TINYUSB_VIDEO_ENABLED */

libraries/USB/src/USBVideo.h

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
#include "Stream.h"
17+
#include "sdkconfig.h"
18+
19+
#if CONFIG_TINYUSB_VIDEO_ENABLED
20+
#include "esp_event.h"
21+
22+
ESP_EVENT_DECLARE_BASE(ARDUINO_USB_VIDEO_EVENTS);
23+
24+
typedef enum {
25+
ARDUINO_USB_VIDEO_ANY_EVENT = ESP_EVENT_ANY_ID,
26+
ARDUINO_USB_VIDEO_DATA_EVENT,
27+
ARDUINO_USB_VIDEO_MAX_EVENT,
28+
} arduino_usb_video_event_t;
29+
30+
typedef union {
31+
struct {
32+
uint16_t len;
33+
} data;
34+
} arduino_usb_video_event_data_t;
35+
36+
class USBVideo {
37+
private:
38+
uint8_t _ctl;
39+
uint8_t _stm;
40+
public:
41+
USBVideo(uint8_t ctl=0, uint8_t stm=0);
42+
void begin(void);
43+
void end(void);
44+
bool streaming(void);
45+
bool sendFrame(void *buffer, size_t len);
46+
47+
void onEvent(esp_event_handler_t callback);
48+
void onEvent(arduino_usb_video_event_t event, esp_event_handler_t callback);
49+
};
50+
51+
void video_task(void);
52+
53+
#endif /* CONFIG_TINYUSB_VIDEO_ENABLED */

0 commit comments

Comments
 (0)