diff --git a/examples/peripherals/usb/host/uvc/CMakeLists.txt b/examples/peripherals/usb/host/uvc/CMakeLists.txt new file mode 100644 index 00000000000..e85e1a87120 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/CMakeLists.txt @@ -0,0 +1,8 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(usb-uvc) diff --git a/examples/peripherals/usb/host/uvc/README.md b/examples/peripherals/usb/host/uvc/README.md new file mode 100644 index 00000000000..3fd8e0cfaa0 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/README.md @@ -0,0 +1,199 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# USB Camera Example + +## Overview + +This example demonstrates how to: + +- Capture video from a USB camera using the `libuvc` library. +- Stream the video over WiFi by hosting a TCP server. + +The example enumerates connected camera, negotiates selected resolution together with `FPS` and starts capturing video. +`frame_callback` function is then invoked after receiving each frame. User can process received frame according to his needs. + +Optionally, captured video can be visualized on computer with help of `player.py` script located in this example. +After setting `Enable streaming` menuconfig option, example will create TCP server upon start, and wait until `player.py` connects to server. +Once connection is established, example streams each received frame to computer for visualization. + +**Notice** that `libuvc` selects highest possible `dwMaxPayloadTransferSize` by default, so example has to manually overwrite this value to 512 bytes (maximum transfer size supported by ESP32-S2/S3). + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +## How to Use Example + +### Hardware Required + +This example requires any ESP32-S2 or ESP32-S3 with external PSRAM and exposed USB connector attached to USB camera. +*ESP module without external PSRAM will fail to initialize* + +### Configure the project + +Following configuration is needed for streaming video: + +Open the project configuration menu (`idf.py menuconfig`). + +In the `Example Connection Configuration` menu: + +* Set the Wi-Fi configuration. + * Set `WiFi SSID`. + * Set `WiFi Password`. + +In the `Example Configuration` menu: + +* Set the Example configuration + * `Enable streaming` + +Optional: If you need, change the other options according to your requirements. + +Additionally, `player.py` python script makes use of `opencv-python` and `numpy` packages, +not included in `idf-env` environment. Run following commands to install: +* `pip install opencv-python` +* `pip install numpy` + +### Build and Flash + +Build the project and flash it to the board, then run the monitor tool to view the serial output: + +Run `idf.py set-target esp32s2` to set target chip. + +Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects. + +## Known limitations + +Having only Full Speed USB peripheral and hardware limited MPS (maximum packet size) to 512 bytes, ESP32-S2/S3 is capable of reading about 0.5 MB of data per second. When connected to Full Speed USB host, cameras normally provide resolution no larger than 640x480 pixels. +Following two supported formats are the most common (both encoded in MJPEG): + * 320x240 30 FPS + * 640x480 15 FPS + +## Tested cameras + * Logitech C980 + * CANYON CNE-CWC2 + +## Example Output + +``` +... +Waiting for device +Device found +DEVICE CONFIGURATION (0c45:6340/ S) --- +Status: idle +VideoControl: + bcdUVC: 0x0100 +VideoStreaming(1): + bEndpointAddress: 129 + Formats: + MJPEGFormat(1) + bits per pixel: 0 + GUID: 4d4a5047000000000000000000000000 (MJPG) + default frame: 1 + aspect ratio: 0x0 + interlace flags: 00 + copy protect: 00 + FrameDescriptor(1) + capabilities: 00 + size: 640x480 + bit rate: 24576000-147456000 + max frame size: 614400 + default interval: 1/30 + interval[0]: 1/30 + interval[1]: 1/25 + interval[2]: 1/20 + interval[3]: 1/15 + interval[4]: 1/10 + interval[5]: 1/5 + FrameDescriptor(2) + capabilities: 00 + size: 352x288 + bit rate: 8110080-48660480 + max frame size: 202752 + default interval: 1/30 + interval[0]: 1/30 + interval[1]: 1/25 + interval[2]: 1/20 + interval[3]: 1/15 + interval[4]: 1/10 + interval[5]: 1/5 + FrameDescriptor(3) + capabilities: 00 + size: 320x240 + bit rate: 6144000-36864000 + max frame size: 153600 + default interval: 1/30 + interval[0]: 1/30 + interval[1]: 1/25 + interval[2]: 1/20 + interval[3]: 1/15 + interval[4]: 1/10 + interval[5]: 1/5 + FrameDescriptor(4) + capabilities: 00 + size: 176x144 + bit rate: 2027520-12165120 + max frame size: 50688 + default interval: 1/30 + interval[0]: 1/30 + interval[1]: 1/25 + interval[2]: 1/20 + interval[3]: 1/15 + interval[4]: 1/10 + interval[5]: 1/5 + FrameDescriptor(5) + capabilities: 00 + size: 160x120 + bit rate: 1536000-9216000 + max frame size: 38400 + default interval: 1/30 + interval[0]: 1/30 + interval[1]: 1/25 + interval[2]: 1/20 + interval[3]: 1/15 + interval[4]: 1/10 + interval[5]: 1/5 + StillFrameDescriptor + bEndPointAddress: 00 + wWidth(1) = 640 + wHeight(1) = 480 + wWidth(2) = 352 + wHeight(2) = 288 + wWidth(3) = 320 + wHeight(3) = 240 + wWidth(4) = 176 + wHeight(4) = 144 + wWidth(5) = 160 + wHeight(5) = 120 +END DEVICE CONFIGURATION +bmHint: 0001 +bFormatIndex: 1 +bFrameIndex: 3 +dwFrameInterval: 333333 +wKeyFrameRate: 0 +wPFrameRate: 0 +wCompQuality: 0 +wCompWindowSize: 0 +wDelay: 20905 +dwMaxVideoFrameSize: 153600 +dwMaxPayloadTransferSize: 512 +bInterfaceNumber: 1 +Streaming... +I (4801) example: fps: 35, bytes per second: 170480 +I (5821) example: fps: 34, bytes per second: 172448 +I (6841) example: fps: 34, bytes per second: 172448 +I (7871) example: fps: 34, bytes per second: 172448 +I (8891) example: fps: 34, bytes per second: 172448 +I (9921) example: fps: 35, bytes per second: 177520 +I (10941) example: fps: 34, bytes per second: 172448 +I (11961) example: fps: 34, bytes per second: 172448 +I (12991) example: fps: 34, bytes per second: 172448 +I (14011) example: fps: 34, bytes per second: 172448 +I (15041) example: fps: 34, bytes per second: 172448 +I (16061) example: fps: 34, bytes per second: 172448 +I (17081) example: fps: 34, bytes per second: 172448 +Done streaming. +UVC exited +``` diff --git a/examples/peripherals/usb/host/uvc/main/CMakeLists.txt b/examples/peripherals/usb/host/uvc/main/CMakeLists.txt new file mode 100644 index 00000000000..1ab9ce5ce55 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" "tcp_server.c" + INCLUDE_DIRS "" + REQUIRES protocol_examples_common nvs_flash usb mdns esp_ringbuf esp_timer esp_wifi driver) diff --git a/examples/peripherals/usb/host/uvc/main/Kconfig.projbuild b/examples/peripherals/usb/host/uvc/main/Kconfig.projbuild new file mode 100644 index 00000000000..2dac27bf669 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/main/Kconfig.projbuild @@ -0,0 +1,7 @@ +menu "Example Configuration" + config EXAMPLE_ENABLE_STREAMING + bool "Enable streaming" + default n + help + Enables streaming of captured video +endmenu diff --git a/examples/peripherals/usb/host/uvc/main/idf_component.yml b/examples/peripherals/usb/host/uvc/main/idf_component.yml new file mode 100644 index 00000000000..8b906ef6cfb --- /dev/null +++ b/examples/peripherals/usb/host/uvc/main/idf_component.yml @@ -0,0 +1,7 @@ +## IDF Component Manager Manifest File +dependencies: + idf: ">=4.4" + usb_host_uvc: "1.0.0" + mdns: + rules: + - if: "idf_version >= 5.0" diff --git a/examples/peripherals/usb/host/uvc/main/main.c b/examples/peripherals/usb/host/uvc/main/main.c new file mode 100644 index 00000000000..7cf4f476b64 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/main/main.c @@ -0,0 +1,236 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include "esp_log.h" +#include "tcp_server.h" +#include "libuvc/libuvc.h" +#include "libuvc_helper.h" +#include "libuvc_adapter.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/event_groups.h" +#include "driver/gpio.h" +#include "usb/usb_host.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_timer.h" + +static const char *TAG = "example"; + +#define USB_DISCONNECT_PIN GPIO_NUM_0 + +#define FPS 30 +#define WIDTH 320 +#define HEIGHT 240 +#define FORMAT UVC_COLOR_FORMAT_MJPEG // UVC_COLOR_FORMAT_YUYV + +// Attached camera can be filtered out based on (non-zero value of) PID, VID, SERIAL_NUMBER +#define PID 0 +#define VID 0 +#define SERIAL_NUMBER NULL + +#define UVC_CHECK(exp) do { \ + uvc_error_t _err_ = (exp); \ + if(_err_ < 0) { \ + ESP_LOGE(TAG, "UVC error: %s", \ + uvc_error_string(_err_)); \ + assert(0); \ + } \ +} while(0) + +static SemaphoreHandle_t ready_to_uninstall_usb; +static EventGroupHandle_t app_flags; + +// Handles common USB host library events +static void usb_lib_handler_task(void *args) +{ + while (1) { + uint32_t event_flags; + usb_host_lib_handle_events(portMAX_DELAY, &event_flags); + // Release devices once all clients has deregistered + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) { + usb_host_device_free_all(); + } + // Give ready_to_uninstall_usb semaphore to indicate that USB Host library + // can be deinitialized, and terminate this task. + if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) { + xSemaphoreGive(ready_to_uninstall_usb); + } + } + + vTaskDelete(NULL); +} + +static esp_err_t initialize_usb_host_lib(void) +{ + TaskHandle_t task_handle = NULL; + + const usb_host_config_t host_config = { + .intr_flags = ESP_INTR_FLAG_LEVEL1 + }; + + esp_err_t err = usb_host_install(&host_config); + if (err != ESP_OK) { + return err; + } + + ready_to_uninstall_usb = xSemaphoreCreateBinary(); + if (ready_to_uninstall_usb == NULL) { + usb_host_uninstall(); + return ESP_ERR_NO_MEM; + } + + if (xTaskCreate(usb_lib_handler_task, "usb_events", 4096, NULL, 2, &task_handle) != pdPASS) { + vSemaphoreDelete(ready_to_uninstall_usb); + usb_host_uninstall(); + return ESP_ERR_NO_MEM; + } + + return ESP_OK; +} + +static void uninitialize_usb_host_lib(void) +{ + xSemaphoreTake(ready_to_uninstall_usb, portMAX_DELAY); + vSemaphoreDelete(ready_to_uninstall_usb); + + if ( usb_host_uninstall() != ESP_OK) { + ESP_LOGE(TAG, "Failed to uninstall usb_host"); + } +} + +/* This callback function runs once per frame. Use it to perform any + * quick processing you need, or have it put the frame into your application's + * input queue. If this function takes too long, you'll start losing frames. */ +void frame_callback(uvc_frame_t *frame, void *ptr) +{ + static size_t fps; + static size_t bytes_per_second; + static int64_t start_time; + + int64_t current_time = esp_timer_get_time(); + bytes_per_second += frame->data_bytes; + fps++; + + if (!start_time) { + start_time = current_time; + } + + if (current_time > start_time + 1000000) { + ESP_LOGI(TAG, "fps: %u, bytes per second: %u", fps, bytes_per_second); + start_time = current_time; + bytes_per_second = 0; + fps = 0; + } + + // Stream received frame to client, if enabled + tcp_server_send(frame->data, frame->data_bytes); +} + +void button_callback(int button, int state, void *user_ptr) +{ + printf("button %d state %d\n", button, state); +} + +static void libuvc_adapter_cb(libuvc_adapter_event_t event) +{ + xEventGroupSetBits(app_flags, event); +} + +static EventBits_t wait_for_event(EventBits_t event) +{ + return xEventGroupWaitBits(app_flags, event, pdTRUE, pdFALSE, portMAX_DELAY) & event; +} + +int app_main(int argc, char **argv) +{ + uvc_context_t *ctx; + uvc_device_t *dev; + uvc_device_handle_t *devh; + uvc_stream_ctrl_t ctrl; + uvc_error_t res; + + app_flags = xEventGroupCreate(); + assert(app_flags); + + const gpio_config_t input_pin = { + .pin_bit_mask = BIT64(USB_DISCONNECT_PIN), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + }; + ESP_ERROR_CHECK( gpio_config(&input_pin) ); + + ESP_ERROR_CHECK( initialize_usb_host_lib() ); + + libuvc_adapter_config_t config = { + .create_background_task = true, + .task_priority = 5, + .stack_size = 4096, + .callback = libuvc_adapter_cb + }; + + libuvc_adapter_set_config(&config); + + UVC_CHECK( uvc_init(&ctx, NULL) ); + + // Streaming takes place only when enabled in menuconfig + ESP_ERROR_CHECK( tcp_server_wait_for_connection() ); + + do { + + printf("Waiting for device\n"); + wait_for_event(UVC_DEVICE_CONNECTED); + + UVC_CHECK( uvc_find_device(ctx, &dev, PID, VID, SERIAL_NUMBER) ); + puts("Device found"); + + UVC_CHECK( uvc_open(dev, &devh) ); + + // Uncomment to print configuration descriptor + // libuvc_adapter_print_descriptors(devh); + + uvc_set_button_callback(devh, button_callback, NULL); + + // Print known device information + uvc_print_diag(devh, stderr); + + // Negotiate stream profile + res = uvc_get_stream_ctrl_format_size(devh, &ctrl, FORMAT, WIDTH, HEIGHT, FPS ); + while (res != UVC_SUCCESS) { + printf("Negotiating streaming format failed, trying again...\n"); + res = uvc_get_stream_ctrl_format_size(devh, &ctrl, FORMAT, WIDTH, HEIGHT, FPS ); + sleep(1); + } + + // dwMaxPayloadTransferSize has to be overwritten to MPS (maximum packet size) + // supported by ESP32-S2(S3), as libuvc selects the highest possible MPS by default. + ctrl.dwMaxPayloadTransferSize = 512; + + uvc_print_stream_ctrl(&ctrl, stderr); + + UVC_CHECK( uvc_start_streaming(devh, &ctrl, frame_callback, NULL, 0) ); + puts("Streaming..."); + + wait_for_event(UVC_DEVICE_DISCONNECTED); + + uvc_stop_streaming(devh); + puts("Done streaming."); + + uvc_close(devh); + + } while (gpio_get_level(USB_DISCONNECT_PIN) != 0); + + tcp_server_close_when_done(); + + uvc_exit(ctx); + puts("UVC exited"); + + uninitialize_usb_host_lib(); + + return 0; +} diff --git a/examples/peripherals/usb/host/uvc/main/tcp_server.c b/examples/peripherals/usb/host/uvc/main/tcp_server.c new file mode 100644 index 00000000000..d11c8b3a4ed --- /dev/null +++ b/examples/peripherals/usb/host/uvc/main/tcp_server.c @@ -0,0 +1,227 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/ringbuf.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "protocol_examples_common.h" +#include "addr_from_stdin.h" +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "tcp_server.h" +#include "mdns.h" + +#define TAG "tcp_server" +#define PORT 2222 + +typedef struct { + int sock; + int listen_sock; + RingbufHandle_t buffer; + volatile bool close_request; + bool is_active; +} tcp_server_t; + +#ifdef CONFIG_EXAMPLE_ENABLE_STREAMING + +static tcp_server_t *s_server; + +void socket_close(tcp_server_t *server) +{ + ESP_LOGI(TAG, "Closing socket"); + shutdown(server->sock, 0); + close(server->sock); + close(server->listen_sock); +} + +static void sender_task(void *arg) +{ + tcp_server_t *server = (tcp_server_t *)arg; + server->is_active = true; + + while (1) { + size_t bytes_received = 0; + char *payload = (char *)xRingbufferReceiveUpTo( + server->buffer, &bytes_received, pdMS_TO_TICKS(2500), 20000); + + if (payload != NULL && server->is_active) { + int sent = send(server->sock, payload, bytes_received, 0); + if (sent < 0) { + ESP_LOGE(TAG, "Error occurred during sending: errno %d, \ + Shutting down tcp server...", errno); + server->is_active = false; + } + vRingbufferReturnItem(server->buffer, (void *)payload); + } + + if (server->close_request) { + socket_close(server); + vRingbufferDelete(server->buffer); + vTaskDelete(NULL); + s_server = NULL; + free(server); + return; + } + } +} + +esp_err_t tcp_server_send(uint8_t *payload, size_t size) +{ + if (!s_server || !s_server->is_active) { + return ESP_OK; + } + + if ( xRingbufferSend(s_server->buffer, payload, size, pdMS_TO_TICKS(1)) != pdTRUE ) { + ESP_LOGW(TAG, "Failed to send frame to ring buffer."); + return ESP_FAIL; + } + + return ESP_OK; +} + +static esp_err_t start_mdns_service(void) +{ + esp_err_t err = mdns_init(); + if (err) { + printf("MDNS Init failed: %d\n", err); + return ESP_FAIL; + } + + mdns_hostname_set("esp-cam"); + + return ESP_OK; +} + +static esp_err_t create_server(tcp_server_t *server) +{ + char addr_str[128]; + int ip_protocol = 0; + int addr_family = AF_INET; + struct sockaddr_storage dest_addr; + + struct sockaddr_in *dest_addr_ip4 = (struct sockaddr_in *)&dest_addr; + dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY); + dest_addr_ip4->sin_family = addr_family; + dest_addr_ip4->sin_port = htons(PORT); + ip_protocol = IPPROTO_IP; + + server->listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol); + if (server->listen_sock < 0) { + ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); + return ESP_FAIL; + } + int opt = 1; + setsockopt(server->listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + ESP_LOGI(TAG, "Socket created"); + + int err = bind(server->listen_sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); + if (err != 0) { + ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno); + ESP_LOGE(TAG, "IPPROTO: %d", addr_family); + close(server->listen_sock); + return ESP_FAIL; + } + ESP_LOGI(TAG, "Socket bound, port %d", PORT); + + err = listen(server->listen_sock, 1); + if (err != 0) { + ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno); + close(server->listen_sock); + return ESP_FAIL; + } + + ESP_LOGI(TAG, "Socket listening..."); + ESP_LOGI(TAG, "Execute player.py script"); + + struct sockaddr_storage source_addr; + socklen_t addr_len = sizeof(source_addr); + server->sock = accept(server->listen_sock, (struct sockaddr *)&source_addr, &addr_len); + if (server->sock < 0) { + ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno); + close(server->listen_sock); + return ESP_FAIL; + } + + // Convert ip address to string + if (source_addr.ss_family == PF_INET) { + inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1); + } + ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str); + + return ESP_OK; +} + +esp_err_t tcp_server_wait_for_connection(void) +{ + TaskHandle_t task_handle = NULL; + + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(start_mdns_service()); + ESP_ERROR_CHECK(example_connect()); + + tcp_server_t *server = calloc(1, sizeof(tcp_server_t)); + if (server == NULL) { + return ESP_ERR_NO_MEM; + } + + server->buffer = xRingbufferCreate(100000, RINGBUF_TYPE_BYTEBUF); + if ( server->buffer == NULL) { + free(server); + return ESP_ERR_NO_MEM;; + } + + if ( create_server(server) != ESP_OK) { + vRingbufferDelete(server->buffer); + free(server); + return ESP_FAIL; + } + + + BaseType_t task_created = xTaskCreate(sender_task, "sender_task", 4096, server, 10, &task_handle); + if (!task_created) { + socket_close(server); + vRingbufferDelete(server->buffer); + free(server); + return ESP_ERR_NO_MEM; + } + + s_server = server; + return ESP_OK; +} + +void tcp_server_close_when_done(void) +{ + if (s_server) { + s_server->close_request = true; + } +} + +#else + +esp_err_t tcp_server_wait_for_connection(void) +{ + return ESP_OK; +} + +esp_err_t tcp_server_send(uint8_t *payload, size_t size) +{ + return ESP_OK; +} + +void tcp_server_close_when_done(void) { } + +#endif diff --git a/examples/peripherals/usb/host/uvc/main/tcp_server.h b/examples/peripherals/usb/host/uvc/main/tcp_server.h new file mode 100644 index 00000000000..b4562a3fb70 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/main/tcp_server.h @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t tcp_server_wait_for_connection(void); + +esp_err_t tcp_server_send(uint8_t *payload, size_t size); + +void tcp_server_close_when_done(void); + +#ifdef __cplusplus +} +#endif diff --git a/examples/peripherals/usb/host/uvc/player.py b/examples/peripherals/usb/host/uvc/player.py new file mode 100644 index 00000000000..1340ce36f46 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/player.py @@ -0,0 +1,37 @@ +# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 +import socket + +import cv2 +import numpy as np + +frame_count = 0 +stream = bytearray() + +print('Connecting to server...') + +with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: + sock.connect(('esp-cam.local', 2222)) + + print('Receiving data ') + while True: + data = sock.recv(4096) + if not data: + break + stream += data + print('.', end='', flush=True) + + a = stream.find(b'\xff\xd8') + b = stream.find(b'\xff\xd9', a) + + if a != -1 and b != -1: + jpg = stream[a:b + 2] + stream = stream[b + 2:] + buffer = np.frombuffer(jpg, dtype=np.uint8) + image = cv2.imdecode(buffer, cv2.IMREAD_COLOR) + cv2.imshow('Stream', image) + if cv2.waitKey(10) == 27: + exit(0) + frame_count += 1 + +print('\nFrames received ', frame_count) diff --git a/examples/peripherals/usb/host/uvc/sdkconfig.defaults b/examples/peripherals/usb/host/uvc/sdkconfig.defaults new file mode 100644 index 00000000000..cb7325efc72 --- /dev/null +++ b/examples/peripherals/usb/host/uvc/sdkconfig.defaults @@ -0,0 +1,26 @@ +# +# SPIRAM +# +CONFIG_SPIRAM=y +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=150000 +CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP=y + +# +# USB +# +CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE=1024 +CONFIG_USB_HOST_HW_BUFFER_BIAS_IN=y + +# +# WIFI +# +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=8 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=8 +CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=8 +CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=16 +CONFIG_ESP32_WIFI_RX_BA_WIN=8 + +# +# LWIP +# +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=15000