【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别

IP摄像头技术纵览(一)— linux 内核编译,USB摄像头设备识别


开始正文之前先来认识一下我的开发环境:

系统:ubuntu 10.04
开发板:AT91SAM9260 + Linux-2.6.30

USB摄像头:UVC无驱摄像头(着手开发时只是随便买了个usb摄像头,根本不知道摄像头还有那么多讲究)
【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别_第1张图片
关于UVC摄像头,这里引用度娘的一段解释:

UVC,全称为:USB video class 或USB video device class,UVC是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,目前已成为USB org标准之一。如今的主流操作系统(如Windows XP SP2 and later, Linux 2.4.6 and later, MacOS 10.5 and later)都已提供UVC设备驱动,因此符合UVC规格的硬件设备在不需要安装任何的驱动程序下即可在主机中正常使用。
使用 UVC 的好处是省略了驱动程序安装这一环节,所以称为无驱—-其实就是不用安装驱动的意思。

开始正文前先来看一下我的开发板:
【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别_第2张图片
有没有很霸气 (0^◇^0)/,IP64防护等级,绝对秒杀其他开发板,不服拍倒:-D。好了,闲话少提,进入正题。

本文属于《IP摄像头技术纵览》系列文章之一:

Author: chad
Mail: [email protected]

本文可以自由转载,但转载请务必注明出处以及本声明信息。


引子

  天地初开,大道始行。进行Linux开发的第一步是创建交叉编译工具链,不同的Linux内核版本,不同的硬件平台,我们使用的交叉工具链是不同的。曾经,很长一段时间我一直有个疑惑:为什么我用at91sam9260交叉编译工具链编译的程序只能在at91sam9260上运行,在mini2440上就不能运行?相反,用使用于mini2440的交叉编译工具链编译的程序在at91上也不能运行?mini2440与at91sam9260都是arm平台,同样使用linux系统,为何二进制程序不能通用呢?

  交叉编译工具链的创建只是第一步,为了实现usb摄像头图像采集功能,又该如何配着linux内核,如何检测usb摄像头并采集图像呢?

  要搞清楚这些问题,本文将追本溯源,从头到尾,围绕以下问题展开:

  • 交叉编译工具链是什么?
  • 交叉编译工具链有什么组成?
  • 交叉编译工具链如何工作?
  • 如何自己创建交叉编译工具链?
  • 如何配置、编译Linux内核?
  • 如何创建Linux文件系统?
  • 如何编写USB摄像头视频图像采集程序?
  • 编译完成的程序文件是什么格式?
  • 在我们运行程序文件时,程序是如何运行起来的?
  • 传说中的linux虚拟存储管理是什么?它与程序的运行有什么关系?
  • 。。。。。。。

      问题是无穷的,只有有问题的程序员才能越走越远。我们是不建议重复发明轮子的,但如果我们不自己发明一次,我们永远不知道轮子是怎么来的。我在这里只发问,引起大家的思考,然后给出部分答案(我也没能力给出太多^_^o~ 努力!)。我相信,只有明白了大道,才能更好的开发。

一、内核编译、文件系统移植—茅庐初创

  上文已说,内核编译的第一步是建立交叉编译工具,那么:

1. 交叉编译工具链是什么?

  交叉编译是嵌入式开发过程中的一项重要技术,其主要特征是某机器中执行的程序代码不是在本机编译生成,而是由另一台机器编译生成,一般把前者称为目标机,后者称为主机。
  采用交叉编译的主要原因在于,多数嵌入式目标系统不能提供足够的资源供编译过程使用,因而只好将编译工程转移到高性能的主机中进行,这就需要在强大的pc机上建立一个用于目标机的交叉编译环境。

2. 交叉编译工具链有什么组成?

  交叉编译工具链是一个由编译器、连接器和解释器组成的综合开发环境。
  Linux下的交叉编译环境重要包括以下几个部分:

 (1)针对目标系统的编译器gcc/g++;
 (2)针对目标系统的二进制工具binutils;
 (3)目标系统的标准c库glibc,有时出于减小libc 库大小的考虑,你也可以用别的c库来代替glibc,例如uClibc、newlib等;
 (4)目标系统的Linux内核头文件等。

3. 交叉编译工具链如何工作—编译原理?

  使用gcc编译程序时,编译过程可被细分为四个阶段:

(1)预处理
(2)编译
(3)汇编
(4)链接

以hello.c为例:

#include 
int main()
{
    printf("hello world.\n");
    return 0;
}
1、预处理(Preprocessing)

  预处理阶段,编译器将对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析,它把”stdio.h”的内容插入到hello.i文件中,用户使用-E选项进行查看:

gcc -E hello.c -o hello.i

2、编译(Compilation)

  gcc首先检查语法的规范性以及是否有语法错误等,以确定代码实际要做的工作,用户可以使用”-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码:

gcc -S hello.i -o hello.s

3、汇编(Assembly)

  汇编阶段是把编译阶段生成的“.s”文件转成目标文件,用户在此可使用选项”-c”就可看到汇编代码已转化为”.o”的二进制目标代码:

gcc -c hello.s -o hello.o

4、链接(Linking)

  在该阶段涉及一个重要的概念:函数库。上例程序中并没有定义”printf”的函数实现,在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,”printf”函数是如何被调用的呢?最后的答案是:系统把这些函数实现都己经被放入名为libc.so.6的库文件中去了,在没有特别指定时库函数搜索路径时,gcc会到系统默认的搜索路径“/usr/Iib”下进行查找,链接到libc.so.6库中的”printf”实现,链接的最终结果是生成可执行ELF文件:

gcc hello.o –o hello

4. 如何自己创建交叉编译工具链?

  进行嵌入式Linux开发的第一步是创建交叉编译工具链,在过去很长的一段时间里,构建一套交叉编译工具链对于嵌入式开发者来说简直是一场恶梦,因为他们得手动跟踪各种源码包(及其更新包)之间的依赖关系。直到buildroot的出现改变了这一事实。

  Buildroot是一个Makefiles和patches的命令集,它可以非常简单的为你的目标系统产生一个交叉编译工具链和根文件系统,整个创建过程就如同编译Linux内核一般。

在Linux中使用Buildroot建立整个ARM交叉编译环境的整体过程为:

    (1)下载buildroot
    (2)安装依赖库软件包
    (3)解压buildroot压缩包
    (4)进入源码目录,执行make menuconfig配置
    (5)保存退出生成.config 文件
    (6)编译
    (7)修改环境变量
    (8)测试arm-linux-gcc
    (9)hello.c测试

其中,导致不同平台交叉编译工具链不可通用的主要参数是:

#下面的注释同时对比mini2440平台与at91sam9260平台
Target Architecture(arm) ---> 目标的架构,s3c2440 与 at9260 都是arm,这个相同
Target Architecture Variant(arm926t)  ---> 内核类型(s3c2440[arm920t] 而 at91sam9260[arm926EJ-S],但是配置时选择arm926t),此处不同
Target ABI (OABI)   --->     目标使用的应用程序二进制接口,此处不同
       ①EABI(Embedded ABI)    mini2440的选择。
       ②OABI(Old ABI)         at91sam9260的选择
>   Kernel Headers (Linux 3.18.x kernel headers)  --->  此处差别影响不大
    C library (glibc)  --->  都选择的是glibc    
    glibc version (2.20)  --->  版本不一样      

也正是上面的配置,决定了我们最终生成的交叉工具链是只能针对特定的处理器和操作系统平台的。

编译过程的其他步骤不再说明,感兴趣的可以参考我的另一篇博文《使用buildroot创建自己的交叉编译工具链》。

5. 如何配置、编译Linux内核?

  搞明白了交叉编译工具链以后,我们就该进入Linux内核配置阶段了,如为了支持USB无驱摄像头(UVC),我们需要进行的配置如下:

Device Drivers --->
    <*> Multimedia support --->
        <*> Video For Linux
        [*] Enable Video For Linux API 1 (DEPRECATED)
        [*] Video capture adapters --->
            [*] V4L USB devices --->
                <*> USB Video Class (UVC)
                [*] UVC input events device support
                [M] GSPCA based webcams --->

  Linux内核中已经集成了几乎你能找到的所有的摄像头的驱动程序,添加摄像头支持时可以根据情况在编译内核的时候进行针对性配置,或者索性把摄像头驱动相关的全部编译在内核中(这样势必造成内核尺寸增大,一般linux系统移植时已经设定好了Flash分区大小,所以,如果内核尺寸超过限值,将导致系统启动异常),这样你就可以宣称你的开发板支持所有的摄像头了(^o^)/。其中的GSPCA 是一个法国程序员在业余时间制作的一个万能USB 摄像头驱动程序,我没有用到,所以仅作为演示模块编译,大家实际使用时候根据自己的摄像头情况进行选择。

配置好Linux内核以后,进行编译:

make ARCH=arm CROSS_COMPILE=arm-linux- uImage

如果要编译生成GSPCA内核模块,则使用如下命令:

make modules

关于Linux编译后生成镜像格式的简要说明如下:

zImage是ARM Linux常用的一种压缩映像文件,uImage是U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的“头”,说明这个映像文件的类型、加载位置、生成时间、大小等信息。换句话说,如果直接从uImage的0x40位置开始执行,zImage和uImage没有任何区别。另外,Linux2.4内核不支持uImage,Linux2.6内核加入了很多对嵌入式系统的支持,但是uImage的生成也需要设置。

格式 说明
vmlinux 编译出来的最原始的内核文件,未压缩。
zImage 是vmlinux经过gzip压缩后的文件。
bzImage bz表示“big zImage”,不是用bzip2压缩的。两者的不同之处在于,zImage解压缩内核到低端内存(第一个640K),bzImage解压缩内核到高端内存(1M以上)。如果内核比较小,那么采用zImage或bzImage都行,如果比较大应该用bzImage。
uImage U-boot专用的映像文件,它是在zImage之前加上一个长度为0x40的tag。
vmlinuz 是bzImage/zImage文件的拷贝或指向bzImage/zImage的链接。
initrd 是“initial ramdisk”的简写。一般被用来临时的引导硬件到实际内核vmlinuz能够接管并继续引导的状态。

我开发板的引导程序是uboot,所以这里生成的是uImage 。

6.创建Linux文件系统

  根文件系统使用 busybox 制作,busybox 以小巧著称,适合于嵌入式设备的linux 文件系统。具体制作及移植过程参考《at91sam9260 Linux 系统文件系统定制》一文。

二、USB摄像头初识

  Linux UVC driver(uvc) 该驱动适用于符合USB视频类(USB Video Class)规范的摄像头设备,它包括V4L2内核设备驱动和用户空间工具补丁。大多数大容量存储器设备(如优盘)都遵循USB规范,因而仅用一个单一驱动就可以操作它们。与此类似,UVC兼容外设只需要一个通用驱动即可。
  USB摄像头大体上可以分为UVC cameras和non-UVC cameras。推荐购买UVC cameras。UVC是一个开放的标准,拥有维护良好的驱动,它属于内核代码的一部分。non- UVC cameras通常情况下不比UVC cameras工作出色,驱动并不遵循通用的协议,需要针对每种摄像头做出单独的处理,Linux内核中已经集成了常见的摄像头驱,所以,随便买一个摄像头进行测试一般都不成问题。

1、摄像头类型的确定方法

  关于摄像头类型的确定,最简单的方法就是查看USB摄像头的硬件ID,硬件ID主要分为VID和PID,在Winows中可以在设备管理器中查看,方法如下图:
【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别_第3张图片
  在这个图中能够看到VID和PID为1871:0141。可以通过这个网页(http://www.ideasonboard.org/uvc/)来查看是否是否支持UVC。
很神奇,我的摄像头不在这个网页的列表中!但是网上随便一搜,出来一大堆,摘录个我们需要的信息如下:
【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别_第4张图片
可以确定,我的摄像头是UVC制式的#^_^#。

VID和PID的意义如下:

根据USB规范的规定,所有的USB设备都有供应商ID(VID)和产品识别码(PID),主机通过不同的VID和PID来区别不同的设备,VID和PID都是两个字节长,其中,供应商ID(VID)由供应商向USB执行论坛申请,每个供应商的VID是唯一的,PID由供应商自行决定,理论上来说,不同的产品、相同产品的不同型号、相同型号的不同设计的产品最好采用不同的PID,以便区别相同厂家的不同设备。

  Linux环境下可以使用lsusb命令或其它硬件信息查看工具找出摄像头的设备号(Vendor ID)和产品号(Product ID),此处我就不再测试了。

2、USB摄像头的识别

  前文已经简要叙述了Linux内核配置、编译以及文件系统移植,所以此处假设Linux开发板已经可以正常运行,此时插入USB摄像头如果提示如下说明内核配置正确:

usb 1-1: USB disconnect, address 2
usb 1-1: new full speed USB device using at91_ohci and address 3
usb 1-1: configuration #1 chosen from 1 choice
uvcvideo: Found UVC 1.00 device USB2.0 Camera (1871:0141)
input: USB2.0 Camera as /class/input/input1

  我的USB摄像头设备名称是:/dev/video1,问我我的为什么是video1?因为我配置Linux内核时多选择了下面选项:

<*> Virtual Video Driver

3、编写USB摄像头简单测试程序

  由于USB摄像头图像采集并生成可预览的JPEG文件涉及:V4L2接口编程,视频图像数据格式,jpeg库移植这些方面。所以本处仅给出一个进行V4L2图像采集并将采集到的图像直接输出到Framebuffer设备(液晶屏)上的实例,下一篇文章将讲解摄像头视频图像格式以及如何将采集到的数据存储为jpeg文件。

  下例为开源的V4L2测试实例(默认为YUYV格式转RGB24),该实例要求系统中有fb0设备存在,在ubuntu上也可以测试,只要ctl+F1切换到控制台再执行程序即可。

#include 
#include 
#include 
#include 
#include   
#include   
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define uchar unsigned char
#define uint unsigned int
#define CLEAR(x) memset (&(x), 0, sizeof (x))

struct buffer {
    void * start;
    size_t length;
};

static char * dev_name = NULL;
static int fd = -1;
struct buffer * buffers = NULL;
static unsigned int n_buffers = 0;
static int time_in_sec_capture=5;
static int fbfd = -1;
static struct fb_var_screeninfo vinfo;
static struct fb_fix_screeninfo finfo;
static char *fbp=NULL;
static long screensize=0;

static void errno_exit (const char * s)
{
    fprintf (stderr, "%s error %d, %s\n",s, errno, strerror (errno));
    exit (EXIT_FAILURE);
}
/*yuv4:2:2格式转换为rgb24格式*/
int convert_yuv_to_rgb_pixel(int y, int u, int v)
{
    uint pixel32 = 0;
    uchar *pixel = (uchar *)&pixel32;
    int r, g, b;
    r = y + (1.370705 * (v-128));
    g = y - (0.698001 * (v-128)) - (0.337633 * (u-128));
    b = y + (1.732446 * (u-128));
    if(r > 255) r = 255;
    if(g > 255) g = 255;
    if(b > 255) b = 255;
    if(r < 0) r = 0;
    if(g < 0) g = 0;
    if(b < 0) b = 0;
    pixel[0] = r * 220 / 256;
    pixel[1] = g * 220 / 256;
    pixel[2] = b * 220 / 256;

    return pixel32;
}

int convert_yuv_to_rgb_buffer(uchar *yuv, uchar *rgb, uint width,uint height)
{
    uint in, out = 0;
    uint pixel_16;
    uchar pixel_24[3];
    uint pixel32;
    int y0, u, y1, v;

    for(in = 0; in < width * height * 2; in += 4) {
        pixel_16 =
        yuv[in + 3] << 24 |
        yuv[in + 2] << 16 |
        yuv[in + 1] <<  8 |
        yuv[in + 0];//YUV422每个像素2字节,每两个像素共用一个Cr,Cb值,即u和v,RGB24每个像素3个字节
        y0 = (pixel_16 & 0x000000ff);
        u  = (pixel_16 & 0x0000ff00) >>  8;
        y1 = (pixel_16 & 0x00ff0000) >> 16;
        v  = (pixel_16 & 0xff000000) >> 24;
        pixel32 = convert_yuv_to_rgb_pixel(y0, u, v);
        pixel_24[0] = (pixel32 & 0x000000ff);
        pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
        pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
        rgb[out++] = pixel_24[0];
        rgb[out++] = pixel_24[1];
        rgb[out++] = pixel_24[2];//rgb的一个像素
        pixel32 = convert_yuv_to_rgb_pixel(y1, u, v);
        pixel_24[0] = (pixel32 & 0x000000ff);
        pixel_24[1] = (pixel32 & 0x0000ff00) >> 8;
        pixel_24[2] = (pixel32 & 0x00ff0000) >> 16;
        rgb[out++] = pixel_24[0];
        rgb[out++] = pixel_24[1];
        rgb[out++] = pixel_24[2];
    }
    return 0;
}
static int xioctl (int fd,int request,void * arg)
{
    int r;
    do r = ioctl (fd, request, arg);
    while (-1 == r && EINTR == errno);
    return r;
}

inline int clip(int value, int min, int max) {
    return (value > max ? max : value < min ? min : value);
  }

static void process_image (const void * p){

    unsigned char* in=(char*)p;
    int width=320;
    int height=240;
    int istride=640;
    int x,y,j;
    int y0,u,y1,v,r,g,b;
    long location=0;

    convert_yuv_to_rgb_buffer(in,fbp,320,240);
}

static int read_frame (void)
{
    struct v4l2_buffer buf;
    unsigned int i;

    CLEAR (buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    if (-1 == xioctl (fd, VIDIOC_DQBUF, &buf)) {
        switch (errno) {
        case EAGAIN:
        return 0;
        case EIO:    



        default:
            errno_exit ("VIDIOC_DQBUF");
        }
    }

    assert (buf.index < n_buffers);
    printf("v4l2_pix_format->field(%d)\n", buf.field);

    process_image (buffers[buf.index].start);
    if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
        errno_exit ("VIDIOC_QBUF");

    return 1;
}

static void run (void)
{
    unsigned int count;
    int frames;
    frames = 30 * time_in_sec_capture;

    while (frames-- > 0) {
        for (;;) {
            fd_set fds;
            struct timeval tv;
            int r;
            FD_ZERO (&fds);
            FD_SET (fd, &fds);


            tv.tv_sec = 2;
            tv.tv_usec = 0;

            r = select (fd + 1, &fds, NULL, NULL, &tv);

            if (-1 == r) {
                if (EINTR == errno)
                    continue;
                errno_exit ("select");
            }

            if (0 == r) {
                fprintf (stderr, "select timeout/n");
                exit (EXIT_FAILURE);
            }
            read_frame ();

            }
    }
}

static void stop_capturing (void)
{
    enum v4l2_buf_type type;

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type))
        errno_exit ("VIDIOC_STREAMOFF");
}

static void start_capturing (void)
{
    unsigned int i;
    enum v4l2_buf_type type;

    for (i = 0; i < n_buffers; ++i) {
        struct v4l2_buffer buf;
        CLEAR (buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;

        if (-1 == xioctl (fd, VIDIOC_QBUF, &buf))
            errno_exit ("VIDIOC_QBUF");
        }

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (-1 == xioctl (fd, VIDIOC_STREAMON, &type))
        errno_exit ("VIDIOC_STREAMON");

}

static void uninit_device (void)
{
    unsigned int i;

    for (i = 0; i < n_buffers; ++i)
        if (-1 == munmap (buffers[i].start, buffers[i].length))
            errno_exit ("munmap");

    if (-1 == munmap(fbp, screensize)) {
          printf(" Error: framebuffer device munmap() failed.\n");
          exit (EXIT_FAILURE) ;
        }    
    free (buffers);
}


static void init_mmap (void)
{
    struct v4l2_requestbuffers req;

    //mmap framebuffer
    fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0);
    if ((int)fbp == -1) {
        printf("Error: failed to map framebuffer device to memory.\n");
        exit (EXIT_FAILURE) ;
    }
    memset(fbp, 0, screensize);
    CLEAR (req);

    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if (-1 == xioctl (fd, VIDIOC_REQBUFS, &req)) {
        if (EINVAL == errno) {
            fprintf (stderr, "%s does not support memory mapping\n", dev_name);
            exit (EXIT_FAILURE);
        } else {
            errno_exit ("VIDIOC_REQBUFS");
        }
    }

    if (req.count < 4) {    //if (req.count < 2)
        fprintf (stderr, "Insufficient buffer memory on %s\n",dev_name);
        exit (EXIT_FAILURE);
    }

    buffers = calloc (req.count, sizeof (*buffers));

    if (!buffers) {
        fprintf (stderr, "Out of memory\n");
        exit (EXIT_FAILURE);
    }

    for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
        struct v4l2_buffer buf;

        CLEAR (buf);

        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = n_buffers;

        if (-1 == xioctl (fd, VIDIOC_QUERYBUF, &buf))
            errno_exit ("VIDIOC_QUERYBUF");

        buffers[n_buffers].length = buf.length;
        buffers[n_buffers].start =mmap (NULL,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset);

        if (MAP_FAILED == buffers[n_buffers].start)
            errno_exit ("mmap");
    }

}



static void init_device (void)
{
    struct v4l2_capability cap;
    struct v4l2_cropcap cropcap;
    struct v4l2_crop crop;
    struct v4l2_format fmt;
    unsigned int min;


    // Get fixed screen information
    if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) {
        printf("Error reading fixed information.\n");
        exit (EXIT_FAILURE);
    }

    // Get variable screen information
    if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) {
        printf("Error reading variable information.\n");
        exit (EXIT_FAILURE);
    }
    screensize = 320*240 * vinfo.bits_per_pixel / 8;
    printf("vinfo.xres=%d\n",vinfo.xres);
    printf("vinfo.yres=%d\n",vinfo.yres);
    printf("vinfo.bits_per_pixel=%d\n",vinfo.bits_per_pixel);
    printf("screensize=%d\n",screensize);

    if (-1 == xioctl (fd, VIDIOC_QUERYCAP, &cap)) {
        if (EINVAL == errno) {
            fprintf (stderr, "%s is no V4L2 device/n",dev_name);
            exit (EXIT_FAILURE);
        } else {
            errno_exit ("VIDIOC_QUERYCAP");
        }
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        fprintf (stderr, "%s is no video capture device\n",dev_name);
        exit (EXIT_FAILURE);
    }

    if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
        fprintf (stderr, "%s does not support streaming i/o\n",dev_name);
        exit (EXIT_FAILURE);
    }



    CLEAR (cropcap);

    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (0 == xioctl (fd, VIDIOC_CROPCAP, &cropcap)) {
        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        crop.c = cropcap.defrect;

        if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) {
            switch (errno) {
            case EINVAL:    
            break;
            default:
            break;
            }
        }
    }else {    }

    CLEAR (fmt);

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 320;  
    fmt.fmt.pix.height = 240;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_MJPEG;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    if (-1 == xioctl (fd, VIDIOC_S_FMT, &fmt))
        errno_exit ("VIDIOC_S_FMT");

    init_mmap ();

}

static void close_device (void)
{
    if (-1 == close (fd))
    errno_exit ("close");
    fd = -1;
    close(fbfd);
}

static void open_device (void)
{
    struct stat st;  

    if (-1 == stat (dev_name, &st)) {
    fprintf (stderr, "Cannot identify '%s': %d, %s\n",dev_name, errno, strerror (errno));
    exit (EXIT_FAILURE);
    }

    if (!S_ISCHR (st.st_mode)) {
        fprintf (stderr, "%s is no device\n", dev_name);
        exit (EXIT_FAILURE);
    }

    //open framebuffer
    fbfd = open("/dev/fb0", O_RDWR);
    if (fbfd==-1) {
        printf("Error: cannot open framebuffer device.\n");
        exit (EXIT_FAILURE);
    }

    //open camera
    fd = open (dev_name, O_RDWR| O_NONBLOCK, 0);

    if (-1 == fd) {
        fprintf (stderr, "Cannot open '%s': %d, %s\n",dev_name, errno, strerror (errno));
        exit (EXIT_FAILURE);
    }
}

static void usage (FILE * fp,int argc,char ** argv)
{
fprintf (fp,
"Usage: %s [options]\n\n"
"Options:/n"
"-d | --device name Video device name [/dev/video]\n"
"-h | --help Print this message\n"
"-t | --how long will display in seconds\n"
"",
argv[0]);
}

static const char short_options [] = "d:ht:";
static const struct option long_options [] = {
{ "device", required_argument, NULL, 'd' },
{ "help", no_argument, NULL, 'h' },
{ "time", no_argument, NULL, 't' },
{ 0, 0, 0, 0 }
};

int main (int argc,char ** argv)
{
    dev_name = "/dev/video0";

    for (;;)  
    {
        int index;
        int c;

        c = getopt_long (argc, argv,short_options, long_options,&index);
        if (-1 == c)
        break;

        switch (c) {
        case 0:
        break;

        case 'd':
        dev_name = optarg;
        break;

        case 'h':
        usage (stdout, argc, argv);
        exit (EXIT_SUCCESS);
        case 't':
          time_in_sec_capture = atoi(optarg);
            break;

        default:
        usage (stderr, argc, argv);
        exit (EXIT_FAILURE);
        }
    }

    open_device ();

    init_device ();

    start_capturing ();

    run ();

    stop_capturing ();

    uninit_device ();

    close_device ();

    exit (EXIT_SUCCESS);

    return 0;
}

编译后测试结果如下图:
【原创】IP摄像头技术纵览(一)---linux 内核编译,USB摄像头设备识别_第5张图片

最后附上常见摄像头VID和PID:

{USB_DEVICE (0x0733, 0x0430)}, /* Intel PC Camera Pro */ 
{USB_DEVICE (0x0733, 0x0401)}, /* Intel Create and Share */ 
{USB_DEVICE (0x99FA, 0x8988)}, /* Grandtec V.cap */ 
{USB_DEVICE (0x0733, 0x0402)}, /* ViewQuest M318B */ 
{USB_DEVICE (0x0733, 0x0110)}, /* ViewQuest VQ110 */ 
{USB_DEVICE (0x040A, 0x0002)}, /* Kodak DVC-325 */
{USB_DEVICE (0x055f, 0xc420)}, /* Mustek gSmart Mini 2 */ 
{USB_DEVICE (0x055f, 0xc520)}, /* Mustek gSmart Mini 3 */ 
{USB_DEVICE (0x041E, 0x400A)}, /* Creative PC-CAM 300 */ 
{USB_DEVICE (0x084D, 0x0003)}, /* D-Link DSC-350 */
{USB_DEVICE (0x041E, 0x400B)}, /* Creative PC-CAM 600 */
{USB_DEVICE (0x8086, 0x0630)}, /* Intel Pocket PC Camera */ 
{USB_DEVICE (0x8086, 0x0110)}, /* Intel Easy PC Camera */ 
{USB_DEVICE (0x0506, 0x00df)}, /* 3Com HomeConnect Lite */ 
{USB_DEVICE (0x040a, 0x0300)}, /* Kodak EZ200 */ 
{USB_DEVICE (0x04fc, 0x504b)}, /* Maxell MaxPocket LE 1.3 */
{USB_DEVICE (0x08ca, 0x2008)}, /* Aiptek Mini PenCam 2 M */ 
{USB_DEVICE (0x08ca, 0x0104)}, /* Aiptek PocketDVII 1.3 */ 
{USB_DEVICE (0x08ca, 0x2018)}, /* Aiptek Pencam SD 2M */ 
{USB_DEVICE (0x04fc, 0x504a)}, /* Aiptek Mini PenCam 1.3 */ 
{USB_DEVICE (0x055f, 0xc530)}, /* Mustek Gsmart LCD 3 */
{USB_DEVICE (0x055f, 0xc650)}, /* Mustek MDC5500Z */ 
{USB_DEVICE (0x052b, 0x1513)}, /* Megapix V4 */
{USB_DEVICE (0x08ca, 0x0103)}, /* Aiptek PocketDV */ 
{USB_DEVICE (0x0af9, 0x0010)}, /* Hama USB Sightcam 100 */
{USB_DEVICE (0x1776, 0x501c)}, /* Arowana 300K CMOS Camera */ 
{USB_DEVICE (0x08ca, 0x0106)}, /* Aiptek Pocket DV3100+ */ 
{USB_DEVICE (0x08ca, 0x2010)}, /* Aiptek PocketCam 3M */ 
{USB_DEVICE (0x0458, 0x7004)}, /* Genius VideoCAM Express V2 */ 
{USB_DEVICE (0x04fc, 0x0561)}, /* Flexcam 100 */
{USB_DEVICE (0x055f, 0xc430)}, /* Mustek Gsmart LCD 2 */ 
{USB_DEVICE (0x04fc, 0xffff)}, /* Pure DigitalDakota */ 
{USB_DEVICE (0xabcd, 0xcdee)}, /* Petcam */
{USB_DEVICE (0x04a5, 0x3008)}, /* Benq DC 1500 */ 
{USB_DEVICE (0x046d, 0x0960)}, /* Logitech Inc. ClickSmart 420 */ 
{USB_DEVICE (0x046d, 0x0901)}, /* Logitech Inc. ClickSmart 510 */ 
{USB_DEVICE (0x04a5, 0x3003)}, /* Benq DC 1300 */ 
{USB_DEVICE (0x0af9, 0x0011)}, /* Hama USB Sightcam 100 */ 
{USB_DEVICE (0x055f, 0xc440)}, /* Mustek DV 3000 */ 
{USB_DEVICE (0x041e, 0x4013)}, /* Creative Pccam750 */ 
{USB_DEVICE (0x060b, 0xa001)}, /* Maxell Compact Pc PM3 */ 
{USB_DEVICE (0x04a5, 0x300a)}, /* Benq DC3410 */ 
{USB_DEVICE (0x04a5, 0x300c)}, /* Benq DC1016 */ 
{USB_DEVICE (0x0461, 0x0815)}, /* Micro Innovation IC200 */
{USB_DEVICE (0x046d, 0x0890)}, /* Logitech QuickCam traveler */
{USB_DEVICE (0x10fd, 0x7e50)}, /* FlyCam Usb 100 */ 
{USB_DEVICE (0x06e1, 0xa190)}, /* ADS Instant VCD */ 
{USB_DEVICE (0x055f, 0xc220)}, /* Gsmart Mini */
{USB_DEVICE (0x0733, 0x2211)}, /* Jenoptik jdc 21 LCD */
{USB_DEVICE (0x046d, 0x0900)}, /* Logitech Inc. ClickSmart 310 */
{USB_DEVICE (0x055f, 0xc360)}, /* Mustek DV4000 Mpeg4 */ 
{USB_DEVICE (0x08ca, 0x2024)}, /* Aiptek DV3500 Mpeg4 */ 
{USB_DEVICE (0x046d, 0x0905)}, /* Logitech ClickSmart820 */ 
{USB_DEVICE (0x05da, 0x1018)}, /* Digital Dream Enigma 1.3 */
{USB_DEVICE (0x0c45, 0x6025)}, /* Xcam Shanga */
{USB_DEVICE (0x0733, 0x1311)}, /* Digital Dream Epsilon 1.3 */ 
{USB_DEVICE (0x041e, 0x401d)}, /* Creative Webcam NX ULTRA */ 
{USB_DEVICE (0x08ca, 0x2016)}, /* Aiptek PocketCam 2 Mega */ 
{USB_DEVICE (0x0734, 0x043b)}, /* 3DeMon USB Capture aka */
{USB_DEVICE (0x041E, 0x4018)}, /* Creative Webcam Vista (PD1100) */
{USB_DEVICE (0x0546, 0x3273)}, /* Polaroid PDC2030*/
{USB_DEVICE (0x041e, 0x401f)}, /* Creative Webcam Notebook PD1171*/ 
{USB_DEVICE (0x041e, 0x4017)}, /* Creative Webcam Mobile PD1090*/ 
{USB_DEVICE (0x046d, 0x08a2)}, /* Labtec Webcam Pro*/
{USB_DEVICE (0x055f, 0xd003)}, /* Mustek WCam300A*/ 
{USB_DEVICE (0x0458, 0x7007)}, /* Genius VideoCam V2*/
{USB_DEVICE (0x0458, 0x700c)}, /* Genius VideoCam V3*/ 
{USB_DEVICE (0x0458, 0x700f)}, /* Genius VideoCam Web V2*/ 
{USB_DEVICE (0x041e, 0x401e)}, /* Creative Nx Pro*/ 
{USB_DEVICE (0x0c45, 0x6029)}, /* spcaCam@150 */ 
{USB_DEVICE (0x0c45, 0x6009)}, /* spcaCam@120 */
{USB_DEVICE (0x0c45, 0x600d)}, /* spcaCam@120 */
{USB_DEVICE (0x04fc, 0x5330)}, /* Digitrex 2110*/ 
{USB_DEVICE (0x055f, 0xc540)}, /* Gsmart D30*/ 
{USB_DEVICE (0x0ac8, 0x301b)}, /* Asam Vimicro*/
{USB_DEVICE (0x041e, 0x403a)}, /* Creative Nx Pro 2*/
{USB_DEVICE (0x055f, 0xc211)}, /* Kowa Bs888e Microcamera*/ 
{USB_DEVICE (0x0ac8, 0x0302)}, /* Z-star Vimicro zc0302*/
{USB_DEVICE (0x0572, 0x0041)}, /* Creative Notebook cx11646*/
{USB_DEVICE (0x08ca, 0x2022)}, /* Aiptek Slim 3200*/ 
{USB_DEVICE (0x046d, 0x0921)}, /* Labtec Webcam */
{USB_DEVICE (0x046d, 0x0920)}, /* QC Express */
{USB_DEVICE (0x0923, 0x010f)}, /* ICM532 cams */ 
{USB_DEVICE (0x055f, 0xc200)}, /* Mustek Gsmart 300 */
{USB_DEVICE (0x0733, 0x2221)}, /* Mercury Digital Pro 3.1p*/ 
{USB_DEVICE (0x041e, 0x4036)}, /* Creative Live ! */ 
{USB_DEVICE (0x055f, 0xc005)}, /* Mustek Wcam300A */ 
{USB_DEVICE (0x041E, 0x403b)}, /* Creative Webcam Vista (VF0010) */ 
{USB_DEVICE (0x0545, 0x8333)}, /* Veo Stingray */ 
{USB_DEVICE (0x0545, 0x808b)}, /* Veo Stingray */ 
{USB_DEVICE (0x10fd, 0x8050)}, /* Typhoon Webshot II USB 300k */ 
{USB_DEVICE (0x0000, 0x0000)}, /* MystFromOri Unknow Camera */

附记:

关于编译完成的程序文件是什么格式?程序是如何运行起来的?传说中的linux虚拟存储管理是什么?它与程序的运行有什么关系?这几个问题此处不再详述,请参考以下几篇文章中:
《UNIX/LINUX 平台可执行文件格式分析—施聪》
《Linux 从虚拟地址到物理地址》
《Linux 虚拟地址空间布局及程序各个逻辑段详解》

———未完待续

你可能感兴趣的:(IP摄像头技术,linux,图像处理,嵌入式开发)