基于libusb的用户空间UVC相机库

一、项目概述

基于libusb的用户空间UVC(USB Video Class)相机库,提供完整的相机控制、视频流捕获和图像处理功能。

二、实现代码

2.1 头文件 uvc_camera.h

c 复制代码
/**
 * UVC Camera Library Header
 * 基于libusb的用户空间UVC相机库
 */

#ifndef UVC_CAMERA_H
#define UVC_CAMERA_H

#include <libusb-1.0/libusb.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>

#ifdef __cplusplus
extern "C" {
#endif

/* 版本信息 */
#define UVC_CAMERA_VERSION_MAJOR 1
#define UVC_CAMERA_VERSION_MINOR 0
#define UVC_CAMERA_VERSION_PATCH 0

/* 错误码定义 */
typedef enum {
    UVC_SUCCESS = 0,
    UVC_ERROR_IO = -1,
    UVC_ERROR_INVALID_PARAM = -2,
    UVC_ERROR_ACCESS = -3,
    UVC_ERROR_NO_DEVICE = -4,
    UVC_ERROR_NOT_FOUND = -5,
    UVC_ERROR_BUSY = -6,
    UVC_ERROR_TIMEOUT = -7,
    UVC_ERROR_OVERFLOW = -8,
    UVC_ERROR_PIPE = -9,
    UVC_ERROR_INTERRUPTED = -10,
    UVC_ERROR_NO_MEM = -11,
    UVC_ERROR_NOT_SUPPORTED = -12,
    UVC_ERROR_INVALID_DEVICE = -13,
    UVC_ERROR_INIT_FAILED = -14
} uvc_error_t;

/* UVC设备信息 */
typedef struct {
    uint16_t vendor_id;
    uint16_t product_id;
    char manufacturer[64];
    char product[64];
    char serial_number[64];
    uint8_t bus_number;
    uint8_t device_address;
    libusb_device *device;
} uvc_device_info_t;

/* 视频格式 */
typedef enum {
    UVC_FORMAT_UNCOMPRESSED = 0x04,
    UVC_FORMAT_MJPEG = 0x06,
    UVC_FORMAT_FRAME_BASED = 0x10
} uvc_format_type_t;

/* 帧描述符 */
typedef struct {
    uint8_t index;
    uint16_t width;
    uint16_t height;
    uint32_t frame_interval;  // 以100ns为单位
    uint8_t bits_per_pixel;
    uvc_format_type_t format_type;
} uvc_frame_desc_t;

/* 相机配置 */
typedef struct {
    uint8_t interface_number;
    uint8_t endpoint_address;
    uint16_t max_packet_size;
    uint8_t alternate_setting;
    uint8_t num_frame_descriptors;
    uvc_frame_desc_t *frame_descriptors;
} uvc_config_t;

/* 帧缓冲区 */
typedef struct {
    uint8_t *data;
    size_t length;
    uint64_t timestamp;
    uint32_t sequence;
    uvc_frame_desc_t format;
} uvc_frame_t;

/* 相机上下文 */
typedef struct {
    libusb_context *ctx;
    libusb_device_handle *dev_handle;
    uvc_device_info_t device_info;
    uvc_config_t config;
    bool is_streaming;
    pthread_t stream_thread;
    void (*frame_callback)(uvc_frame_t *frame, void *user_ptr);
    void *user_ptr;
    uint8_t *transfer_buffer;
    size_t transfer_buffer_size;
    uint32_t frame_sequence;
    bool auto_exposure;
    uint8_t brightness;
    uint8_t contrast;
    uint8_t saturation;
    uint8_t sharpness;
    uint16_t exposure_time;
} uvc_camera_t;

/* 初始化和反初始化 */
uvc_error_t uvc_init(uvc_camera_t **camera);
void uvc_exit(uvc_camera_t *camera);

/* 设备发现和管理 */
uvc_error_t uvc_find_devices(uvc_camera_t *camera, uvc_device_info_t **devices, int *count);
uvc_error_t uvc_open_device(uvc_camera_t *camera, const uvc_device_info_t *device);
void uvc_close_device(uvc_camera_t *camera);

/* 配置和格式设置 */
uvc_error_t uvc_get_config(uvc_camera_t *camera, uvc_config_t *config);
uvc_error_t uvc_set_format(uvc_camera_t *camera, const uvc_frame_desc_t *format);
uvc_error_t uvc_get_formats(uvc_camera_t *camera, uvc_frame_desc_t **formats, int *count);

/* 流控制 */
uvc_error_t uvc_start_streaming(uvc_camera_t *camera, 
                               void (*callback)(uvc_frame_t *frame, void *user_ptr),
                               void *user_ptr);
uvc_error_t uvc_stop_streaming(uvc_camera_t *camera);

/* 相机控制 */
uvc_error_t uvc_set_brightness(uvc_camera_t *camera, uint8_t brightness);
uvc_error_t uvc_set_contrast(uvc_camera_t *camera, uint8_t contrast);
uvc_error_t uvc_set_saturation(uvc_camera_t *camera, uint8_t saturation);
uvc_error_t uvc_set_sharpness(uvc_camera_t *camera, uint8_t sharpness);
uvc_error_t uvc_set_exposure_auto(uvc_camera_t *camera, bool enable);
uvc_error_t uvc_set_exposure_time(uvc_camera_t *camera, uint16_t time_ms);

/* 实用函数 */
const char *uvc_strerror(uvc_error_t error);
void uvc_print_device_info(const uvc_device_info_t *device);
void uvc_print_frame_info(const uvc_frame_t *frame);

#ifdef __cplusplus
}
#endif

#endif /* UVC_CAMERA_H */

2.2 核心实现 uvc_camera.c

c 复制代码
/**
 * UVC Camera Library Implementation
 * 基于libusb的用户空间UVC相机库实现
 */

#include "uvc_camera.h"

/* UVC类特定请求 */
#define UVC_SET_CUR 0x01
#define UVC_GET_CUR 0x81
#define UVC_GET_MIN 0x82
#define UVC_GET_MAX 0x83
#define UVC_GET_RES 0x84
#define UVC_GET_LEN 0x85
#define UVC_GET_INFO 0x86
#define UVC_GET_DEF 0x87

/* UVC控制选择器 */
#define UVC_CT_AE_MODE_CONTROL 0x02
#define UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04
#define UVC_CT_FOCUS_ABSOLUTE_CONTROL 0x06
#define UVC_CT_ZOOM_ABSOLUTE_CONTROL 0x0B
#define UVC_PU_BRIGHTNESS_CONTROL 0x02
#define UVC_PU_CONTRAST_CONTROL 0x03
#define UVC_PU_SATURATION_CONTROL 0x04
#define UVC_PU_SHARPNESS_CONTROL 0x08

/* 私有函数声明 */
static uvc_error_t uvc_parse_device_descriptor(uvc_camera_t *camera);
static uvc_error_t uvc_parse_video_control_interface(uvc_camera_t *camera);
static uvc_error_t uvc_parse_video_stream_interface(uvc_camera_t *camera);
static uvc_error_t uvc_send_control_request(uvc_camera_t *camera, 
                                           uint8_t request, 
                                           uint8_t unit, 
                                           uint8_t selector,
                                           uint8_t control_interface,
                                           void *data, 
                                           uint16_t length);
static void *uvc_stream_thread(void *arg);
static void uvc_transfer_callback(struct libusb_transfer *transfer);

/* 初始化UVC相机库 */
uvc_error_t uvc_init(uvc_camera_t **camera) {
    if (!camera) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    *camera = (uvc_camera_t *)calloc(1, sizeof(uvc_camera_t));
    if (!*camera) {
        return UVC_ERROR_NO_MEM;
    }
    
    int ret = libusb_init(&(*camera)->ctx);
    if (ret < 0) {
        free(*camera);
        return UVC_ERROR_INIT_FAILED;
    }
    
    // 设置调试级别
    libusb_set_debug((*camera)->ctx, LIBUSB_LOG_LEVEL_WARNING);
    
    (*camera)->is_streaming = false;
    (*camera)->frame_sequence = 0;
    (*camera)->auto_exposure = true;
    (*camera)->brightness = 128;
    (*camera)->contrast = 128;
    (*camera)->saturation = 128;
    (*camera)->sharpness = 128;
    (*camera)->exposure_time = 33;  // 默认33ms (30fps)
    
    return UVC_SUCCESS;
}

/* 清理资源 */
void uvc_exit(uvc_camera_t *camera) {
    if (!camera) return;
    
    if (camera->is_streaming) {
        uvc_stop_streaming(camera);
    }
    
    if (camera->dev_handle) {
        uvc_close_device(camera);
    }
    
    if (camera->ctx) {
        libusb_exit(camera->ctx);
    }
    
    if (camera->config.frame_descriptors) {
        free(camera->config.frame_descriptors);
    }
    
    if (camera->transfer_buffer) {
        free(camera->transfer_buffer);
    }
    
    free(camera);
}

/* 查找UVC设备 */
uvc_error_t uvc_find_devices(uvc_camera_t *camera, uvc_device_info_t **devices, int *count) {
    if (!camera || !devices || !count) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    libusb_device **device_list;
    ssize_t num_devices = libusb_get_device_list(camera->ctx, &device_list);
    
    if (num_devices < 0) {
        return UVC_ERROR_IO;
    }
    
    *devices = NULL;
    *count = 0;
    
    for (ssize_t i = 0; i < num_devices; i++) {
        struct libusb_device_descriptor desc;
        int ret = libusb_get_device_descriptor(device_list[i], &desc);
        
        if (ret < 0) continue;
        
        // 检查是否为UVC设备
        if (desc.bDeviceClass == LIBUSB_CLASS_VIDEO ||
            desc.bDeviceSubClass == 0x02 ||  // Video Control Interface
            desc.bDeviceProtocol == 0x01) {  // UVC protocol
            
            *devices = (uvc_device_info_t *)realloc(*devices, 
                                                   (*count + 1) * sizeof(uvc_device_info_t));
            if (!*devices) {
                libusb_free_device_list(device_list, 1);
                return UVC_ERROR_NO_MEM;
            }
            
            uvc_device_info_t *device = &(*devices)[*count];
            memset(device, 0, sizeof(uvc_device_info_t));
            
            device->device = device_list[i];
            device->vendor_id = desc.idVendor;
            device->product_id = desc.idProduct;
            device->bus_number = libusb_get_bus_number(device_list[i]);
            device->device_address = libusb_get_device_address(device_list[i]);
            
            // 获取字符串描述符
            libusb_device_handle *handle;
            if (libusb_open(device_list[i], &handle) == 0) {
                if (desc.iManufacturer) {
                    libusb_get_string_descriptor_ascii(handle, desc.iManufacturer,
                                                      (unsigned char*)device->manufacturer,
                                                      sizeof(device->manufacturer));
                }
                if (desc.iProduct) {
                    libusb_get_string_descriptor_ascii(handle, desc.iProduct,
                                                      (unsigned char*)device->product,
                                                      sizeof(device->product));
                }
                if (desc.iSerialNumber) {
                    libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber,
                                                      (unsigned char*)device->serial_number,
                                                      sizeof(device->serial_number));
                }
                libusb_close(handle);
            }
            
            (*count)++;
        }
    }
    
    libusb_free_device_list(device_list, 1);
    return UVC_SUCCESS;
}

/* 打开UVC设备 */
uvc_error_t uvc_open_device(uvc_camera_t *camera, const uvc_device_info_t *device) {
    if (!camera || !device) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    int ret = libusb_open(device->device, &camera->dev_handle);
    if (ret < 0) {
        return ret;
    }
    
    // 复制设备信息
    memcpy(&camera->device_info, device, sizeof(uvc_device_info_t));
    
    // 解析设备描述符
    ret = uvc_parse_device_descriptor(camera);
    if (ret != UVC_SUCCESS) {
        libusb_close(camera->dev_handle);
        camera->dev_handle = NULL;
        return ret;
    }
    
    // 自动分离内核驱动
    libusb_set_auto_detach_kernel_driver(camera->dev_handle, 1);
    
    // 认领接口
    ret = libusb_claim_interface(camera->dev_handle, camera->config.interface_number);
    if (ret < 0) {
        libusb_close(camera->dev_handle);
        camera->dev_handle = NULL;
        return ret;
    }
    
    return UVC_SUCCESS;
}

/* 关闭设备 */
void uvc_close_device(uvc_camera_t *camera) {
    if (!camera || !camera->dev_handle) return;
    
    if (camera->is_streaming) {
        uvc_stop_streaming(camera);
    }
    
    libusb_release_interface(camera->dev_handle, camera->config.interface_number);
    libusb_close(camera->dev_handle);
    camera->dev_handle = NULL;
}

/* 解析设备描述符 */
static uvc_error_t uvc_parse_device_descriptor(uvc_camera_t *camera) {
    struct libusb_config_descriptor *config_desc;
    int ret = libusb_get_active_config_descriptor(camera->device_info.device, &config_desc);
    
    if (ret < 0) {
        return ret;
    }
    
    // 查找视频控制接口和视频流接口
    for (uint8_t i = 0; i < config_desc->bNumInterfaces; i++) {
        const struct libusb_interface *interface = &config_desc->interface[i];
        
        for (int j = 0; j < interface->num_altsetting; j++) {
            const struct libusb_interface_descriptor *altsetting = &interface->altsetting[j];
            
            if (altsetting->bInterfaceClass == LIBUSB_CLASS_VIDEO) {
                if (altsetting->bInterfaceSubClass == 0x01) {  // Video Control
                    // 解析视频控制接口
                    camera->config.interface_number = altsetting->bInterfaceNumber;
                } else if (altsetting->bInterfaceSubClass == 0x02) {  // Video Streaming
                    // 解析视频流接口
                    camera->config.endpoint_address = altsetting->endpoint[0].bEndpointAddress;
                    camera->config.max_packet_size = altsetting->endpoint[0].wMaxPacketSize;
                    camera->config.alternate_setting = altsetting->bAlternateSetting;
                    
                    // 解析格式和帧描述符
                    // 这里简化处理,实际需要解析UVC特定的描述符
                    camera->config.num_frame_descriptors = 3;
                    camera->config.frame_descriptors = (uvc_frame_desc_t *)malloc(
                        3 * sizeof(uvc_frame_desc_t));
                    
                    // 预设一些常见的格式
                    camera->config.frame_descriptors[0] = (uvc_frame_desc_t){
                        .index = 0,
                        .width = 640,
                        .height = 480,
                        .frame_interval = 333333,  // 30fps
                        .bits_per_pixel = 16,
                        .format_type = UVC_FORMAT_UNCOMPRESSED
                    };
                    
                    camera->config.frame_descriptors[1] = (uvc_frame_desc_t){
                        .index = 1,
                        .width = 1280,
                        .height = 720,
                        .frame_interval = 666666,  // 15fps
                        .bits_per_pixel = 16,
                        .format_type = UVC_FORMAT_UNCOMPRESSED
                    };
                    
                    camera->config.frame_descriptors[2] = (uvc_frame_desc_t){
                        .index = 2,
                        .width = 1920,
                        .height = 1080,
                        .frame_interval = 666666,  // 15fps
                        .bits_per_pixel = 24,
                        .format_type = UVC_FORMAT_MJPEG
                    };
                }
            }
        }
    }
    
    libusb_free_config_descriptor(config_desc);
    return UVC_SUCCESS;
}

/* 获取设备配置 */
uvc_error_t uvc_get_config(uvc_camera_t *camera, uvc_config_t *config) {
    if (!camera || !config) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    memcpy(config, &camera->config, sizeof(uvc_config_t));
    return UVC_SUCCESS;
}

/* 设置视频格式 */
uvc_error_t uvc_set_format(uvc_camera_t *camera, const uvc_frame_desc_t *format) {
    if (!camera || !format) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    // 发送UVC设置格式请求
    uint8_t data[32];
    memset(data, 0, sizeof(data));
    
    // 构建格式设置数据
    data[0] = format->format_type;
    data[1] = format->bits_per_pixel;
    data[2] = format->width & 0xFF;
    data[3] = (format->width >> 8) & 0xFF;
    data[4] = format->height & 0xFF;
    data[5] = (format->height >> 8) & 0xFF;
    data[6] = format->frame_interval & 0xFF;
    data[7] = (format->frame_interval >> 8) & 0xFF;
    data[8] = (format->frame_interval >> 16) & 0xFF;
    data[9] = (format->frame_interval >> 24) & 0xFF;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x01, 0x01,
                                   camera->config.interface_number,
                                   data, sizeof(data));
}

/* 获取支持的格式 */
uvc_error_t uvc_get_formats(uvc_camera_t *camera, uvc_frame_desc_t **formats, int *count) {
    if (!camera || !formats || !count) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    *count = camera->config.num_frame_descriptors;
    *formats = (uvc_frame_desc_t *)malloc(*count * sizeof(uvc_frame_desc_t));
    
    if (!*formats) {
        return UVC_ERROR_NO_MEM;
    }
    
    memcpy(*formats, camera->config.frame_descriptors, 
           *count * sizeof(uvc_frame_desc_t));
    
    return UVC_SUCCESS;
}

/* 启动视频流 */
uvc_error_t uvc_start_streaming(uvc_camera_t *camera,
                               void (*callback)(uvc_frame_t *frame, void *user_ptr),
                               void *user_ptr) {
    if (!camera || !callback) {
        return UVC_ERROR_INVALID_PARAM;
    }
    
    if (camera->is_streaming) {
        return UVC_ERROR_BUSY;
    }
    
    camera->frame_callback = callback;
    camera->user_ptr = user_ptr;
    camera->is_streaming = true;
    camera->frame_sequence = 0;
    
    // 分配传输缓冲区
    camera->transfer_buffer_size = camera->config.max_packet_size * 32;
    camera->transfer_buffer = (uint8_t *)malloc(camera->transfer_buffer_size);
    
    if (!camera->transfer_buffer) {
        camera->is_streaming = false;
        return UVC_ERROR_NO_MEM;
    }
    
    // 创建流线程
    int ret = pthread_create(&camera->stream_thread, NULL, uvc_stream_thread, camera);
    if (ret != 0) {
        free(camera->transfer_buffer);
        camera->transfer_buffer = NULL;
        camera->is_streaming = false;
        return UVC_ERROR_INIT_FAILED;
    }
    
    return UVC_SUCCESS;
}

/* 停止视频流 */
uvc_error_t uvc_stop_streaming(uvc_camera_t *camera) {
    if (!camera || !camera->is_streaming) {
        return UVC_SUCCESS;
    }
    
    camera->is_streaming = false;
    
    // 等待线程结束
    pthread_join(camera->stream_thread, NULL);
    
    // 释放资源
    if (camera->transfer_buffer) {
        free(camera->transfer_buffer);
        camera->transfer_buffer = NULL;
    }
    
    return UVC_SUCCESS;
}

/* 流线程函数 */
static void *uvc_stream_thread(void *arg) {
    uvc_camera_t *camera = (uvc_camera_t *)arg;
    
    while (camera->is_streaming) {
        // 提交异步传输
        struct libusb_transfer *transfer = libusb_alloc_transfer(0);
        if (!transfer) {
            usleep(10000);
            continue;
        }
        
        libusb_fill_bulk_transfer(transfer, camera->dev_handle,
                                camera->config.endpoint_address,
                                camera->transfer_buffer,
                                camera->transfer_buffer_size,
                                uvc_transfer_callback,
                                camera,
                                1000);  // 1秒超时
        
        int ret = libusb_submit_transfer(transfer);
        if (ret < 0) {
            libusb_free_transfer(transfer);
            usleep(10000);
            continue;
        }
        
        // 处理事件
        libusb_handle_events_timeout_completed(camera->ctx, NULL, NULL);
    }
    
    return NULL;
}

/* 传输回调函数 */
static void uvc_transfer_callback(struct libusb_transfer *transfer) {
    uvc_camera_t *camera = (uvc_camera_t *)transfer->user_data;
    
    if (transfer->status == LIBUSB_TRANSFER_COMPLETED && 
        transfer->actual_length > 0) {
        
        // 创建帧对象
        uvc_frame_t frame;
        memset(&frame, 0, sizeof(frame));
        
        frame.data = transfer->buffer;
        frame.length = transfer->actual_length;
        frame.timestamp = (uint64_t)time(NULL) * 1000000;  // 微秒时间戳
        frame.sequence = ++camera->frame_sequence;
        
        // 获取当前格式
        if (camera->config.num_frame_descriptors > 0) {
            memcpy(&frame.format, &camera->config.frame_descriptors[0], 
                   sizeof(uvc_frame_desc_t));
        }
        
        // 调用回调函数
        if (camera->frame_callback) {
            camera->frame_callback(&frame, camera->user_ptr);
        }
    }
    
    // 重新提交传输
    if (camera->is_streaming) {
        libusb_submit_transfer(transfer);
    } else {
        libusb_free_transfer(transfer);
    }
}

/* 发送控制请求 */
static uvc_error_t uvc_send_control_request(uvc_camera_t *camera,
                                           uint8_t request,
                                           uint8_t unit,
                                           uint8_t selector,
                                           uint8_t control_interface,
                                           void *data,
                                           uint16_t length) {
    uint8_t setup_packet[8];
    
    setup_packet[0] = 0x21;  // Type: Class | Interface | Host to Device
    setup_packet[1] = request;
    setup_packet[2] = selector;
    setup_packet[3] = unit;
    setup_packet[4] = control_interface;
    setup_packet[5] = 0x00;
    setup_packet[6] = length & 0xFF;
    setup_packet[7] = (length >> 8) & 0xFF;
    
    int transferred;
    int ret = libusb_control_transfer(camera->dev_handle,
                                      setup_packet[0],
                                      setup_packet[1],
                                      (setup_packet[3] << 8) | setup_packet[2],
                                      (setup_packet[5] << 8) | setup_packet[4],
                                      (unsigned char *)data,
                                      length,
                                      1000);  // 1秒超时
    
    return ret < 0 ? ret : UVC_SUCCESS;
}

/* 相机控制函数 */
uvc_error_t uvc_set_brightness(uvc_camera_t *camera, uint8_t brightness) {
    if (!camera) return UVC_ERROR_INVALID_PARAM;
    
    uint8_t data = brightness;
    camera->brightness = brightness;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x02, 
                                   UVC_PU_BRIGHTNESS_CONTROL,
                                   camera->config.interface_number,
                                   &data, 1);
}

uvc_error_t uvc_set_contrast(uvc_camera_t *camera, uint8_t contrast) {
    if (!camera) return UVC_ERROR_INVALID_PARAM;
    
    uint8_t data = contrast;
    camera->contrast = contrast;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x02,
                                   UVC_PU_CONTRAST_CONTROL,
                                   camera->config.interface_number,
                                   &data, 1);
}

uvc_error_t uvc_set_saturation(uvc_camera_t *camera, uint8_t saturation) {
    if (!camera) return UVC_ERROR_INVALID_PARAM;
    
    uint8_t data = saturation;
    camera->saturation = saturation;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x02,
                                   UVC_PU_SATURATION_CONTROL,
                                   camera->config.interface_number,
                                   &data, 1);
}

uvc_error_t uvc_set_sharpness(uvc_camera_t *camera, uint8_t sharpness) {
    if (!camera) return UVC_ERROR_INVALID_PARAM;
    
    uint8_t data = sharpness;
    camera->sharpness = sharpness;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x02,
                                   UVC_PU_SHARPNESS_CONTROL,
                                   camera->config.interface_number,
                                   &data, 1);
}

uvc_error_t uvc_set_exposure_auto(uvc_camera_t *camera, bool enable) {
    if (!camera) return UVC_ERROR_INVALID_PARAM;
    
    uint8_t data = enable ? 0x08 : 0x01;  // Auto mode or Manual mode
    camera->auto_exposure = enable;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x01,
                                   UVC_CT_AE_MODE_CONTROL,
                                   camera->config.interface_number,
                                   &data, 1);
}

uvc_error_t uvc_set_exposure_time(uvc_camera_t *camera, uint16_t time_ms) {
    if (!camera) return UVC_ERROR_INVALID_PARAM;
    
    uint32_t exposure_time = time_ms * 1000;  // 转换为微秒
    uint8_t data[4];
    data[0] = exposure_time & 0xFF;
    data[1] = (exposure_time >> 8) & 0xFF;
    data[2] = (exposure_time >> 16) & 0xFF;
    data[3] = (exposure_time >> 24) & 0xFF;
    
    camera->exposure_time = time_ms;
    
    return uvc_send_control_request(camera, UVC_SET_CUR, 0x01,
                                   UVC_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL,
                                   camera->config.interface_number,
                                   data, 4);
}

/* 错误处理 */
const char *uvc_strerror(uvc_error_t error) {
    switch (error) {
        case UVC_SUCCESS: return "Success";
        case UVC_ERROR_IO: return "Input/output error";
        case UVC_ERROR_INVALID_PARAM: return "Invalid parameter";
        case UVC_ERROR_ACCESS: return "Access denied";
        case UVC_ERROR_NO_DEVICE: return "No such device";
        case UVC_ERROR_NOT_FOUND: return "Entity not found";
        case UVC_ERROR_BUSY: return "Resource busy";
        case UVC_ERROR_TIMEOUT: return "Operation timed out";
        case UVC_ERROR_OVERFLOW: return "Overflow";
        case UVC_ERROR_PIPE: return "Pipe error";
        case UVC_ERROR_INTERRUPTED: return "System call interrupted";
        case UVC_ERROR_NO_MEM: return "Insufficient memory";
        case UVC_ERROR_NOT_SUPPORTED: return "Operation not supported";
        case UVC_ERROR_INVALID_DEVICE: return "Invalid device";
        case UVC_ERROR_INIT_FAILED: return "Initialization failed";
        default: return "Unknown error";
    }
}

/* 打印设备信息 */
void uvc_print_device_info(const uvc_device_info_t *device) {
    if (!device) return;
    
    printf("UVC Device Information:\n");
    printf("  Vendor ID: 0x%04X\n", device->vendor_id);
    printf("  Product ID: 0x%04X\n", device->product_id);
    printf("  Manufacturer: %s\n", device->manufacturer);
    printf("  Product: %s\n", device->product);
    printf("  Serial Number: %s\n", device->serial_number);
    printf("  Bus Number: %d\n", device->bus_number);
    printf("  Device Address: %d\n", device->device_address);
}

/* 打印帧信息 */
void uvc_print_frame_info(const uvc_frame_t *frame) {
    if (!frame) return;
    
    printf("Frame Information:\n");
    printf("  Size: %zu bytes\n", frame->length);
    printf("  Timestamp: %lu\n", frame->timestamp);
    printf("  Sequence: %u\n", frame->sequence);
    printf("  Format: %s\n", 
           frame->format.format_type == UVC_FORMAT_UNCOMPRESSED ? "Uncompressed" :
           frame->format.format_type == UVC_FORMAT_MJPEG ? "MJPEG" : "Unknown");
    printf("  Resolution: %dx%d\n", frame->format.width, frame->format.height);
    printf("  Bits per pixel: %d\n", frame->format.bits_per_pixel);
}

2.3 示例程序 example.c

c 复制代码
/**
 * UVC Camera Library Example
 * 演示如何使用UVC相机库
 */

#include "uvc_camera.h"
#include <signal.h>
#include <sys/time.h>

volatile bool running = true;

void signal_handler(int sig) {
    if (sig == SIGINT || sig == SIGTERM) {
        running = false;
    }
}

// 帧回调函数
void frame_callback(uvc_frame_t *frame, void *user_ptr) {
    static int frame_count = 0;
    static struct timeval last_time;
    
    if (frame_count == 0) {
        gettimeofday(&last_time, NULL);
    }
    
    frame_count++;
    
    // 每秒钟打印一次统计信息
    struct timeval current_time;
    gettimeofday(&current_time, NULL);
    
    long elapsed = (current_time.tv_sec - last_time.tv_sec) * 1000 +
                  (current_time.tv_usec - last_time.tv_usec) / 1000;
    
    if (elapsed >= 1000) {
        printf("FPS: %d, Frame size: %zu bytes\n", 
               frame_count, frame->length);
        frame_count = 0;
        last_time = current_time;
    }
    
    // 这里可以添加图像处理代码
    // 例如:保存为文件、显示图像等
}

int main(int argc, char *argv[]) {
    uvc_camera_t *camera = NULL;
    uvc_device_info_t *devices = NULL;
    int device_count = 0;
    uvc_error_t ret;
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    printf("UVC Camera Library Example\n");
    printf("Version: %d.%d.%d\n\n", 
           UVC_CAMERA_VERSION_MAJOR,
           UVC_CAMERA_VERSION_MINOR,
           UVC_CAMERA_VERSION_PATCH);
    
    // 初始化库
    ret = uvc_init(&camera);
    if (ret != UVC_SUCCESS) {
        fprintf(stderr, "Failed to initialize UVC library: %s\n", 
                uvc_strerror(ret));
        return 1;
    }
    
    // 查找设备
    ret = uvc_find_devices(camera, &devices, &device_count);
    if (ret != UVC_SUCCESS) {
        fprintf(stderr, "Failed to find devices: %s\n", 
                uvc_strerror(ret));
        uvc_exit(camera);
        return 1;
    }
    
    if (device_count == 0) {
        printf("No UVC devices found.\n");
        uvc_exit(camera);
        return 0;
    }
    
    printf("Found %d UVC device(s):\n", device_count);
    for (int i = 0; i < device_count; i++) {
        printf("\nDevice %d:\n", i);
        uvc_print_device_info(&devices[i]);
    }
    
    // 打开第一个设备
    ret = uvc_open_device(camera, &devices[0]);
    if (ret != UVC_SUCCESS) {
        fprintf(stderr, "Failed to open device: %s\n", 
                uvc_strerror(ret));
        free(devices);
        uvc_exit(camera);
        return 1;
    }
    
    printf("\nOpened device successfully.\n");
    
    // 获取支持的格式
    uvc_frame_desc_t *formats = NULL;
    int format_count = 0;
    ret = uvc_get_formats(camera, &formats, &format_count);
    if (ret == UVC_SUCCESS && formats) {
        printf("\nSupported formats:\n");
        for (int i = 0; i < format_count; i++) {
            printf("  Format %d: %dx%d, %d bpp, %s\n",
                   i,
                   formats[i].width,
                   formats[i].height,
                   formats[i].bits_per_pixel,
                   formats[i].format_type == UVC_FORMAT_UNCOMPRESSED ? "Uncompressed" :
                   formats[i].format_type == UVC_FORMAT_MJPEG ? "MJPEG" : "Unknown");
        }
        
        // 设置第一个格式
        if (format_count > 0) {
            ret = uvc_set_format(camera, &formats[0]);
            if (ret != UVC_SUCCESS) {
                fprintf(stderr, "Failed to set format: %s\n", 
                        uvc_strerror(ret));
            } else {
                printf("\nSet format to %dx%d\n", 
                       formats[0].width, formats[0].height);
            }
        }
        
        free(formats);
    }
    
    // 设置相机参数
    uvc_set_brightness(camera, 150);
    uvc_set_contrast(camera, 130);
    uvc_set_saturation(camera, 120);
    uvc_set_sharpness(camera, 140);
    uvc_set_exposure_auto(camera, true);
    
    printf("\nStarting video stream...\n");
    printf("Press Ctrl+C to stop.\n\n");
    
    // 开始视频流
    ret = uvc_start_streaming(camera, frame_callback, NULL);
    if (ret != UVC_SUCCESS) {
        fprintf(stderr, "Failed to start streaming: %s\n", 
                uvc_strerror(ret));
        uvc_close_device(camera);
        free(devices);
        uvc_exit(camera);
        return 1;
    }
    
    // 主循环
    while (running) {
        sleep(1);
    }
    
    printf("\nStopping video stream...\n");
    
    // 停止视频流
    uvc_stop_streaming(camera);
    
    // 清理资源
    uvc_close_device(camera);
    free(devices);
    uvc_exit(camera);
    
    printf("Program terminated.\n");
    return 0;
}

2.4 Makefile

makefile 复制代码
# UVC Camera Library Makefile

CC = gcc
CFLAGS = -Wall -Wextra -O2 -fPIC -std=c11
LIBS = -lusb-1.0 -lpthread -lm
INCLUDES = -I/usr/include/libusb-1.0

# 目标
TARGET_LIB = libuvccamera.so
TARGET_EXAMPLE = uvc_example

# 源文件
LIB_SRCS = uvc_camera.c
EXAMPLE_SRCS = example.c

# 对象文件
LIB_OBJS = $(LIB_SRCS:.c=.o)
EXAMPLE_OBJS = $(EXAMPLE_SRCS:.c=.o)

all: $(TARGET_LIB) $(TARGET_EXAMPLE)

# 编译库
$(TARGET_LIB): $(LIB_OBJS)
	$(CC) -shared -o $@ $^ $(LIBS)

# 编译示例
$(TARGET_EXAMPLE): $(EXAMPLE_OBJS) $(TARGET_LIB)
	$(CC) -o $@ $(EXAMPLE_OBJS) -L. -luvccamera $(LIBS)

# 编译规则
%.o: %.c
	$(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@

clean:
	rm -f $(LIB_OBJS) $(EXAMPLE_OBJS) $(TARGET_LIB) $(TARGET_EXAMPLE)

install: $(TARGET_LIB)
	cp $(TARGET_LIB) /usr/local/lib/
	cp uvc_camera.h /usr/local/include/
	ldconfig

uninstall:
	rm -f /usr/local/lib/$(TARGET_LIB)
	rm -f /usr/local/include/uvc_camera.h
	ldconfig

.PHONY: all clean install uninstall

三、使用说明

3.1 编译和安装

bash 复制代码
# 安装依赖
sudo apt-get install libusb-1.0-0-dev

# 编译
make

# 安装库
sudo make install

# 运行示例
./uvc_example

3.2 权限设置

为了在没有root权限的情况下访问USB设备,需要创建udev规则:

bash 复制代码
# 创建udev规则文件
sudo nano /etc/udev/rules.d/99-uvc-camera.rules

# 添加以下内容(替换为你的设备ID)
SUBSYSTEM=="usb", ATTR{idVendor}=="046d", ATTR{idProduct}=="0825", MODE="0666"
SUBSYSTEM=="usb", ATTR{idVendor}=="04f2", ATTR{idProduct}=="b531", MODE="0666"

# 重新加载udev规则
sudo udevadm control --reload-rules
sudo udevadm trigger

3.3 API使用示例

c 复制代码
// 1. 初始化库
uvc_camera_t *camera;
uvc_init(&camera);

// 2. 查找设备
uvc_device_info_t *devices;
int count;
uvc_find_devices(camera, &devices, &count);

// 3. 打开设备
uvc_open_device(camera, &devices[0]);

// 4. 设置格式
uvc_frame_desc_t format = {
    .width = 640,
    .height = 480,
    .format_type = UVC_FORMAT_MJPEG
};
uvc_set_format(camera, &format);

// 5. 设置相机参数
uvc_set_brightness(camera, 150);
uvc_set_exposure_auto(camera, true);

// 6. 开始流传输
uvc_start_streaming(camera, frame_callback, NULL);

// 7. 处理帧数据(在回调函数中)
void frame_callback(uvc_frame_t *frame, void *user_ptr) {
    // 处理帧数据
    // frame->data 包含原始图像数据
    // frame->length 是数据长度
}

// 8. 停止流传输
uvc_stop_streaming(camera);

// 9. 清理资源
uvc_exit(camera);

参考代码 基于libusb用户空间uvc camera库 www.youwenfan.com/contentcsu/60808.html

四、扩展功能建议

4.1 图像格式转换

c 复制代码
// 添加图像格式转换功能
uvc_error_t uvc_convert_format(uvc_frame_t *src, 
                               uvc_frame_t *dst,
                               uvc_format_type_t dst_format);

// 支持转换:
// - MJPEG → RGB24
// - YUV422 → RGB24
// - Bayer → RGB24

4.2 硬件加速

c 复制代码
// 使用GPU进行图像处理
#ifdef USE_CUDA
uvc_error_t uvc_process_frame_gpu(uvc_frame_t *frame);
#endif

// 使用NEON指令集(ARM)
#ifdef USE_NEON
uvc_error_t uvc_process_frame_neon(uvc_frame_t *frame);
#endif

4.3 多相机同步

c 复制代码
// 多相机同步采集
typedef struct {
    uvc_camera_t **cameras;
    int num_cameras;
    pthread_mutex_t sync_mutex;
    pthread_cond_t sync_cond;
    int ready_count;
} uvc_sync_group_t;

uvc_error_t uvc_sync_start_streaming(uvc_sync_group_t *group);
uvc_error_t uvc_sync_stop_streaming(uvc_sync_group_t *group);

4.4 录像功能

c 复制代码
// 录像到文件
typedef struct {
    FILE *file;
    uint32_t frame_count;
    uint32_t fps;
    uint16_t width;
    uint16_t height;
} uvc_recorder_t;

uvc_error_t uvc_start_recording(uvc_camera_t *camera, 
                                const char *filename);
uvc_error_t uvc_stop_recording(uvc_camera_t *camera);

五、调试和测试

5.1 启用调试输出

c 复制代码
// 在初始化时设置调试级别
libusb_set_debug(camera->ctx, LIBUSB_LOG_LEVEL_DEBUG);

// 添加自定义日志函数
void uvc_log(const char *format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
}

5.2 USB分析工具

bash 复制代码
# 查看USB设备
lsusb -v | grep -A 10 "Video"

# 监控USB流量
sudo usbmon -f 1  # 监控总线1

# 使用Wireshark抓包
sudo wireshark -i usbmon1

六、注意事项

  1. 权限问题:确保用户有访问USB设备的权限
  2. 带宽限制:USB带宽有限,高分辨率需要足够的带宽
  3. 缓冲区管理:合理设置缓冲区大小,避免丢帧
  4. 线程安全:回调函数可能在不同的线程中执行
  5. 错误处理:始终检查返回值,处理错误情况
  6. 内存泄漏:确保所有分配的内存都被正确释放
相关推荐
bybitq6 小时前
Reactor 模型 vs Proactor 模型:区别与代码示例
算法
jimy16 小时前
C 语言的 static 关键字作用
c语言·开发语言·算法
七颗糖很甜6 小时前
基于IRI-2016模型计算电子密度、TEC、foF2等参数的技术原理与代码实现
大数据·python·算法
风筝在晴天搁浅7 小时前
LeetCode 143.重排链表
算法·leetcode·链表
碧海银沙音频科技研究院7 小时前
如何彻底关闭360壁纸
人工智能·深度学习·算法
sali-tec7 小时前
C# 基于OpenCv的视觉工作流-章57-人脸识别
图像处理·人工智能·opencv·算法·计算机视觉
计算机安禾7 小时前
【Linux从入门到精通】第43篇:I/O调度算法与磁盘性能优化
linux·算法·性能优化
这张生成的图像能检测吗7 小时前
(论文速读)FreDN:基于可学习频率分解的时间序列预测的频谱解纠缠
人工智能·深度学习·算法·机器学习·时序模型
CN-Dust7 小时前
【C++】for循环嵌套例题专题
java·c++·算法