IoTLabs

Nghiên cứu, Sáng tạo và Thử nghiệm

Series ESP-WebRTC: Dự Án USB Camera Bridge — Biến ESP32 Thành WebCam Online

Bạn có USB webcam muốn stream lên web mà không cần PC chạy 24/7? ESP32-P4 với USB 2.0 HS có thể làm bridge: nhận video từ USB cam và stream qua WebRTC — cần điện 5V là đủ.

Tại Sao ESP32-P4 Làm Được?

ESP32-S3 có USB nhưng chỉ USB 1.1 Full Speed (12Mbps) — không đủ bandwidth cho video 720p. ESP32-P4 có USB 2.0 High Speed (480Mbps) — đủ để đọc UVC webcam ở 720p@30fps.

UVC (USB Video Class) là chuẩn plug-and-play cho webcam — hầu hết USB camera đều hỗ trợ, không cần driver riêng.

Danh Sách Linh Kiện

Linh KiệnGhi Chú
ESP32-P4 EV BoardBắt buộc (USB 2.0 HS)
USB WebcamHỗ trợ UVC, MJPEG output, 720p
USB OTG adapterMicro-USB / USB-C OTG
Nguồn 5V 2ACấp riêng cho ESP32 và camera

Camera Tương Thích Test Được

  • Logitech C270 (640×480@30fps, MJPEG)
  • Logitech C920 (1080p@30fps, H.264)
  • Microsoft LifeCam HD-3000 (720p@30fps)
  • Camera clone no-brand từ Shopee (hầu hết UVC MJPEG 640×480)

Cấu Trúc Code

esp-webrtc-solution có demo sẵn solutions/usbcamerabridge/. Cấu trúc chính:

usb_camera_bridge/
├── main/
│   ├── app_main.c          ← Entry point
│   ├── uvc_camera.c        ← UVC driver wrapper
│   └── webrtc_bridge.c     ← Kết nối UVC → WebRTC
└── CMakeLists.txt

Code: UVC Camera Init

#include "usb/usb_host.h"
#include "usb/uvc_host.h"

static uvc_host_stream_hdl_t g_uvc_stream;

// Callback nhận frame từ USB camera
static bool uvc_frame_callback(const uvc_host_frame_t *frame, void *user_data) {
    if (frame->data_len == 0) return true;

    // Gửi frame vào WebRTC pipeline
    esp_webrtc_handle_t webrtc = (esp_webrtc_handle_t)user_data;
    esp_webrtc_video_frame_t vf = {
        .data     = frame->data,
        .data_len = frame->data_len,
        .pts      = esp_timer_get_time() / 1000,  // ms
        .codec    = ESP_WEBRTC_VIDEO_CODEC_MJPEG,  // UVC thường output MJPEG
    };
    esp_webrtc_push_video_frame(webrtc, &vf);
    return true;
}

static void init_uvc_camera(esp_webrtc_handle_t webrtc) {
    // Khởi tạo USB Host
    usb_host_config_t host_cfg = {
        .skip_phy_setup  = false,
        .intr_flags      = ESP_INTR_FLAG_LEVEL1,
    };
    usb_host_install(&host_cfg);

    // Cấu hình UVC stream
    const uvc_host_stream_config_t stream_cfg = {
        .event_cb = NULL,
        .frame_cb = uvc_frame_callback,
        .frame_cb_arg = webrtc,
        .vs_format = {
            .h_res   = 640,
            .v_res   = 480,
            .fps     = 30,
            .format  = UVC_VS_FORMAT_MJPEG,
        },
    };
    uvc_host_stream_open(&stream_cfg, portMAX_DELAY, &g_uvc_stream);
    uvc_host_stream_start(g_uvc_stream);
}

MJPEG → H.264 Transcoding (Tùy Chọn)

Nếu camera chỉ output MJPEG (phổ biến), ESP32-P4 có thể transcode sang H.264 bằng hardware:

// ESP-Capture có MJPEG → H.264 transcoder sử dụng HW
esp_capture_transcode_cfg_t transcode_cfg = {
    .input_codec  = CAPTURE_CODEC_MJPEG,
    .output_codec = CAPTURE_CODEC_H264,
    .width        = 640,
    .height       = 480,
    .fps          = 30,
    .bitrate      = 500000,  // 500kbps
};
esp_capture_add_transcoder(cap, &transcode_cfg);

Transcoding tốn thêm ~10% CPU nhưng giảm 5× bandwidth so với MJPEG raw.

Main App

void app_main(void) {
    // Wi-Fi
    wifi_init_sta(CONFIG_WIFI_SSID, CONFIG_WIFI_PASS);
    wifi_wait_connected();

    // WebRTC không cần esp_capture — push frame thủ công từ UVC callback
    esp_webrtc_cfg_t cfg = {
        .signaling = {
            .type     = ESP_WEBRTC_SIGNALING_WHIP,
            .whip_url = CONFIG_WHIP_URL,
        },
        .video_codec    = ESP_WEBRTC_VIDEO_CODEC_H264,
        .audio_codec    = ESP_WEBRTC_AUDIO_CODEC_G711A,  // Dùng G.711 nhẹ nếu không có mic tốt
        .manual_video_push = true,  // Bật chế độ push thủ công
    };
    esp_webrtc_handle_t webrtc;
    esp_webrtc_init(&cfg, &webrtc);
    esp_webrtc_start(webrtc);

    // Khởi tạo UVC camera, pass webrtc handle vào callback
    init_uvc_camera(webrtc);

    ESP_LOGI("bridge", "USB Camera Bridge running");
    // Main loop
    while (1) {
        vTaskDelay(pdMS_TO_TICKS(1000));
    }
}

📷 [Hình minh hoạ: Ảnh: USB webcam cắm vào ESP32-P4 EV Board qua OTG adapter, stream lên browser]

Latency So Sánh

PipelineLatency
Camera USB → ESP32 UVC~33ms (1 frame @30fps)
MJPEG → H.264 transcode~15ms
WebRTC encode + transmit~50ms
Tổng (LAN)~100ms
Tổng (WAN)~150–250ms

Xử Lý Camera Ngắt Kết Nối

USB camera có thể ngắt đột ngột (tháo, lỗi). Thêm reconnect logic:

static void uvc_event_cb(const uvc_host_driver_event_data_t *event, void *arg) {
    if (event->type == UVC_HOST_DRIVER_EVENT_DEVICE_DISCONNECTED) {
        ESP_LOGW("bridge", "USB Camera disconnected, waiting for reconnect...");
        g_uvc_stream = NULL;
        // WebRTC vẫn chạy nhưng không có video
        esp_webrtc_pause_video(g_webrtc);
    } else if (event->type == UVC_HOST_DEVICE_CONNECTED) {
        ESP_LOGI("bridge", "USB Camera reconnected");
        init_uvc_camera(g_webrtc);
        esp_webrtc_resume_video(g_webrtc);
    }
}

Ứng Dụng Thực Tế

  • Camera an ninh cũ lên stream: biến USB cam cũ thành IP camera không cần NVR
  • Streaming từ microscope USB: kết nối microscope kỹ thuật số vào WebRTC stream
  • Kiosk camera: tablet hoặc ESP32-P4 + USB cam làm camera kiosk
  • Kiểm soát từ xa máy móc: camera USB gắn ở máy CNC stream về dashboard

Bài tiếp theo: Tích Hợp OpenAI Realtime API — ESP32 Nói Chuyện Với GPT-4o