Skip to content

能否把摄像头改成uvc摄像头 #122

@nmslnzdm

Description

@nmslnzdm

Checklist

  • Checked the issue tracker for similar issues to ensure this is not a duplicate.
  • Described the feature in detail and justified the reason for the request.
  • Provided specific use cases and examples.

Feature description

// Forward declaration
static esp_capture_err_t usb_video_src_open(esp_capture_video_src_if_t *src);
static esp_capture_err_t usb_video_src_get_support_codecs(esp_capture_video_src_if_t *src, const esp_capture_format_id_t **codecs, uint8_t *num);
static esp_capture_err_t usb_video_src_set_fixed_caps(esp_capture_video_src_if_t *src, const esp_capture_video_info_t *fixed_caps);
static esp_capture_err_t usb_video_src_negotiate_caps(esp_capture_video_src_if_t *src, esp_capture_video_info_t *in_caps, esp_capture_video_info_t *out_caps);
static esp_capture_err_t usb_video_src_start(esp_capture_video_src_if_t *src);
static esp_capture_err_t usb_video_src_acquire_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame);
static esp_capture_err_t usb_video_src_release_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame);
static esp_capture_err_t usb_video_src_stop(esp_capture_video_src_if_t *src);
static esp_capture_err_t usb_video_src_close(esp_capture_video_src_if_t *src);

// Supported codecs
static const esp_capture_format_id_t supported_codecs[] = {
ESP_CAPTURE_FMT_ID_MJPEG,
ESP_CAPTURE_FMT_ID_MJPEG,
};

static esp_capture_err_t usb_video_src_open(esp_capture_video_src_if_t *src)
{
ESP_LOGI(TAG, "USB video source open called");
return ESP_CAPTURE_ERR_OK;
}

static esp_capture_err_t usb_video_src_get_support_codecs(esp_capture_video_src_if_t *src, const esp_capture_format_id_t **codecs, uint8_t *num)
{
*codecs = supported_codecs;
*num = sizeof(supported_codecs) / sizeof(supported_codecs[0]);
return ESP_CAPTURE_ERR_OK;
}

static esp_capture_err_t usb_video_src_set_fixed_caps(esp_capture_video_src_if_t *src, const esp_capture_video_info_t *fixed_caps)
{
usb_video_src_t *usb_src = (usb_video_src_t *)src;
if (fixed_caps) {
memcpy(&usb_src->video_info, fixed_caps, sizeof(esp_capture_video_info_t));
}
return ESP_CAPTURE_ERR_OK;
}

static esp_capture_err_t usb_video_src_negotiate_caps(esp_capture_video_src_if_t *src, esp_capture_video_info_t *in_caps, esp_capture_video_info_t *out_caps)
{
(void)src; // Unused parameter

out_caps->format_id = ESP_CAPTURE_FMT_ID_MJPEG;
out_caps->width = VIDEO_WIDTH;
out_caps->height = VIDEO_HEIGHT;
out_caps->fps = VIDEO_FPS;

ESP_LOGI(TAG, "Negotiated caps: format=%d, %dx%d@%dfps (MJPEG->YUV420->H.264 pipeline)",
         out_caps->format_id, out_caps->width, out_caps->height, out_caps->fps);

return ESP_CAPTURE_ERR_OK;

}

static esp_capture_err_t usb_video_src_start(esp_capture_video_src_if_t *src)
{
usb_video_src_t *usb_src = (usb_video_src_t *)src;
if (usb_src->started) {
ESP_LOGI(TAG, "USB video source already started");
return ESP_CAPTURE_ERR_OK;
}
usb_src->started = true;
ESP_LOGI(TAG, "USB video source started - ready to receive MJPEG frames for direct streaming");
return ESP_CAPTURE_ERR_OK;
}

// Helper function to start video source (can be called externally)
static void start_usb_video_src_internal(void)
{
if (g_usb_video_src && !g_usb_video_src->started) {
usb_video_src_start((esp_capture_video_src_if_t *)g_usb_video_src);
}
}

static esp_capture_err_t usb_video_src_acquire_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame)
{
static int acquire_count = 0;
usb_video_src_t *usb_src = (usb_video_src_t *)src;

if (!usb_src->started) {
    ESP_LOGW(TAG, "USB video source not started, trying to start it...");
    usb_video_src_start(src);
    if (!usb_src->started) {
        ESP_LOGE(TAG, "Failed to start USB video source");
        return ESP_CAPTURE_ERR_INVALID_STATE;
    }
}

// Try to get a frame from queue (non-blocking)
queued_frame_t queued_frame;
if (xQueueReceive(usb_src->frame_queue, &queued_frame, pdMS_TO_TICKS(100)) == pdTRUE) {
    frame->stream_type = ESP_CAPTURE_STREAM_TYPE_VIDEO;
    frame->data = queued_frame.data;
    frame->size = queued_frame.size;
    frame->pts = queued_frame.pts;
    usb_src->current_frame_data = queued_frame.data;
    usb_src->current_frame_size = queued_frame.size;
    usb_src->current_frame_pts = queued_frame.pts;

    acquire_count++;
    if (acquire_count <= 5 || acquire_count % 30 == 0) {
        ESP_LOGI(TAG, "Frame acquired: size=%u, pts=%u, total=%d",
                 queued_frame.size, queued_frame.pts, acquire_count);
    }
    return ESP_CAPTURE_ERR_OK;
}

// No frame available
if (acquire_count < 5) {
    ESP_LOGW(TAG, "No frame available in queue (acquire_count=%d)", acquire_count);
}
return ESP_CAPTURE_ERR_TIMEOUT;

}

static esp_capture_err_t usb_video_src_release_frame(esp_capture_video_src_if_t *src, esp_capture_stream_frame_t *frame)
{
usb_video_src_t *usb_src = (usb_video_src_t *)src;

// Free the frame data if it was allocated
if (frame->data && frame->data == usb_src->current_frame_data) {
    heap_caps_free(frame->data);
    usb_src->current_frame_data = NULL;
}

return ESP_CAPTURE_ERR_OK;

}

static esp_capture_err_t usb_video_src_stop(esp_capture_video_src_if_t *src)
{
usb_video_src_t *usb_src = (usb_video_src_t *)src;
usb_src->started = false;
ESP_LOGI(TAG, "USB video source stopped");
return ESP_CAPTURE_ERR_OK;
}

static esp_capture_err_t usb_video_src_close(esp_capture_video_src_if_t *src)
{
ESP_LOGI(TAG, "USB video source close");
return ESP_CAPTURE_ERR_OK;
}

// Function to push frame from UVC callback
void media_sys_push_uvc_frame(uvc_frame_t *frame)
{
static int frame_push_count = 0;

if (!g_usb_video_src) {
    if (frame_push_count < 5) {
        ESP_LOGW(TAG, "g_usb_video_src is NULL, frame dropped (count=%d)", frame_push_count);
        frame_push_count++;
    }
    return;
}

if (!g_usb_video_src->started) {
    // Log first few times to debug
    if (frame_push_count < 5) {
        ESP_LOGW(TAG, "Video source not started yet, frame dropped (count=%d), started=%d",
                 frame_push_count, g_usb_video_src->started);
        frame_push_count++;
    }
    return;
}

uint8_t *frame_data = NULL;
size_t frame_size = 0;

if (frame->frame_format == UVC_FRAME_FORMAT_MJPEG) 
    frame_size = frame->data_bytes;
    frame_data = (uint8_t *)heap_caps_malloc(frame_size, MALLOC_CAP_8BIT);
    if (!frame_data) {
        ESP_LOGW(TAG, "Failed to allocate MJPEG frame buffer");
        return;
    }
    memcpy(frame_data, frame->data, frame_size);
} else {
    frame_size = frame->data_bytes;
    frame_data = (uint8_t *)heap_caps_malloc(frame_size, MALLOC_CAP_8BIT);
    if (!frame_data) {
        ESP_LOGW(TAG, "Failed to allocate frame buffer");
        return;
    }
    memcpy(frame_data, frame->data, frame_size);
}

queued_frame_t queued_frame = {
    .data = frame_data,
    .size = frame_size,
    .pts = (uint32_t)(esp_timer_get_time() / 1000)  // Convert to ms
};

// Send to queue (non-blocking, drop if queue is full)
BaseType_t queue_ret = xQueueSend(g_usb_video_src->frame_queue, &queued_frame, 0);
if (queue_ret != pdTRUE) {
    // Queue full, free the frame
    heap_caps_free(frame_data);
    if (frame_push_count % 30 == 0) {  // Log every 30 frames to avoid spam
        ESP_LOGW(TAG, "MJPEG frame queue full, dropping frame (total pushed: %d), queue size: %d",
                 frame_push_count, uxQueueSpacesAvailable(g_usb_video_src->frame_queue));
    }
} else {
    frame_push_count++;
    // Log first 10 frames after source started, then every 30 frames
    if (frame_push_count <= 10 || frame_push_count % 30 == 0) {
        ESP_LOGI(TAG, "MJPEG frame pushed to queue: size=%u, total=%d, queue free spaces: %d",
                 frame->data_bytes, frame_push_count,
                 uxQueueSpacesAvailable(g_usb_video_src->frame_queue));
    }
}

}

int media_sys_buildup(void)
{
ESP_LOGI(TAG, "Media system buildup - USB camera MJPEG direct streaming mode");

// Create USB video source
g_usb_video_src = (usb_video_src_t *)calloc(1, sizeof(usb_video_src_t));
if (!g_usb_video_src) {
    ESP_LOGE(TAG, "Failed to allocate USB video source");
    return -1;
}

// Initialize video source interface
g_usb_video_src->base.open = usb_video_src_open;
g_usb_video_src->base.get_support_codecs = usb_video_src_get_support_codecs;
g_usb_video_src->base.set_fixed_caps = usb_video_src_set_fixed_caps;
g_usb_video_src->base.negotiate_caps = usb_video_src_negotiate_caps;
g_usb_video_src->base.start = usb_video_src_start;
g_usb_video_src->base.acquire_frame = usb_video_src_acquire_frame;
g_usb_video_src->base.release_frame = usb_video_src_release_frame;
g_usb_video_src->base.stop = usb_video_src_stop;
g_usb_video_src->base.close = usb_video_src_close;

// Initialize video info (YUV420 format for H.264 encoding)
g_usb_video_src->video_info.format_id = ESP_CAPTURE_FMT_ID_MJPEG;
g_usb_video_src->video_info.width = 640;
g_usb_video_src->video_info.height = 480;
g_usb_video_src->video_info.fps = 15;

// Create frame queue (hold up to 3 frames)
g_usb_video_src->frame_queue = xQueueCreate(3, sizeof(queued_frame_t));
if (!g_usb_video_src->frame_queue) {
    ESP_LOGE(TAG, "Failed to create frame queue");
    free(g_usb_video_src);
    g_usb_video_src = NULL;
    return -1;
}

// Create esp_capture configuration
esp_capture_cfg_t capture_cfg = {
    .sync_mode = ESP_CAPTURE_SYNC_MODE_NONE,
    .audio_src = NULL,  // No audio for now
    .video_src = (esp_capture_video_src_if_t *)g_usb_video_src,
};

// Open capture
esp_capture_err_t ret = esp_capture_open(&capture_cfg, &capture_handle);
if (ret != ESP_CAPTURE_ERR_OK) {
    ESP_LOGE(TAG, "Failed to open capture: %d", ret);
    vQueueDelete(g_usb_video_src->frame_queue);
    free(g_usb_video_src);
    g_usb_video_src = NULL;
    return -1;
}

ESP_LOGI(TAG, "Media system initialized (USB camera), capture_handle=%p", capture_handle);
return 0;

}

Use cases

..

Alternatives

..

Additional context

...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions