安装UVC Class驱动,识别摄像头并获取视频流
app/tasks/task_capture.c
↓(uvc_port_start)
↓
ports\uvc\uvc_port.c
uvc_port.c
esp_err_t uvc_port_start(uvc_frame_callback_t frame_cb,
uvc_state_callback_t state_cb,
void *ctx)
{
s_callbacks = (callbacks_t){.frame_cb = frame_cb, .state_cb = state_cb, .ctx = ctx};
s_frames = xQueueCreate(3, sizeof(uvc_host_frame_t *)); // uvc帧缓冲
if (!s_frames) {
return ESP_ERR_NO_MEM;
}
const uvc_host_driver_config_t config = {
.driver_task_stack_size = 6144,//UVC驱动后台任务栈大小
.driver_task_priority = 16, //UVC驱动后台任务的FreeRTOS优先级
.xCoreID = tskNO_AFFINITY, //表示不固定运行在哪一个CPU核心上,由FreeRTOS调度器安排
.create_background_task = true, //让官方UVC组件自己创建后台任务。
.event_cb = driver_connected, //当UVC驱动识别到符合标准的摄像头时调用driver_connected
};
esp_err_t err = uvc_host_install(&config); //主要是这里,安装驱动
if (err != ESP_OK) {
return err;
}
if (xTaskCreate(stream_task, "uvc_stream", 8192, NULL, 12, NULL) != pdPASS) { //持续获取流的任务
return ESP_ERR_NO_MEM;
}
return ESP_OK;
}
uvc_port_start该任务需要处理:
- UVC设备连接
- 描述符解析
- 传输完成事件
- UVC数据包
- 帧组装
- 断开事件
stream_task
static void stream_task(void *arg)
{
(void)arg;
const uvc_host_stream_config_t config = {
.event_cb = driver_event,
.frame_cb = driver_frame,
.user_ctx = &s_frames,
.usb = {
.dev_addr = UVC_HOST_ANY_DEV_ADDR,
.vid = UVC_HOST_ANY_VID,
.pid = UVC_HOST_ANY_PID,
.uvc_stream_index = 0,
},
.vs_format = {
.h_res = MEDIA_VIDEO_WIDTH,
.v_res = MEDIA_VIDEO_HEIGHT,
.fps = MEDIA_VIDEO_FPS,
.format = UVC_VS_FORMAT_YUY2,
},
.advanced = {
.number_of_frame_buffers = 3,
.frame_size = MEDIA_VIDEO_WIDTH * MEDIA_VIDEO_HEIGHT * 2,
.frame_heap_caps = MALLOC_CAP_SPIRAM,
.number_of_urbs = 4,
.urb_size = 16 * 1024,
},
};
for (;;) {
uvc_host_stream_hdl_t stream = NULL;
ESP_LOGI(TAG, "Waiting for YUY2 %dx%d@%d UVC camera",
MEDIA_VIDEO_WIDTH, MEDIA_VIDEO_HEIGHT, MEDIA_VIDEO_FPS);
esp_err_t err = uvc_host_stream_open(&config, pdMS_TO_TICKS(5000), &stream);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Open failed: %s", esp_err_to_name(err));
vTaskDelay(pdMS_TO_TICKS(2000));
continue;
}
s_connected = true;
if (s_callbacks.state_cb) {
s_callbacks.state_cb(true, s_callbacks.ctx);
}
err = uvc_host_stream_start(stream);
if (err != ESP_OK) {
(void)uvc_host_stream_close(stream);
s_connected = false;
if (s_callbacks.state_cb) {
s_callbacks.state_cb(false, s_callbacks.ctx);
}
continue;
}
while (s_connected) {
uvc_host_frame_t *frame = NULL;
if (xQueueReceive(s_frames, &frame, pdMS_TO_TICKS(500)) != pdPASS) {
continue;
}
if (s_callbacks.frame_cb && frame->vs_format.format == UVC_VS_FORMAT_YUY2) {
s_callbacks.frame_cb(frame->data, frame->data_len, s_callbacks.ctx);
}
(void)uvc_host_frame_return(stream, frame);
}
(void)uvc_host_stream_stop(stream);
uvc_host_frame_t *frame = NULL;
while (xQueueReceive(s_frames, &frame, 0) == pdPASS) {
(void)uvc_host_frame_return(stream, frame);
}
(void)uvc_host_stream_close(stream);
if (s_callbacks.state_cb) {
s_callbacks.state_cb(false, s_callbacks.ctx);
}
}
}