主页:GitHub - libuvc/libuvc: a cross-platform library for USB video devices
1 概述
这个和后面的uvc gadget的区别是一个是HOST的,一个是Device的。
libuvc基本上是一个HOST端的库,在windows下是不需要了,在linux下可能用得上。结构是什么样的呢?大概如下:
UVC
USB
OS <------------------> Device
libuvc就是在标准的USB协议之上,增加了USB camera的控制功能。从我对USB协议的一些理解,USB和一般协议的区别就是自发现,要实现自发现,就多了一个设备协商,能力协商的过程。先是设备协商,再匹配找到的驱动。然后是能力协商,根据能力选择不同的通道。
还是看看具体的代码把。它的代码里面有两个示例。一个example,一个test,基本上大同小异。不过example.c更完善一丢丢,所以用它看吧。。。
从大的流程来看,是这样的顺序:
uvc_init->uvc_find_device->uvc_open(uvc_print_diag)->uvc_get_stream_ctrl_format_size(uvc_print_stream_ctrl)->uvc_stop_streaming->uvc_close->uvc_unref_device。
2 具体流程
2.1 uvc_init
主要就是调用了libusb_init,对下面的结构的usb_ctx进行了初始化。因为libusb_init没法跟,所以具体也不清楚,应该主要是USB通信的部分。
/** Context within which we communicate with devices */
struct uvc_context {
/** Underlying context for USB communication */
struct libusb_context *usb_ctx;
/** True iff libuvc initialized the underlying USB context */
uint8_t own_usb_ctx;
/** List of open devices in this context */
uvc_device_handle_t *open_devices;
pthread_t handler_thread;
int kill_handler_thread;
};
2.2 uvc_find_device
主要是调用了uvc_get_device_list,uvc_get_device_descriptor。首先是获取了所有的USB设备(有分支持热拔插和不支持热拔插),用list组织。里面具体很多内容应该是和USB协议相关的。
判断是看每个设备的interface,一个设备应该是多个interface。判断UVC的话用的下面这个, bInterfaceClass:
查了一下bInterfaceClass这个,说明如下,所以14就是视频设备。
找到UVC数据之后,将信息保存在uvc_device结构中,就是上下文和该usb设备信息。如下:
struct uvc_device {
struct uvc_context *ctx;
int ref;
libusb_device *usb_dev;
};
2.3 uvc_open
这个部分的内容就比较多,首先还是libusb_open,然后uvc_get_device_info,获取设备的具体能力,uvc_claim_if,这里申明UVC的接口。然后就是一套,libusb_get_device_descriptor,libusb_fill_interrupt_transfer,libusb_submit_transfer(是 libusb 库中的一个函数,用于将 USB 传输请求提交给 USB 设备。它的作用是向 USB 设备发送一个传输请求,并在请求完成后通知应用程序)。
整个部分应该是打开设备,准备传输。目前在libusb_open打开的时候报错-3,查了一下是权限问题(Linux下权限问题真的防不胜防啊!!!),运行下面命令可以搞定。
sudo chmod -R 777 /dev/bus/usb/
主要用的函数是int libusb_open(libusb_device *dev, libusb_device_handle **dev_handle)
参数说明:
dev
:要打开的 USB 设备的指针。dev_handle
:用于接收指向表示打开设备的设备句柄的指针的指针。
dev就是在上面打开的dev。dev_handle就是打开之后返回的handle。我理解是类似fope,要进入准备读写的状态。同时加锁。
我用的是PDD买的10块钱包邮的摄像头,打印的信息如下,这些都是UVC中control interface和streaming interface的信息,是比较重要的信息了。
DEVICE CONFIGURATION (18ec:3399/[none]) ---
Status: idle
VideoControl:
bcdUVC: 0x0100
VideoStreaming(1):
bEndpointAddress: 131
Formats:
UncompressedFormat(1)
bits per pixel: 16
GUID: 5955593200001000800000aa00389b71 (YUY2)
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/15
interval[2]: 1/10
interval[3]: 1/5
FrameDescriptor(2)
capabilities: 00
size: 160x120
bit rate: 24576000-147456000
max frame size: 38400
default interval: 1/30
interval[0]: 1/30
interval[1]: 1/15
interval[2]: 1/10
interval[3]: 1/5
FrameDescriptor(3)
capabilities: 00
size: 176x144
bit rate: 24576000-147456000
max frame size: 50688
default interval: 1/30
interval[0]: 1/30
interval[1]: 1/15
interval[2]: 1/10
interval[3]: 1/5
FrameDescriptor(4)
capabilities: 00
size: 320x240
bit rate: 24576000-147456000
max frame size: 153600
default interval: 1/30
interval[0]: 1/30
interval[1]: 1/15
interval[2]: 1/10
interval[3]: 1/5
FrameDescriptor(5)
capabilities: 00
size: 352x288
bit rate: 24576000-147456000
max frame size: 202752
default interval: 1/30
interval[0]: 1/30
interval[1]: 1/15
interval[2]: 1/10
interval[3]: 1/5
StillFrameDescriptor
bEndPointAddress: 00
wWidth(1) = 640
wHeight(1) = 480
wWidth(2) = 160
wHeight(2) = 120
wWidth(3) = 176
wHeight(3) = 144
wWidth(4) = 320
wHeight(4) = 240
wWidth(5) = 352
wHeight(5) = 288
END DEVICE CONFIGURATION
2.4 uvc_get_stream_ctrl_format_size
这部分是协商传输的格式和尺寸,用设置的尺寸,帧率等,去轮询匹配streaming interface的规格。视频类独有。完了输出匹配的结果。
First format: (YUY2) 640x480 30fps
bmHint: 0001
bFormatIndex: 1
bFrameIndex: 1
dwFrameInterval: 333333
wKeyFrameRate: 0
wPFrameRate: 0
wCompQuality: 0
wCompWindowSize: 0
wDelay: 0
dwMaxVideoFrameSize: 614400
dwMaxPayloadTransferSize: 3000
bInterfaceNumber: 1
2.5 uvc_start_streaming
开始传输。这个是stream的核心数据结构,操作都是围绕着这个来的。
struct uvc_stream_handle {
struct uvc_device_handle *devh;
struct uvc_stream_handle *prev, *next;
struct uvc_streaming_interface *stream_if;
/** if true, stream is running (streaming video to host) */
uint8_t running;
/** Current control block */
struct uvc_stream_ctrl cur_ctrl;
/* listeners may only access hold*, and only when holding a
* lock on cb_mutex (probably signaled with cb_cond) */
uint8_t fid;
uint32_t seq, hold_seq;
uint32_t pts, hold_pts;
uint32_t last_scr, hold_last_scr;
size_t got_bytes, hold_bytes;
uint8_t *outbuf, *holdbuf;
pthread_mutex_t cb_mutex;
pthread_cond_t cb_cond;
pthread_t cb_thread;
uint32_t last_polled_seq;
uvc_frame_callback_t *user_cb;
void *user_ptr;
struct libusb_transfer *transfers[LIBUVC_NUM_TRANSFER_BUFS];
uint8_t *transfer_bufs[LIBUVC_NUM_TRANSFER_BUFS];
struct uvc_frame frame;
enum uvc_frame_format frame_format;
struct timespec capture_time_finished;
/* raw metadata buffer if available */
uint8_t *meta_outbuf, *meta_holdbuf;
size_t meta_got_bytes, meta_hold_bytes;
};
在这里,使用了两个buf,一个out_buffer,一个hold_buf。之前好像是在哪看到过这种双缓冲的机制。之后就是生产者消费者的常规操作。
pthread_mutex_init(&strmh->cb_mutex, NULL);
pthread_cond_init(&strmh->cb_cond, NULL);
之后的传输要看是同步还是非同步。如果是非同步,那还比较简单。libusb_alloc_transfer和libusb_fill_bulk_transfer就行了。
同步看起来是设置了最大帧长度,还有备份端口,在有多个备用端口的时候使用同步传输isochronous transfers,具体空了再看吧。。。
最后调用libusb_submit_transfer发送数据。
2.6 uvc_stop_streaming
主要调用的是libusb_cancel_transfer,就是停掉生产者消费者,停掉usb的传输,然后清除内存,关闭端口。
2.7 uvc_close
这个相对就简单点,直接调用的libusb_close,关闭interface,还有清掉usb引用。
2.8 uvc_set_ae_mode
中途修改属性,这里是自动曝光,其实就是直接组一个usb包就可以了。。
ret = libusb_control_transfer(
devh->usb_devh,
REQ_TYPE_SET, UVC_SET_CUR,
UVC_CT_AE_MODE_CONTROL << 8,
uvc_get_camera_terminal(devh)->bTerminalID << 8 | devh->info->ctrl_if.bInterfaceNumber,
data,
sizeof(data),
0);
跟了一阵代码,发觉还是要对libusb,还有USB协议要有一定了解才行,空了再说吧。。。
参考: