市面上的所谓免驱USB摄像头实际就是服从了统一输出流协议(UVC)的设备。所以只要掌握如何使用UVC协议和USB摄像头进行通讯,就能对USB摄像头进行开发了。著名的开源项目LIBUVC就是分装了C语言接口方便跨平台调用libuvc的库来和USB设备进行通讯。
1. 安装libuvc以及依赖库libusb
git clone https://github.com/libusb/libusb.git
cd libusb
./configure --disable-udev
make
make install
git clone https://github.com/libuvc/libuvc.git
cd libuvc
mkdir -p build/
cd build
cmake ..
make
make install
2. (可选)将库编译出并放置在一个文件夹中用以区分版本
THIRDPARTY_FOLDER=${PWD}
sudo apt-get install libudev-dev -y
cd ${THIRDPARTY_FOLDER}/libusb
sh bootstrap.sh
./configure --disable-udev
make
cd ${THIRDPARTY_FOLDER}/libuvc
mkdir -p build/
cd build
cmake ..
make
mkdir -p ${THIRDPARTY_FOLDER}/libs/
mkdir -p ${THIRDPARTY_FOLDER}/libs/linux/
cp -f ${THIRDPARTY_FOLDER}/libusb/libusb/.libs/libusb-1.0.a ${THIRDPARTY_FOLDER}/libs/linux/
cp -f ${THIRDPARTY_FOLDER}/libuvc/build/libuvc.a ${THIRDPARTY_FOLDER}/libs/linux/
cp -r ${THIRDPARTY_FOLDER}/libuvc/include/libuvc ${THIRDPARTY_FOLDER}/libs/linux/
cp -r ${THIRDPARTY_FOLDER}/libuvc/build/include/libuvc/libuvc_config.h ${THIRDPARTY_FOLDER}/libs/linux/libuvc/
3. 写Makefile调用libuvc和libusb-1.0
cc:=g++
THIRD_PATY_LIBS_PATH:=$(PWD)/../../thirdParty/libs/linux
ccflag:=-w -O3 -fpermissive -luvc -lusb-1.0 -L$(THIRD_PATY_LIBS_PATH)/. -I$(THIRD_PATY_LIBS_PATH)/
ccflag+=-lGL -lGLU -lglut -w -O3
# src:=./leouvc.c
src_demo:=./leouvc.c ./leouvcDemo.c
src_demo_gl:=./leouvc.c ./leouvcDemoGL.c
exe_demo:=./leouvcDemo
exe_demoGL:=./leouvcDemoGL
build:
$(cc) $(src_demo) $(ccflag) -o $(exe_demo)
$(cc) $(src_demo_gl) $(ccflag) -o $(exe_demoGL)
clean:
rm -rf $(exe_demo)
rm -rf $(exe_demoGL)
run:
./$(exe_demo)
run_gl:
./$(exe_demoGL)
4. 分装libuvc接口进leouvc.c & leouvc.h
#ifndef __LEOUVC_H__
#define __LEOUVC_H__
#include "libuvc/libuvc.h"
typedef void (*uvc_utils_device_cb)(uvc_frame_t *frame, void *ptr);
typedef struct UVC_UTILS_DEVICE_t
{
int pid;
int vid;
int format;
int targetFps;
int frameWidth;
int frameHeight;
uvc_utils_device_cb cb;
uvc_context_t *ctx;
uvc_error_t res;
uvc_device_t *dev;
uvc_device_handle_t *devh;
uvc_stream_ctrl_t ctrl;
}UVC_UTILS_DEVICE;
void init_uvc_camera_device(UVC_UTILS_DEVICE * dev);
void free_uvc_camera_device(UVC_UTILS_DEVICE * dev);
void start_uvc_camera_device(UVC_UTILS_DEVICE * dev);
void log_uvc_frame_format(int uvc_f);
void cvt_yuyv2rgb(uint8_t * yuyv,uint8_t * gl_rgb_buffer,uint32_t w,uint32_t h);
#endif //__LEOUVC_H__
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include "leouvc.h"
#define CHECK_0_255(V) ( (V>255) ? (255) : (V<0 ? 0 : V ) )
void init_uvc_camera_device(UVC_UTILS_DEVICE * dev)
{
dev->res = uvc_init(&dev->ctx, NULL);
if(dev->res<0) printf("uvc_init failed!\n");
dev->res = uvc_find_device(dev->ctx, &dev->dev, dev->pid, dev->vid, NULL);
if(dev->res<0) printf("uvc_find_device failed!\n");
dev->res = uvc_open(dev->dev, &dev->devh);
if(dev->res<0) printf("uvc_open failed!\n");
dev->res = uvc_get_stream_ctrl_format_size(dev->devh, &dev->ctrl, dev->format, dev->frameWidth, dev->frameHeight, dev->targetFps );
if(dev->res<0) printf("uvc_get_stream_ctrl_format_size failed!\n");
if(dev->res>=0) printf("init_uvc_camera_device ok!\n");
}
void free_uvc_camera_device(UVC_UTILS_DEVICE * dev)
{
uvc_stop_streaming(dev->devh);
uvc_close(dev->devh);
uvc_unref_device(dev->dev);
uvc_exit(dev->ctx);
printf("free_uvc_camera_device ok!\n");
}
void start_uvc_camera_device(UVC_UTILS_DEVICE * dev)
{
dev->res = uvc_start_streaming(dev->devh, &dev->ctrl, dev->cb, 0, 0);
if(dev->res>=0)
{
printf("start_uvc_camera_device ok!\n");
}
}
void log_uvc_frame_format(int uvc_f)
{
switch(uvc_f)
{
case UVC_FRAME_FORMAT_ANY: printf("UVC_FORMAT=UVC_FRAME_FORMAT_ANY\n"); break;
case UVC_FRAME_FORMAT_UNCOMPRESSED:printf("UVC_FORMAT=UVC_FRAME_FORMAT_UNCOMPRESSED\n");break;
case UVC_FRAME_FORMAT_COMPRESSED: printf("UVC_FORMAT=UVC_FRAME_FORMAT_COMPRESSED\n"); break;
case UVC_FRAME_FORMAT_YUYV: printf("UVC_FORMAT=UVC_FRAME_FORMAT_YUYV\n"); break;
case UVC_FRAME_FORMAT_UYVY: printf("UVC_FORMAT=UVC_FRAME_FORMAT_UYVY\n"); break;
case UVC_FRAME_FORMAT_RGB: printf("UVC_FORMAT=UVC_FRAME_FORMAT_RGB\n"); break;
case UVC_FRAME_FORMAT_BGR: printf("UVC_FORMAT=UVC_FRAME_FORMAT_BGR\n"); break;
case UVC_FRAME_FORMAT_MJPEG: printf("UVC_FORMAT=UVC_FRAME_FORMAT_MJPEG\n"); break;
case UVC_FRAME_FORMAT_H264: printf("UVC_FORMAT=UVC_FRAME_FORMAT_H264\n"); break;
case UVC_FRAME_FORMAT_GRAY8: printf("UVC_FORMAT=UVC_FRAME_FORMAT_GRAY8\n"); break;
case UVC_FRAME_FORMAT_GRAY16: printf("UVC_FORMAT=UVC_FRAME_FORMAT_GRAY16\n"); break;
case UVC_FRAME_FORMAT_BY8: printf("UVC_FORMAT=UVC_FRAME_FORMAT_BY8\n"); break;
case UVC_FRAME_FORMAT_BA81: printf("UVC_FORMAT=UVC_FRAME_FORMAT_BA81\n"); break;
case UVC_FRAME_FORMAT_SGRBG8: printf("UVC_FORMAT=UVC_FRAME_FORMAT_SGRBG8\n"); break;
case UVC_FRAME_FORMAT_SGBRG8: printf("UVC_FORMAT=UVC_FRAME_FORMAT_SGBRG8\n"); break;
case UVC_FRAME_FORMAT_SRGGB8: printf("UVC_FORMAT=UVC_FRAME_FORMAT_SRGGB8\n"); break;
case UVC_FRAME_FORMAT_SBGGR8: printf("UVC_FORMAT=UVC_FRAME_FORMAT_SBGGR8\n"); break;
case UVC_FRAME_FORMAT_NV12: printf("UVC_FORMAT=UVC_FRAME_FORMAT_NV12\n"); break;
case UVC_FRAME_FORMAT_P010: printf("UVC_FORMAT=UVC_FRAME_FORMAT_P010\n"); break;
case UVC_FRAME_FORMAT_COUNT: printf("UVC_FORMAT=UVC_FRAME_FORMAT_COUNT\n"); break;
}
}
void cvt_yuyv2rgb(uint8_t * yuyv,uint8_t * gl_rgb_buffer,uint32_t w,uint32_t h)
{
for(uint32_t i=0;i<h;i++)
{
for(uint32_t j=0;j<(w/2);j++)
{
float Y1 = (float)(yuyv[i*2*w+j*4+0]);
float U = (float)(yuyv[i*2*w+j*4+1])-128;
float Y2 = (float)(yuyv[i*2*w+j*4+2]);
float V = (float)(yuyv[i*2*w+j*4+3])-128;
uint8_t r1 = (uint8_t)CHECK_0_255((float)Y1 + 1.4075*(float)V);
uint8_t g1 = (uint8_t)CHECK_0_255((float)Y1 - 0.3455*(float)U - 0.7169*(float)V);
uint8_t b1 = (uint8_t)CHECK_0_255((float)Y1 + 1.779* (float)U);
uint8_t r2 = (uint8_t)CHECK_0_255((float)Y2 + 1.4075*(float)V);
uint8_t g2 = (uint8_t)CHECK_0_255((float)Y2 - 0.3455*(float)U - 0.7169*(float)V);
uint8_t b2 = (uint8_t)CHECK_0_255((float)Y2 + 1.779* (float)U);
gl_rgb_buffer[i*3*w+j*6+0]=r1;
gl_rgb_buffer[i*3*w+j*6+1]=g1;
gl_rgb_buffer[i*3*w+j*6+2]=b1;
gl_rgb_buffer[i*3*w+j*6+3]=r2;
gl_rgb_buffer[i*3*w+j*6+4]=g2;
gl_rgb_buffer[i*3*w+j*6+5]=b2;
}
}
}
5. 同时使用两个leouvc的结构体调用两个usb摄像头案例
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/time.h>
#include "leouvc.h"
void cam1_callback(uvc_frame_t *frame, void *ptr)
{
struct timeval ts_cur;
gettimeofday(&ts_cur,NULL);
printf("[cam1_callback]: %lf ms ",(double)(ts_cur.tv_sec*100000+ts_cur.tv_usec)/1000);
log_uvc_frame_format(frame->frame_format);
}
void cam2_callback(uvc_frame_t *frame, void *ptr)
{
struct timeval ts_cur;
gettimeofday(&ts_cur,NULL);
printf("[cam2_callback]: %lf ms ",(double)(ts_cur.tv_sec*100000+ts_cur.tv_usec)/1000);
log_uvc_frame_format(frame->frame_format);
}
int main(int argc , char * argv[])
{
UVC_UTILS_DEVICE cam1;
cam1.format = UVC_FRAME_FORMAT_YUYV;
cam1.frameWidth = 640;
cam1.frameHeight = 480;
cam1.targetFps = 30;
cam1.pid = 0x0120;
cam1.vid = 0x1234;
cam1.cb = cam1_callback;
UVC_UTILS_DEVICE cam2;
cam2.format = UVC_FRAME_FORMAT_YUYV;
cam2.frameWidth = 1280;
cam2.frameHeight = 480;
cam2.targetFps = 100;
cam2.pid = 0x15aa;
cam2.vid = 0x1555;
cam2.cb = cam2_callback;
init_uvc_camera_device(&cam1);
init_uvc_camera_device(&cam2);
start_uvc_camera_device(&cam1);
start_uvc_camera_device(&cam2);
sleep(10);
free_uvc_camera_device(&cam1);
free_uvc_camera_device(&cam2);
}
6. 工程下载地址和demo演示视频地址
https://github.com/leonard73/LeoCPorintg.git
B站视频:
【纯C语言手撸UVC摄像头开发】
https://www.bilibili.com/video/BV1Vj411S74i/?share_source=copy_web&vd_source=0673d40f3e253d0787bd1c78fef7873d