|
36 | 36 | #define EXAMPLE_NUMBER_OF_STREAMS (1) // This example shows how to control multiple UVC streams. Set this to 1 if you camera offers only 1 stream |
37 | 37 | #define EXAMPLE_USE_SDCARD (0) // SD card on P4 evaluation board will be initialized |
38 | 38 |
|
| 39 | +#define UVC_DESC_DWFRAMEINTERVAL_TO_FPS(dwFrameInterval) (((dwFrameInterval) != 0) ? 10000000 / ((float)(dwFrameInterval)) : 0) |
| 40 | + |
39 | 41 | static const char *TAG = "UVC example"; |
40 | 42 | static QueueHandle_t rx_frames_queue[EXAMPLE_NUMBER_OF_STREAMS]; |
41 | 43 | static bool dev_connected = false; |
| 44 | +static uvc_host_frame_info_t *frame_info_list[EXAMPLE_NUMBER_OF_STREAMS] = {NULL}; |
| 45 | +static size_t frame_info_list_size[EXAMPLE_NUMBER_OF_STREAMS] = {0}; |
| 46 | + |
| 47 | +static const char *FORMAT_STR[] = { |
| 48 | + "FORMAT_UNDEFINED", |
| 49 | + "FORMAT_MJPEG", |
| 50 | + "FORMAT_YUY2", |
| 51 | + "FORMAT_H264", |
| 52 | + "FORMAT_H265", |
| 53 | +}; |
42 | 54 |
|
43 | 55 | bool frame_callback(const uvc_host_frame_t *frame, void *user_ctx) |
44 | 56 | { |
@@ -95,24 +107,24 @@ static void usb_lib_task(void *arg) |
95 | 107 |
|
96 | 108 | static void frame_handling_task(void *arg) |
97 | 109 | { |
98 | | - const uvc_host_stream_config_t *stream_config = (const uvc_host_stream_config_t *)arg; |
99 | | - QueueHandle_t frame_q = *((QueueHandle_t *)(stream_config->user_ctx)); |
100 | | - const int uvc_index = stream_config->usb.uvc_stream_index; |
| 110 | + const uvc_host_stream_config_t stream_config = *(uvc_host_stream_config_t *)arg; |
| 111 | + QueueHandle_t frame_q = *((QueueHandle_t *)(stream_config.user_ctx)); |
| 112 | + const int uvc_index = stream_config.usb.uvc_stream_index; |
101 | 113 |
|
102 | 114 | while (true) { |
103 | 115 | uvc_host_stream_hdl_t uvc_stream = NULL; |
104 | 116 |
|
105 | 117 | ESP_LOGI(TAG, "Opening UVC device 0x%04X:0x%04X-%d\n\t%dx%d@%2.1fFPS...", |
106 | | - stream_config->usb.vid, stream_config->usb.pid, uvc_index, stream_config->vs_format.h_res, stream_config->vs_format.v_res, stream_config->vs_format.fps); |
107 | | - esp_err_t err = uvc_host_stream_open(stream_config, pdMS_TO_TICKS(5000), &uvc_stream); |
| 118 | + stream_config.usb.vid, stream_config.usb.pid, uvc_index, stream_config.vs_format.h_res, stream_config.vs_format.v_res, stream_config.vs_format.fps); |
| 119 | + esp_err_t err = uvc_host_stream_open(&stream_config, pdMS_TO_TICKS(5000), &uvc_stream); |
108 | 120 | if (ESP_OK != err) { |
109 | 121 | ESP_LOGI(TAG, "Failed to open device"); |
110 | 122 | vTaskDelay(pdMS_TO_TICKS(5000)); |
111 | 123 | continue; |
112 | 124 | } |
113 | 125 | //uvc_host_desc_print(uvc_stream); |
114 | 126 | dev_connected = true; |
115 | | - ESP_LOGI(TAG, "Device 0x%04X:0x%04X-%d OPENED!", stream_config->usb.vid, stream_config->usb.pid, uvc_index); |
| 127 | + ESP_LOGI(TAG, "Device 0x%04X:0x%04X-%d OPENED!", stream_config.usb.vid, stream_config.usb.pid, uvc_index); |
116 | 128 | vTaskDelay(pdMS_TO_TICKS(100)); |
117 | 129 | unsigned count = 0; |
118 | 130 |
|
@@ -153,52 +165,8 @@ static void frame_handling_task(void *arg) |
153 | 165 | } |
154 | 166 | } |
155 | 167 |
|
156 | | -static const uvc_host_stream_config_t stream_mjpeg_config = { |
157 | | - .event_cb = stream_callback, |
158 | | - .frame_cb = frame_callback, |
159 | | - .user_ctx = &rx_frames_queue[0], |
160 | | - .usb = { |
161 | | - .vid = EXAMPLE_USB_DEVICE_VID, |
162 | | - .pid = EXAMPLE_USB_DEVICE_PID, |
163 | | - .uvc_stream_index = 0, |
164 | | - }, |
165 | | - .vs_format = { |
166 | | - .h_res = 720, |
167 | | - .v_res = 1280, |
168 | | - .fps = 15, |
169 | | - .format = UVC_VS_FORMAT_MJPEG, |
170 | | - }, |
171 | | - .advanced = { |
172 | | - .number_of_frame_buffers = EXAMPLE_FRAME_COUNT, |
173 | | - .frame_size = 0, |
174 | | - .number_of_urbs = 4, |
175 | | - .urb_size = 10 * 1024, |
176 | | - }, |
177 | | -}; |
178 | | - |
179 | 168 | #if EXAMPLE_NUMBER_OF_STREAMS > 1 |
180 | | -static const uvc_host_stream_config_t stream_h265_config = { |
181 | | - .event_cb = stream_callback, |
182 | | - .frame_cb = frame_callback, |
183 | | - .user_ctx = &rx_frames_queue[1], |
184 | | - .usb = { |
185 | | - .vid = EXAMPLE_USB_DEVICE_VID, |
186 | | - .pid = EXAMPLE_USB_DEVICE_PID, |
187 | | - .uvc_stream_index = 1, |
188 | | - }, |
189 | | - .vs_format = { |
190 | | - .h_res = 1280, |
191 | | - .v_res = 720, |
192 | | - .fps = 15, |
193 | | - .format = UVC_VS_FORMAT_H265, |
194 | | - }, |
195 | | - .advanced = { |
196 | | - .number_of_frame_buffers = EXAMPLE_FRAME_COUNT, |
197 | | - .frame_size = 0, |
198 | | - .number_of_urbs = 4, |
199 | | - .urb_size = 10 * 1024, |
200 | | - }, |
201 | | -}; |
| 169 | + |
202 | 170 | #endif // EXAMPLE_NUMBER_OF_STREAMS > 1 |
203 | 171 |
|
204 | 172 | #if EXAMPLE_USE_SDCARD |
@@ -279,6 +247,92 @@ void app_init_sdcard(void) |
279 | 247 | } |
280 | 248 | #endif // EXAMPLE_USE_SDCARD |
281 | 249 |
|
| 250 | +static void start_uvc_frame_handling_tasks(void) |
| 251 | +{ |
| 252 | + uvc_host_stream_config_t stream_mjpeg_config = { |
| 253 | + .event_cb = stream_callback, |
| 254 | + .frame_cb = frame_callback, |
| 255 | + .user_ctx = &rx_frames_queue[0], |
| 256 | + .usb = { |
| 257 | + .vid = EXAMPLE_USB_DEVICE_VID, |
| 258 | + .pid = EXAMPLE_USB_DEVICE_PID, |
| 259 | + .uvc_stream_index = 0, |
| 260 | + }, |
| 261 | + .vs_format = { |
| 262 | + .h_res = frame_info_list[0][0].h_res, |
| 263 | + .v_res = frame_info_list[0][0].v_res, |
| 264 | + .fps = UVC_DESC_DWFRAMEINTERVAL_TO_FPS(frame_info_list[0][0].default_interval), |
| 265 | + .format = UVC_VS_FORMAT_MJPEG, |
| 266 | + }, |
| 267 | + .advanced = { |
| 268 | + .number_of_frame_buffers = EXAMPLE_FRAME_COUNT, |
| 269 | + .frame_size = 0, |
| 270 | + .number_of_urbs = 4, |
| 271 | + .urb_size = 10 * 1024, |
| 272 | + }, |
| 273 | + }; |
| 274 | + BaseType_t task_created = xTaskCreatePinnedToCore(frame_handling_task, "mjpeg_handling", 4096, (void *)&stream_mjpeg_config, EXAMPLE_USB_HOST_PRIORITY - 2, NULL, tskNO_AFFINITY); |
| 275 | + assert(task_created == pdTRUE); |
| 276 | + |
| 277 | +#if EXAMPLE_NUMBER_OF_STREAMS > 1 |
| 278 | + vTaskDelay(pdMS_TO_TICKS(1000)); |
| 279 | + uvc_host_stream_config_t stream_h265_config = { |
| 280 | + .event_cb = stream_callback, |
| 281 | + .frame_cb = frame_callback, |
| 282 | + .user_ctx = &rx_frames_queue[1], |
| 283 | + .usb = { |
| 284 | + .vid = EXAMPLE_USB_DEVICE_VID, |
| 285 | + .pid = EXAMPLE_USB_DEVICE_PID, |
| 286 | + .uvc_stream_index = 1, |
| 287 | + }, |
| 288 | + .vs_format = { |
| 289 | + .h_res = frame_info_list[1][0].h_res, |
| 290 | + .v_res = frame_info_list[1][0].v_res, |
| 291 | + .fps = UVC_DESC_DWFRAMEINTERVAL_TO_FPS(frame_info_list[1][0].default_interval), |
| 292 | + .format = UVC_VS_FORMAT_H265, |
| 293 | + }, |
| 294 | + .advanced = { |
| 295 | + .number_of_frame_buffers = EXAMPLE_FRAME_COUNT, |
| 296 | + .frame_size = 0, |
| 297 | + .number_of_urbs = 4, |
| 298 | + .urb_size = 10 * 1024, |
| 299 | + }, |
| 300 | + }; |
| 301 | + task_created = xTaskCreatePinnedToCore(frame_handling_task, "h265_handling", 4096, (void *)&stream_h265_config, EXAMPLE_USB_HOST_PRIORITY - 3, NULL, tskNO_AFFINITY); |
| 302 | + assert(task_created == pdTRUE); |
| 303 | +#endif // EXAMPLE_NUMBER_OF_STREAMS > 1 |
| 304 | +} |
| 305 | + |
| 306 | +static void uvc_event_cb(const uvc_host_driver_event_data_t *event, void *user_ctx) |
| 307 | +{ |
| 308 | + switch (event->type) { |
| 309 | + case UVC_HOST_DRIVER_EVENT_DEVICE_CONNECTED: { |
| 310 | + ESP_LOGI(TAG, "Device connected, addr: %d, stream index: %d", event->device_connected.dev_addr, event->device_connected.uvc_stream_index); |
| 311 | + int stream_index = event->device_connected.uvc_stream_index; |
| 312 | + if (stream_index >= EXAMPLE_NUMBER_OF_STREAMS) { |
| 313 | + ESP_LOGW(TAG, "Stream index %d is out of range. Max supported is %d", stream_index, EXAMPLE_NUMBER_OF_STREAMS - 1); |
| 314 | + break; |
| 315 | + } |
| 316 | + |
| 317 | + frame_info_list_size[stream_index] = event->device_connected.frame_info_num; |
| 318 | + frame_info_list[stream_index] = calloc(frame_info_list_size[stream_index], sizeof(uvc_host_frame_info_t)); |
| 319 | + assert(frame_info_list[stream_index]); |
| 320 | + uvc_host_get_frame_list(event->device_connected.dev_addr, stream_index, (uvc_host_frame_info_t (*)[])frame_info_list[stream_index], &frame_info_list_size[stream_index]); |
| 321 | + for (int i = 0; i < frame_info_list_size[stream_index]; i++) { |
| 322 | + ESP_LOGI(TAG, "Camera format: %s %d*%d@%.1ffps", |
| 323 | + FORMAT_STR[frame_info_list[stream_index][i].format], |
| 324 | + frame_info_list[stream_index][i].h_res, |
| 325 | + frame_info_list[stream_index][i].v_res, |
| 326 | + UVC_DESC_DWFRAMEINTERVAL_TO_FPS(frame_info_list[stream_index][i].default_interval)); |
| 327 | + } |
| 328 | + start_uvc_frame_handling_tasks(); |
| 329 | + break; |
| 330 | + } |
| 331 | + default: |
| 332 | + break; |
| 333 | + } |
| 334 | +} |
| 335 | + |
282 | 336 | /** |
283 | 337 | * @brief Main application |
284 | 338 | */ |
@@ -311,15 +365,7 @@ void app_main(void) |
311 | 365 | .driver_task_priority = EXAMPLE_USB_HOST_PRIORITY + 1, |
312 | 366 | .xCoreID = tskNO_AFFINITY, |
313 | 367 | .create_background_task = true, |
| 368 | + .event_cb = uvc_event_cb, |
314 | 369 | }; |
315 | 370 | ESP_ERROR_CHECK(uvc_host_install(&uvc_driver_config)); |
316 | | - |
317 | | - task_created = xTaskCreatePinnedToCore(frame_handling_task, "mjpeg_handling", 4096, (void *)&stream_mjpeg_config, EXAMPLE_USB_HOST_PRIORITY - 2, NULL, tskNO_AFFINITY); |
318 | | - assert(task_created == pdTRUE); |
319 | | - |
320 | | -#if EXAMPLE_NUMBER_OF_STREAMS > 1 |
321 | | - vTaskDelay(pdMS_TO_TICKS(1000)); |
322 | | - task_created = xTaskCreatePinnedToCore(frame_handling_task, "h265_handling", 4096, (void *)&stream_h265_config, EXAMPLE_USB_HOST_PRIORITY - 3, NULL, tskNO_AFFINITY); |
323 | | - assert(task_created == pdTRUE); |
324 | | -#endif // EXAMPLE_USE_SDCARD |
325 | 371 | } |
0 commit comments