C语言高级应用---操作linux下V4L2摄像头应用程序【转】

简介: 转自:http://blog.csdn.net/morixinguan/article/details/51001713 版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog.csdn.net/morixinguan。

转自:http://blog.csdn.net/morixinguan/article/details/51001713

版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog.csdn.net/morixinguan。若是侵权用于商业用途,请联系博主,否则将追究责任

目录(?)[-]

    采集方式
    V4L2操作流程点击这个网址说得很详细了这里不多说
    httpbaikebaiducomview5494174htm

我们都知道,想要驱动Linux下的摄像头,其实很简单,照着V4L2的手册一步步来写,很快就可以写出来,但是在写之前我们要注意改变系统的一些配置,使系统支持framebuffer,在dev下产生fb0这样的节点,这样我们才能在linux系统上操作Camera摄像头,framebuffer在之前的博文已经有说过了,这里就不再提了。

有需要了解framebuffer的那么请点击:http://baike.baidu.com/view/3351639.htm

       最重要的,我们需要改一个脚本,在/dev/grub.conf,我们来看看怎么改:

[cpp] view plain copy
print?

    # grub.conf generated by anaconda  
    #  
    # Note that you do not have to rerun grub after making changes to this file  
    # NOTICE:  You have a /boot partition.  This means that  
    #          all kernel and initrd paths are relative to /boot/, eg.  
    #          root (hd0,0)  
    #          kernel /vmlinuz-version ro root=/dev/sdb2  
    #          initrd /initrd-[generic-]version.img  
    #boot=/dev/sdb  
    default=0  
    timeout=5  
    splashimage=(hd0,0)/grub/splash.xpm.gz  
    hiddenmenu  
    title CentOS (2.6.32-431.el6.i686)  
        root (hd0,0)  
        kernel /vmlinuz-2.6.32-431.el6.i686 ro root=UUID=2bc12537-d6c1-4e67-b4e5-e9c466205554 nomodeset rd_NO_LUKS  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=auto LANG=zh_CN.UTF-8 rd_NO_LVM rd_NO_DM rhgb quiet vga=0x318  
        initrd /initramfs-2.6.32-431.el6.i686.img  


通常情况下,要让framebuffer生效,要加一句vga=???(这里是参数),简单介绍一下:

我写vga=0x318就是默认就设置为1024x768x24bpp模式。当然还有其它的模式:如下图,根据自己的系统来配置。
色彩     640x400     640x480     800x600     1024x768     1280x1024     1600x1200
4bits     ?     ?     0x302     ?     ?     ?
8bits     0x300     0x301     0x303     0x305     0x307     0x31C
15bits     ?     0x310     0x313     0x316     0x319     0x31D
16bits     ?     0x311     0x314     0x317     0x31A     0x31E
24bits     ?     0x312     0x315     0x318     0x31B     0x31F
32bits     ?     ?     ?     ?     ?     ?
配置完成以后,我们先来了解一下V4L2的主要功能。
V4L2就使程序有发现设备和操作设备的能力.它主要是用一系列的回调函数来实现这些功能。像设置摄像头的频率、帧频、视频压缩格式和图像参数等等。当然也可以用于其他多媒体的开发,如音频等。
但是此框架只能运行在Linux操作系统之上。v4L2是针对uvc免驱usb设备的编程框架 ,主要用于采集usb摄像头等,编程模式如下:

采集方式
打开视频设备后,可以设置该视频设备的属性,例如裁剪、缩放等。这一步是可选的。在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理:
[cpp] view plain copy
print?

    extern int ioctl (int __fd, unsigned long int __request, …) __THROW;  
    __fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd;  
    __request:具体的命令标志符。  
    在进行V4L2开发中,一般会用到以下的命令标志符:  
    VIDIOC_REQBUFS:分配内存  
    VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址  
    VIDIOC_QUERYCAP:查询驱动功能  
    VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式  
    VIDIOC_S_FMT:设置当前驱动的频捕获格式  
    VIDIOC_G_FMT:读取当前驱动的频捕获格式  
    VIDIOC_TRY_FMT:验证当前驱动的显示格式  
    VIDIOC_CROPCAP:查询驱动的修剪能力  
    VIDIOC_S_CROP:设置视频信号的边框  
    VIDIOC_G_CROP:读取视频信号的边框  
    VIDIOC_QBUF:把数据放回缓存队列  
    VIDIOC_DQBUF:把数据从缓存中读取出来  
    VIDIOC_STREAMON:开始视频显示函数  
    VIDIOC_STREAMOFF:结束视频显示函数  
    VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。  
    这些IO调用,有些是必须的,有些是可选择的。  


V4L2操作流程:点击这个网址,说得很详细了,这里不多说。
http://baike.baidu.com/view/5494174.htm
接下来我们来看看实战部分,下面是我自己写的程序接口,可以实现视频采集:生气
1、project.c
[cpp] view plain copy
print?

    #include <stdio.h>  
    #include <unistd.h>  
    #include <fcntl.h>  
    #include "j-yuv.h"  
    #include "CameralOpt.h"  
    #include "FrameBufferOpt.h"  
      
    #define    WIDTH   640  
    #define    HIGHT   480  
      
    int main(void)  
    {  
        char yuyv[WIDTH*HIGHT*2];  
        char bmp[WIDTH*HIGHT*3];  
      
    //  set_bmp_header((struct bmp_header_t *)bmp, WIDTH, HIGHT);  
        //初始化摄像头  
        Init_Cameral(WIDTH , HIGHT );  
        //初始化framebuffer  
        Init_FrameBuffer(WIDTH , HIGHT );   
      
        //开启摄像头  
        Start_Cameral();  
        //采集一张图片  
        int count = 0 ;   
        while(1)  
        {  
            Get_Picture(yuyv);  
            yuyv2rgb24(yuyv, bmp, WIDTH, HIGHT);  
            Write_FrameBuffer(bmp);  
    //      printf("count:%d \n" , count++);  
        }  
        //关闭摄像头  
        Stop_Cameral();  
        //关闭Framebuffer  
        Exit_Framebuffer();  
        //退出  
        Exit_Cameral();  
          
        return 0;  
    }  

2、juv.h
[cpp] view plain copy
print?

    #ifndef __JYUV_H  
    #define __JYUV_H  
      
    typedef unsigned char  u8;  
    typedef unsigned short u16;  
    typedef unsigned int   u32;  
      
    #pragma pack(1)  
    //定义bmp头  
    struct bmp_header_t{  
        u16        magic;  
        u32       file_size;  
        u32       RESERVED1;  
        u32       offset;         //54 bytes 表示54个偏移量  
      
        u32       head_num;    //40  
        u32       width;  
        u32       height;  
        u16       color_planes; //1  
        u16       bit_count;  
        u32       bit_compression; //0  
        u32       image_size; //except the size of header  
        u32       h_resolution;  
        u32       v_resolution;  
        u32       color_num;  
        u32       important_colors;  
    };  
      
    #pragma pack()  
      
    void set_bmp_header(struct bmp_header_t * header, u32 width, u32 height);  
    int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height);  
      
    #endif /* __JYUV_H */   

3、juv.c
[cpp] view plain copy
print?

    #include "j-yuv.h"  
      
    #define BIT_COUNT   24  
      
    void set_bmp_header(struct bmp_header_t *header, u32 width, u32 height)  
    {  
        header->magic = 0x4d42;  
        header->image_size = width * height * BIT_COUNT/8;  
        header->file_size = header->image_size + 54;  
        header->RESERVED1 = 0;  
        header->offset = 54;  
      
        header->head_num = 40;  
        header->width = width;  
        header->height = height;  
        header->color_planes = 1;  
        header->bit_count = BIT_COUNT;  
        header->bit_compression = 0;  
        header->h_resolution = 0;  
        header->v_resolution = 0;  
        header->color_num = 0;  
        header->important_colors = 0;  
    }  
    //yuyv转rgb24的算法实现  
    int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height)  
    {  
        u32 i, in, rgb_index = 0;  
        u8 y0, u0, y1, v1;  
        int r, g, b;  
        u32 out = 0, x, y;  
       
        for(in = 0; in < width * height * 2; in += 4)  
        {  
        y0 = yuyv[in+0];  
        u0 = yuyv[in+1];  
        y1 = yuyv[in+2];  
        v1 = yuyv[in+3];  
      
        for (i = 0; i < 2; i++)  
        {  
            if (i)  
                y = y1;  
            else  
                y = y0;   
            r = y + (140 * (v1-128))/100;  //r  
            g = y - (34 * (u0-128))/100 - (71 * (v1-128))/100; //g  
            b = y + (177 * (u0-128))/100; //b  
            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;  
      
            y = height - rgb_index/width -1;  
            x = rgb_index%width;  
            rgb[(y*width+x)*3+0] = b;  
            rgb[(y*width+x)*3+1] = g;  
            rgb[(y*width+x)*3+2] = r;  
            rgb_index++;  
        }  
        }  
        return 0;  
    }  

4、FrameBufferOpt.c
[cpp] view plain copy
print?

    #include "FrameBufferOpt.h"  
      
    static int Frame_fd ;   
    static int *FrameBuffer = NULL ;   
    static int W , H ;  
      
    //初始化framebuffer  
    int Init_FrameBuffer(int Width , int Higth)  
    {  
        W = Width ;   
        H = Higth ;   
        Frame_fd = open("/dev/fb" , O_RDWR);  
        if(-1 == Frame_fd)  
        {  
            perror("open frame buffer fail");  
            return -1 ;   
        }  
      
    //根本就不用CPU搬运   用DMA做为搬运工  
    FrameBuffer = mmap(0, 1280*1024*4 , PROT_READ | PROT_WRITE , MAP_SHARED , Frame_fd ,0 );  
        if(FrameBuffer == (void *)-1)  
        {  
            perror("memory map fail");  
            return -2 ;  
        }  
        return 0 ;   
    }  
      
    //写入framebuffer  
    int Write_FrameBuffer(const char *buffer)  
    {  
        int row  , col ;   
        char *p = NULL ;   
        for(row = 0 ; row <1024 ; row++)  
        {  
            for(col = 0 ; col < 1280 ;  col++)  
            {  
                if((row < H)  && (col < W))  
                {  
                    p = (char *)(buffer + (row * W+ col ) * 3);   
                    FrameBuffer[row*1280+col] = RGB((unsigned char)(*(p+2)),(unsigned char)(*(p+1)),(unsigned char )(*p));  
                }  
            }  
        }  
        return 0 ;   
    }  
      
    //退出framebuffer  
    int Exit_Framebuffer(void)  
    {  
        munmap(FrameBuffer ,  W*H*4);  
        close(Frame_fd);  
        return 0 ;   
    }  

5、FrameBufferOpt.h
[cpp] view plain copy
print?

    #ifndef  _FRAMEBUFFEROPT_H  
    #define  _FRAMEBUFFEROPT_H  
      
    #include <stdio.h>  
    #include <fcntl.h>  
    #include <unistd.h>  
    #include <sys/mman.h>  
      
      
    #define    RGB(r,g,b)       ((r<<16)|(g<<8)|b)  
      
    //初始化ramebuffer  
    int Init_FrameBuffer(int Width , int Higth);  
      
    //写数据到framebuffer  
    int Write_FrameBuffer(const char *buffer);  
      
    //退出framebuffer  
    int Exit_Framebuffer(void);  
      
      
      
    #endif //_FRAMEBUFFEROPT_H  

6、CameralOpt.h
[cpp] view plain copy
print?

    #ifndef  _CAMERALOPT_H  
    #define  _CAMERALOPT_H  
      
    #include <stdio.h>  
    #include <linux/videodev2.h>  
    #include <fcntl.h>  
    #include <unistd.h>  
    #include <string.h>  
    #include <stdlib.h>  
    #include <errno.h>  
    #include <sys/mman.h>  
      
    #define   COUNT  3  
    //初始化摄像头  
    int Init_Cameral(int Width , int Hight);  
    int Exit_Cameral(void); //退出摄像头  
    //摄像头开始采集  
    int Start_Cameral(void);  
    int Stop_Cameral(void);//停止摄像头  
    //获取摄像头的数据  
    int Get_Picture(char *buffer);  
      
    #endif  //_CAMERALOPT_H  

7、CameralOpt.c
[cpp] view plain copy
print?

    #include "CameralOpt.h"  
    int video_fd ;   
    int length ;   
    char *yuv[COUNT] ;   
    struct v4l2_buffer  enqueue  , dequeue ;  //定义出入队的操作结构体成员  
      
    int Init_Cameral(int Width , int Hight)  
    {  
        //参数检查  
        char *videodevname = NULL ;   
        videodevname = "/dev/video0" ;   
          
        //打开设备  
        video_fd = open(videodevname , O_RDWR);  
        if(-1 == video_fd )  
        {  
            perror("open video device fail");  
            return -1 ;   
        }  
      
        int i ;   
        int ret ;   
        struct v4l2_format  format ;   
        format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
        format.fmt.pix.width  = Width;   
        format.fmt.pix.height = Hight;   
        format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV ;  //我支持的格式是这个  
      
        ret = ioctl(video_fd , VIDIOC_S_FMT , &format);  
        if(ret != 0)  
        {  
            perror("set video format fail");  
            return -2 ;   
        }  
      
      
        //申请buffer,切割成几个部分  
        //3  
        struct v4l2_requestbuffers  requestbuffer ;   
        requestbuffer.count = COUNT ;   
        requestbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
        requestbuffer.memory = V4L2_MEMORY_MMAP ;   
      
        ret = ioctl(video_fd , VIDIOC_REQBUFS , &requestbuffer);  
        if(ret != 0)  
        {  
            perror("request buffer fail ");  
            return -3  ;  
        }  
          
          
        //querybuffer  
        struct v4l2_buffer querybuffer ;   
        querybuffer.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
        querybuffer.memory = V4L2_MEMORY_MMAP ;   
      
        for(i = 0 ; i < COUNT ; i++)  
        {  
            querybuffer.index = i ;       
      
            ret = ioctl(video_fd , VIDIOC_QUERYBUF , &querybuffer);  
            if(ret != 0)  
            {  
                perror("query buffer fail");  
                return -4 ;   
            }  
          
    //      printf("index:%d length:%d  offset:%d \n" ,   
    //      querybuffer.index , querybuffer.length , querybuffer.m.offset);  
            length = querybuffer.length ;   
      
            //将摄像头内存印射到进程的内存地址  
            yuv[i] = mmap(0,querybuffer.length , PROT_READ | PROT_WRITE , MAP_SHARED , video_fd , querybuffer.m.offset );  
      
      
            //列队  
              
            struct v4l2_buffer  queuebuffer ;   
            queuebuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
            queuebuffer.memory =  V4L2_MEMORY_MMAP ;   
            queuebuffer.index = i ;       
      
            ret = ioctl(video_fd , VIDIOC_QBUF , &queuebuffer);  
            if(ret != 0)  
            {  
                perror("queuebuffer fail");  
                return -5 ;   
            }  
        }  
        //初始化入队出队  
        enqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
        dequeue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;   
        enqueue.memory = V4L2_MEMORY_MMAP ;   
        dequeue.memory = V4L2_MEMORY_MMAP ;   
      
        return 0 ;   
    }  
      
    int Exit_Cameral(void)  
    {  
        int i ;   
        for(i = 0 ; i < COUNT ; i++)  
            munmap(yuv+i , length);  
        close(video_fd);  
        return 0 ;   
    }  
      
    int Start_Cameral(void)  
    {  
        //开启摄像头  
        int ret ;   
        int on = 1 ;   
        ret = ioctl(video_fd , VIDIOC_STREAMON , &on);  
        if(ret != 0)  
        {  
            perror("start Cameral fail");  
            return -1 ;   
        }  
        return 0 ;   
    }  
    int Stop_Cameral(void)  
    {  
        //停止摄像头  
        int ret ;   
        int off= 1 ;   
        ret = ioctl(video_fd , VIDIOC_STREAMOFF, &off);  
        if(ret != 0)  
        {  
            perror("stop Cameral fail");  
            return -1 ;   
        }  
        return 0 ;  
    }  
      
    int Get_Picture(char *buffer)  
    {  
        int ret ;   
        //出队  
        ret = ioctl(video_fd , VIDIOC_DQBUF , &dequeue);  
        if(ret != 0)  
        {  
            perror("dequeue fail");  
            return -1 ;   
        }  
      
        //获取图片数据 YUV   yuv[dequeue.index]  
        memcpy(buffer , yuv[dequeue.index] , dequeue.length);  
    //  write(yuyv_fd , yuv[dequeue.index] , dequeue.length);  
      
        enqueue.index = dequeue.index ;   
        ret = ioctl(video_fd , VIDIOC_QBUF , &enqueue);  
        if(ret != 0)  
        {  
            perror("enqueue fail");  
            return -2 ;   
        }  
        return 0 ;   
    }  

 

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
|
1月前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
41 5
|
1月前
|
存储 安全 关系型数据库
Linux系统在服务器领域的应用与优势###
本文深入探讨了Linux操作系统在服务器领域的广泛应用及其显著优势。通过分析其开源性、安全性、稳定性和高效性,揭示了为何Linux成为众多企业和开发者的首选服务器操作系统。文章还列举了Linux在服务器管理、性能优化和社区支持等方面的具体优势,为读者提供了全面而深入的理解。 ###
|
4月前
|
Unix Linux Ruby
在windows和linux上高效快捷地发布Dash应用
在windows和linux上高效快捷地发布Dash应用
|
4月前
|
Linux iOS开发 开发者
跨平台开发不再难:.NET Core如何让你的应用在Windows、Linux、macOS上自如游走?
【8月更文挑战第28天】本文提供了一份详尽的.NET跨平台开发指南,涵盖.NET Core简介、环境配置、项目结构、代码编写、依赖管理、构建与测试、部署及容器化等多个方面,帮助开发者掌握关键技术与最佳实践,充分利用.NET Core实现高效、便捷的跨平台应用开发与部署。
366 3
|
4月前
|
存储 监控 Linux
在Linux中,如何进行虚拟化技术的应用?
在Linux中,如何进行虚拟化技术的应用?
|
4月前
|
存储 Linux 开发工具
【Azure App Service】本地Git部署Python Flask应用上云(Azure App Service For Linux)关键错误
【Azure App Service】本地Git部署Python Flask应用上云(Azure App Service For Linux)关键错误
|
4月前
|
存储 监控 Linux
在Linux中,如何进行容器技术的应用?
在Linux中,如何进行容器技术的应用?
|
4月前
|
算法 Ubuntu Linux
在Linux中,对比apt和yum两种包管理器在不同Linux发行版中应用有何区别?
在Linux中,对比apt和yum两种包管理器在不同Linux发行版中应用有何区别?
|
4月前
|
存储 Linux 网络安全
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
【Azure App Service】.NET代码实验App Service应用中获取TLS/SSL 证书 (App Service Linux/Linux Container)
|
4月前
|
JavaScript Linux
【Azure App Service for Linux】NodeJS镜像应用启动失败,遇见 RangeError: Incorrect locale information provided
【Azure App Service for Linux】NodeJS镜像应用启动失败,遇见 RangeError: Incorrect locale information provided