main.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdio.h>
  7. #include <unistd.h>
  8. #include "esp_log.h"
  9. #include "tcp_server.h"
  10. #include "libuvc/libuvc.h"
  11. #include "libuvc_helper.h"
  12. #include "libuvc_adapter.h"
  13. #include "freertos/FreeRTOS.h"
  14. #include "freertos/task.h"
  15. #include "freertos/semphr.h"
  16. #include "freertos/event_groups.h"
  17. #include "driver/gpio.h"
  18. #include "usb/usb_host.h"
  19. #include "esp_err.h"
  20. #include "esp_log.h"
  21. #include "esp_timer.h"
  22. static const char *TAG = "example";
  23. #define USB_DISCONNECT_PIN GPIO_NUM_0
  24. #if (CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_AUTO)
  25. #define EXAMPLE_UVC_PROTOCOL_AUTO_COUNT 3
  26. typedef struct {
  27. enum uvc_frame_format format;
  28. int width;
  29. int height;
  30. int fps;
  31. const char* name;
  32. } uvc_stream_profile_t;
  33. uvc_stream_profile_t uvc_stream_profiles[EXAMPLE_UVC_PROTOCOL_AUTO_COUNT] = {
  34. {UVC_FRAME_FORMAT_MJPEG, 640, 480, 15, "640x480, fps 15"},
  35. {UVC_FRAME_FORMAT_MJPEG, 320, 240, 30, "320x240, fps 30"},
  36. {UVC_FRAME_FORMAT_MJPEG, 320, 240, 0, "320x240, any fps"}
  37. };
  38. #endif // CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_AUTO
  39. #if (CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_CUSTOM)
  40. #define FPS CONFIG_EXAMPLE_FPS_PARAM
  41. #define WIDTH CONFIG_EXAMPLE_WIDTH_PARAM
  42. #define HEIGHT CONFIG_EXAMPLE_HEIGHT_PARAM
  43. #define FORMAT CONFIG_EXAMPLE_FORMAT_PARAM
  44. #endif // CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_CUSTOM
  45. // Attached camera can be filtered out based on (non-zero value of) PID, VID, SERIAL_NUMBER
  46. #define PID 0
  47. #define VID 0
  48. #define SERIAL_NUMBER NULL
  49. #define UVC_CHECK(exp) do { \
  50. uvc_error_t _err_ = (exp); \
  51. if(_err_ < 0) { \
  52. ESP_LOGE(TAG, "UVC error: %s", \
  53. uvc_error_string(_err_)); \
  54. assert(0); \
  55. } \
  56. } while(0)
  57. static SemaphoreHandle_t ready_to_uninstall_usb;
  58. static EventGroupHandle_t app_flags;
  59. // Handles common USB host library events
  60. static void usb_lib_handler_task(void *args)
  61. {
  62. while (1) {
  63. uint32_t event_flags;
  64. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  65. // Release devices once all clients has deregistered
  66. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  67. usb_host_device_free_all();
  68. }
  69. // Give ready_to_uninstall_usb semaphore to indicate that USB Host library
  70. // can be deinitialized, and terminate this task.
  71. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
  72. xSemaphoreGive(ready_to_uninstall_usb);
  73. }
  74. }
  75. vTaskDelete(NULL);
  76. }
  77. static esp_err_t initialize_usb_host_lib(void)
  78. {
  79. TaskHandle_t task_handle = NULL;
  80. const usb_host_config_t host_config = {
  81. .intr_flags = ESP_INTR_FLAG_LEVEL1
  82. };
  83. esp_err_t err = usb_host_install(&host_config);
  84. if (err != ESP_OK) {
  85. return err;
  86. }
  87. ready_to_uninstall_usb = xSemaphoreCreateBinary();
  88. if (ready_to_uninstall_usb == NULL) {
  89. usb_host_uninstall();
  90. return ESP_ERR_NO_MEM;
  91. }
  92. if (xTaskCreate(usb_lib_handler_task, "usb_events", 4096, NULL, 2, &task_handle) != pdPASS) {
  93. vSemaphoreDelete(ready_to_uninstall_usb);
  94. usb_host_uninstall();
  95. return ESP_ERR_NO_MEM;
  96. }
  97. return ESP_OK;
  98. }
  99. static void uninitialize_usb_host_lib(void)
  100. {
  101. xSemaphoreTake(ready_to_uninstall_usb, portMAX_DELAY);
  102. vSemaphoreDelete(ready_to_uninstall_usb);
  103. if (usb_host_uninstall() != ESP_OK) {
  104. ESP_LOGE(TAG, "Failed to uninstall usb_host");
  105. }
  106. }
  107. /* This callback function runs once per frame. Use it to perform any
  108. * quick processing you need, or have it put the frame into your application's
  109. * input queue. If this function takes too long, you'll start losing frames. */
  110. void frame_callback(uvc_frame_t *frame, void *ptr)
  111. {
  112. static size_t fps;
  113. static size_t bytes_per_second;
  114. static int64_t start_time;
  115. int64_t current_time = esp_timer_get_time();
  116. bytes_per_second += frame->data_bytes;
  117. fps++;
  118. if (!start_time) {
  119. start_time = current_time;
  120. }
  121. if (current_time > start_time + 1000000) {
  122. ESP_LOGI(TAG, "fps: %u, bytes per second: %u", fps, bytes_per_second);
  123. start_time = current_time;
  124. bytes_per_second = 0;
  125. fps = 0;
  126. }
  127. // Stream received frame to client, if enabled
  128. tcp_server_send(frame->data, frame->data_bytes);
  129. }
  130. void button_callback(int button, int state, void *user_ptr)
  131. {
  132. printf("button %d state %d\n", button, state);
  133. }
  134. static void libuvc_adapter_cb(libuvc_adapter_event_t event)
  135. {
  136. xEventGroupSetBits(app_flags, event);
  137. }
  138. static EventBits_t wait_for_event(EventBits_t event)
  139. {
  140. return xEventGroupWaitBits(app_flags, event, pdTRUE, pdFALSE, portMAX_DELAY) & event;
  141. }
  142. static uvc_error_t uvc_negotiate_stream_profile(uvc_device_handle_t *devh,
  143. uvc_stream_ctrl_t *ctrl)
  144. {
  145. uvc_error_t res;
  146. #if (CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_AUTO)
  147. for (int idx = 0; idx < EXAMPLE_UVC_PROTOCOL_AUTO_COUNT; idx++) {
  148. ESP_LOGI(TAG, "Negotiate streaming profile %s ...", uvc_stream_profiles[idx].name);
  149. res = uvc_get_stream_ctrl_format_size(devh,
  150. ctrl,
  151. uvc_stream_profiles[idx].format,
  152. uvc_stream_profiles[idx].width,
  153. uvc_stream_profiles[idx].height,
  154. uvc_stream_profiles[idx].fps);
  155. if (UVC_SUCCESS == res) {
  156. break; // stream profile negotiated
  157. }
  158. sleep(1);
  159. ESP_LOGE(TAG, "Negotiation failed with error %d.", res);
  160. }
  161. #endif // CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_AUTO
  162. #if (CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_CUSTOM)
  163. int attempt = CONFIG_EXAMPLE_NEGOTIATION_ATTEMPTS;
  164. while (attempt--) {
  165. ESP_LOGI(TAG, "Negotiate streaming profile %dx%d, %d fps ...", WIDTH, HEIGHT, FPS);
  166. res = uvc_get_stream_ctrl_format_size(devh, ctrl, FORMAT, WIDTH, HEIGHT, FPS);
  167. if (UVC_SUCCESS == res) {
  168. break;
  169. }
  170. sleep(1);
  171. ESP_LOGE(TAG, "Negotiation failed. Try again (%d) ...", attempt);
  172. }
  173. #endif // CONFIG_EXAMPLE_UVC_PROTOCOL_MODE_CUSTOM
  174. if (UVC_SUCCESS == res) {
  175. ESP_LOGI(TAG, "Negotiation complete.");
  176. } else {
  177. ESP_LOGE(TAG, "Try another UVC USB device of change negotiation parameters.");
  178. }
  179. return res;
  180. }
  181. int app_main(int argc, char **argv)
  182. {
  183. uvc_context_t *ctx;
  184. uvc_device_t *dev;
  185. uvc_device_handle_t *devh;
  186. uvc_stream_ctrl_t ctrl;
  187. app_flags = xEventGroupCreate();
  188. assert(app_flags);
  189. const gpio_config_t input_pin = {
  190. .pin_bit_mask = BIT64(USB_DISCONNECT_PIN),
  191. .mode = GPIO_MODE_INPUT,
  192. .pull_up_en = GPIO_PULLUP_ENABLE,
  193. };
  194. ESP_ERROR_CHECK(gpio_config(&input_pin));
  195. ESP_ERROR_CHECK(initialize_usb_host_lib());
  196. libuvc_adapter_config_t config = {
  197. .create_background_task = true,
  198. .task_priority = 5,
  199. .stack_size = 4096,
  200. .callback = libuvc_adapter_cb
  201. };
  202. libuvc_adapter_set_config(&config);
  203. UVC_CHECK(uvc_init(&ctx, NULL));
  204. // Streaming takes place only when enabled in menuconfig
  205. ESP_ERROR_CHECK(tcp_server_wait_for_connection());
  206. do {
  207. ESP_LOGI(TAG, "Waiting for USB UVC device connection ...");
  208. wait_for_event(UVC_DEVICE_CONNECTED);
  209. UVC_CHECK(uvc_find_device(ctx, &dev, PID, VID, SERIAL_NUMBER));
  210. ESP_LOGI(TAG, "Device found");
  211. // UVC Device open
  212. UVC_CHECK(uvc_open(dev, &devh));
  213. // Uncomment to print configuration descriptor
  214. // libuvc_adapter_print_descriptors(devh);
  215. uvc_set_button_callback(devh, button_callback, NULL);
  216. // Print known device information
  217. uvc_print_diag(devh, stderr);
  218. // Negotiate stream profile
  219. if (UVC_SUCCESS == uvc_negotiate_stream_profile(devh, &ctrl)) {
  220. // dwMaxPayloadTransferSize has to be overwritten to MPS (maximum packet size)
  221. // supported by ESP32-S2(S3), as libuvc selects the highest possible MPS by default.
  222. ctrl.dwMaxPayloadTransferSize = 512;
  223. uvc_print_stream_ctrl(&ctrl, stderr);
  224. UVC_CHECK(uvc_start_streaming(devh, &ctrl, frame_callback, NULL, 0));
  225. ESP_LOGI(TAG, "Streaming...");
  226. wait_for_event(UVC_DEVICE_DISCONNECTED);
  227. uvc_stop_streaming(devh);
  228. ESP_LOGI(TAG, "Done streaming.");
  229. } else {
  230. wait_for_event(UVC_DEVICE_DISCONNECTED);
  231. }
  232. // UVC Device close
  233. uvc_close(devh);
  234. } while (gpio_get_level(USB_DISCONNECT_PIN) != 0);
  235. tcp_server_close_when_done();
  236. uvc_exit(ctx);
  237. ESP_LOGI(TAG, "UVC exited");
  238. uninitialize_usb_host_lib();
  239. return 0;
  240. }