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

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

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

  1. # grub.conf generated by anaconda
  2. #
  3. # Note that you do not have to rerun grub after making changes to this file
  4. # NOTICE: You have a /boot partition. This means that
  5. # all kernel and initrd paths are relative to /boot/, eg.
  6. # root (hd0,0)
  7. # kernel /vmlinuz-version ro root=/dev/sdb2
  8. # initrd /initrd-[generic-]version.img
  9. #boot=/dev/sdb
  10. default=0
  11. timeout=5
  12. splashimage=(hd0,0)/grub/splash.xpm.gz
  13. hiddenmenu
  14. title CentOS (2.6.32-431.el6.i686)
  15. root (hd0,0)
  16. 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
  17. 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通道进行管理:
  1. extern int ioctl (int __fd, unsigned long int __request, …) __THROW;
  2. __fd:设备的ID,例如刚才用open函数打开视频通道后返回的cameraFd
  3. __request:具体的命令标志符。
  4. 在进行V4L2开发中,一般会用到以下的命令标志符:
  5. VIDIOC_REQBUFS:分配内存
  6. VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
  7. VIDIOC_QUERYCAP:查询驱动功能
  8. VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
  9. VIDIOC_S_FMT:设置当前驱动的频捕获格式
  10. VIDIOC_G_FMT:读取当前驱动的频捕获格式
  11. VIDIOC_TRY_FMT:验证当前驱动的显示格式
  12. VIDIOC_CROPCAP:查询驱动的修剪能力
  13. VIDIOC_S_CROP:设置视频信号的边框
  14. VIDIOC_G_CROP:读取视频信号的边框
  15. VIDIOC_QBUF:把数据放回缓存队列
  16. VIDIOC_DQBUF:把数据从缓存中读取出来
  17. VIDIOC_STREAMON:开始视频显示函数
  18. VIDIOC_STREAMOFF:结束视频显示函数
  19. VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PALNTSC
  20. 这些IO调用,有些是必须的,有些是可选择的。

V4L2操作流程:点击这个网址,说得很详细了,这里不多说。

http://baike.baidu.com/view/5494174.htm

接下来我们来看看实战部分,下面是我自己写的程序接口,可以实现视频采集:
1、project.c
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <fcntl.h>
  4. #include "j-yuv.h"
  5. #include "CameralOpt.h"
  6. #include "FrameBufferOpt.h"
  7.  
  8. #define WIDTH 640
  9. #define HIGHT 480
  10.  
  11. int main(void)
  12. {
  13. char yuyv[WIDTH*HIGHT*2];
  14. char bmp[WIDTH*HIGHT*3];
  15.  
  16. // set_bmp_header((struct bmp_header_t *)bmp, WIDTH, HIGHT);
  17. //初始化摄像头
  18. Init_Cameral(WIDTH , HIGHT );
  19. //初始化framebuffer
  20. Init_FrameBuffer(WIDTH , HIGHT );
  21.  
  22. //开启摄像头
  23. Start_Cameral();
  24. //采集一张图片
  25. int count = 0 ;
  26. while(1)
  27. {
  28. Get_Picture(yuyv);
  29. yuyv2rgb24(yuyv, bmp, WIDTH, HIGHT);
  30. Write_FrameBuffer(bmp);
  31. // printf("count:%d \n" , count++);
  32. }
  33. //关闭摄像头
  34. Stop_Cameral();
  35. //关闭Framebuffer
  36. Exit_Framebuffer();
  37. //退出
  38. Exit_Cameral();
  39.  
  40. return 0;
  41. }

2、juv.h

  1. #ifndef __JYUV_H
  2. #define __JYUV_H
  3.  
  4. typedef unsigned char u8;
  5. typedef unsigned short u16;
  6. typedef unsigned int u32;
  7.  
  8. #pragma pack(1)
  9. //定义bmp头
  10. struct bmp_header_t{
  11. u16 magic;
  12. u32 file_size;
  13. u32 RESERVED1;
  14. u32 offset; //54 bytes 表示54个偏移量
  15.  
  16. u32 head_num; //40
  17. u32 width;
  18. u32 height;
  19. u16 color_planes; //1
  20. u16 bit_count;
  21. u32 bit_compression; //0
  22. u32 image_size; //except the size of header
  23. u32 h_resolution;
  24. u32 v_resolution;
  25. u32 color_num;
  26. u32 important_colors;
  27. };
  28.  
  29. #pragma pack()
  30.  
  31. void set_bmp_header(struct bmp_header_t * header, u32 width, u32 height);
  32. int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height);
  33.  
  34. #endif /* __JYUV_H */

3、juv.c

  1. #include "j-yuv.h"
  2.  
  3. #define BIT_COUNT 24
  4.  
  5. void set_bmp_header(struct bmp_header_t *header, u32 width, u32 height)
  6. {
  7. header->magic = 0x4d42;
  8. header->image_size = width * height * BIT_COUNT/8;
  9. header->file_size = header->image_size + 54;
  10. header->RESERVED1 = 0;
  11. header->offset = 54;
  12.  
  13. header->head_num = 40;
  14. header->width = width;
  15. header->height = height;
  16. header->color_planes = 1;
  17. header->bit_count = BIT_COUNT;
  18. header->bit_compression = 0;
  19. header->h_resolution = 0;
  20. header->v_resolution = 0;
  21. header->color_num = 0;
  22. header->important_colors = 0;
  23. }
  24. //yuyv转rgb24的算法实现
  25. int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height)
  26. {
  27. u32 i, in, rgb_index = 0;
  28. u8 y0, u0, y1, v1;
  29. int r, g, b;
  30. u32 out = 0, x, y;
  31.  
  32. for(in = 0; in < width * height * 2; in += 4)
  33. {
  34. y0 = yuyv[in+0];
  35. u0 = yuyv[in+1];
  36. y1 = yuyv[in+2];
  37. v1 = yuyv[in+3];
  38.  
  39. for (i = 0; i < 2; i++)
  40. {
  41. if (i)
  42. y = y1;
  43. else
  44. y = y0;
  45. r = y + (140 * (v1-128))/100; //r
  46. g = y - (34 * (u0-128))/100 - (71 * (v1-128))/100; //g
  47. b = y + (177 * (u0-128))/100; //b
  48. if(r > 255) r = 255;
  49. if(g > 255) g = 255;
  50. if(b > 255) b = 255;
  51. if(r < 0) r = 0;
  52. if(g < 0) g = 0;
  53. if(b < 0) b = 0;
  54.  
  55. y = height - rgb_index/width -1;
  56. x = rgb_index%width;
  57. rgb[(y*width+x)*3+0] = b;
  58. rgb[(y*width+x)*3+1] = g;
  59. rgb[(y*width+x)*3+2] = r;
  60. rgb_index++;
  61. }
  62. }
  63. return 0;
  64. }

4、FrameBufferOpt.c

  1. #include "FrameBufferOpt.h"
  2.  
  3. static int Frame_fd ;
  4. static int *FrameBuffer = NULL ;
  5. static int W , H ;
  6.  
  7. //初始化framebuffer
  8. int Init_FrameBuffer(int Width , int Higth)
  9. {
  10. W = Width ;
  11. H = Higth ;
  12. Frame_fd = open("/dev/fb" , O_RDWR);
  13. if(-1 == Frame_fd)
  14. {
  15. perror("open frame buffer fail");
  16. return -1 ;
  17. }
  18.  
  19. //根本就不用CPU搬运 用DMA做为搬运工
  20. FrameBuffer = mmap(0, 1280*1024*4 , PROT_READ | PROT_WRITE , MAP_SHARED , Frame_fd ,0 );
  21. if(FrameBuffer == (void *)-1)
  22. {
  23. perror("memory map fail");
  24. return -2 ;
  25. }
  26. return 0 ;
  27. }
  28.  
  29. //写入framebuffer
  30. int Write_FrameBuffer(const char *buffer)
  31. {
  32. int row , col ;
  33. char *p = NULL ;
  34. for(row = 0 ; row <1024 ; row++)
  35. {
  36. for(col = 0 ; col < 1280 ; col++)
  37. {
  38. if((row < H) && (col < W))
  39. {
  40. p = (char *)(buffer + (row * W+ col ) * 3);
  41. FrameBuffer[row*1280+col] = RGB((unsigned char)(*(p+2)),(unsigned char)(*(p+1)),(unsigned char )(*p));
  42. }
  43. }
  44. }
  45. return 0 ;
  46. }
  47.  
  48. //退出framebuffer
  49. int Exit_Framebuffer(void)
  50. {
  51. munmap(FrameBuffer , W*H*4);
  52. close(Frame_fd);
  53. return 0 ;
  54. }

5、FrameBufferOpt.h

  1. #ifndef _FRAMEBUFFEROPT_H
  2. #define _FRAMEBUFFEROPT_H
  3.  
  4. #include <stdio.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <sys/mman.h>
  8.  
  9. #define RGB(r,g,b) ((r<<16)|(g<<8)|b)
  10.  
  11. //初始化ramebuffer
  12. int Init_FrameBuffer(int Width , int Higth);
  13.  
  14. //写数据到framebuffer
  15. int Write_FrameBuffer(const char *buffer);
  16.  
  17. //退出framebuffer
  18. int Exit_Framebuffer(void);
  19.  
  20. #endif //_FRAMEBUFFEROPT_H

6、CameralOpt.h

  1. #ifndef _CAMERALOPT_H
  2. #define _CAMERALOPT_H
  3.  
  4. #include <stdio.h>
  5. #include <linux/videodev2.h>
  6. #include <fcntl.h>
  7. #include <unistd.h>
  8. #include <string.h>
  9. #include <stdlib.h>
  10. #include <errno.h>
  11. #include <sys/mman.h>
  12.  
  13. #define COUNT 3
  14. //初始化摄像头
  15. int Init_Cameral(int Width , int Hight);
  16. int Exit_Cameral(void); //退出摄像头
  17. //摄像头开始采集
  18. int Start_Cameral(void);
  19. int Stop_Cameral(void);//停止摄像头
  20. //获取摄像头的数据
  21. int Get_Picture(char *buffer);
  22.  
  23. #endif //_CAMERALOPT_H

7、CameralOpt.c

  1. #include "CameralOpt.h"
  2. int video_fd ;
  3. int length ;
  4. char *yuv[COUNT] ;
  5. struct v4l2_buffer enqueue , dequeue ; //定义出入队的操作结构体成员
  6.  
  7. int Init_Cameral(int Width , int Hight)
  8. {
  9. //参数检查
  10. char *videodevname = NULL ;
  11. videodevname = "/dev/video0" ;
  12.  
  13. //打开设备
  14. video_fd = open(videodevname , O_RDWR);
  15. if(-1 == video_fd )
  16. {
  17. perror("open video device fail");
  18. return -1 ;
  19. }
  20.  
  21. int i ;
  22. int ret ;
  23. struct v4l2_format format ;
  24. format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  25. format.fmt.pix.width = Width;
  26. format.fmt.pix.height = Hight;
  27. format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV ; //我支持的格式是这个
  28.  
  29. ret = ioctl(video_fd , VIDIOC_S_FMT , &format);
  30. if(ret != 0)
  31. {
  32. perror("set video format fail");
  33. return -2 ;
  34. }
  35.  
  36. //申请buffer,切割成几个部分
  37. //3
  38. struct v4l2_requestbuffers requestbuffer ;
  39. requestbuffer.count = COUNT ;
  40. requestbuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  41. requestbuffer.memory = V4L2_MEMORY_MMAP ;
  42.  
  43. ret = ioctl(video_fd , VIDIOC_REQBUFS , &requestbuffer);
  44. if(ret != 0)
  45. {
  46. perror("request buffer fail ");
  47. return -3 ;
  48. }
  49.  
  50. //querybuffer
  51. struct v4l2_buffer querybuffer ;
  52. querybuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  53. querybuffer.memory = V4L2_MEMORY_MMAP ;
  54.  
  55. for(i = 0 ; i < COUNT ; i++)
  56. {
  57. querybuffer.index = i ;
  58.  
  59. ret = ioctl(video_fd , VIDIOC_QUERYBUF , &querybuffer);
  60. if(ret != 0)
  61. {
  62. perror("query buffer fail");
  63. return -4 ;
  64. }
  65.  
  66. // printf("index:%d length:%d offset:%d \n" ,
  67. // querybuffer.index , querybuffer.length , querybuffer.m.offset);
  68. length = querybuffer.length ;
  69.  
  70. //将摄像头内存印射到进程的内存地址
  71. yuv[i] = mmap(0,querybuffer.length , PROT_READ | PROT_WRITE , MAP_SHARED , video_fd , querybuffer.m.offset );
  72.  
  73. //列队
  74.  
  75. struct v4l2_buffer queuebuffer ;
  76. queuebuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  77. queuebuffer.memory = V4L2_MEMORY_MMAP ;
  78. queuebuffer.index = i ;
  79.  
  80. ret = ioctl(video_fd , VIDIOC_QBUF , &queuebuffer);
  81. if(ret != 0)
  82. {
  83. perror("queuebuffer fail");
  84. return -5 ;
  85. }
  86. }
  87. //初始化入队出队
  88. enqueue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  89. dequeue.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
  90. enqueue.memory = V4L2_MEMORY_MMAP ;
  91. dequeue.memory = V4L2_MEMORY_MMAP ;
  92.  
  93. return 0 ;
  94. }
  95.  
  96. int Exit_Cameral(void)
  97. {
  98. int i ;
  99. for(i = 0 ; i < COUNT ; i++)
  100. munmap(yuv+i , length);
  101. close(video_fd);
  102. return 0 ;
  103. }
  104.  
  105. int Start_Cameral(void)
  106. {
  107. //开启摄像头
  108. int ret ;
  109. int on = 1 ;
  110. ret = ioctl(video_fd , VIDIOC_STREAMON , &on);
  111. if(ret != 0)
  112. {
  113. perror("start Cameral fail");
  114. return -1 ;
  115. }
  116. return 0 ;
  117. }
  118. int Stop_Cameral(void)
  119. {
  120. //停止摄像头
  121. int ret ;
  122. int off= 1 ;
  123. ret = ioctl(video_fd , VIDIOC_STREAMOFF, &off);
  124. if(ret != 0)
  125. {
  126. perror("stop Cameral fail");
  127. return -1 ;
  128. }
  129. return 0 ;
  130. }
  131.  
  132. int Get_Picture(char *buffer)
  133. {
  134. int ret ;
  135. //出队
  136. ret = ioctl(video_fd , VIDIOC_DQBUF , &dequeue);
  137. if(ret != 0)
  138. {
  139. perror("dequeue fail");
  140. return -1 ;
  141. }
  142.  
  143. //获取图片数据 YUV yuv[dequeue.index]
  144. memcpy(buffer , yuv[dequeue.index] , dequeue.length);
  145. // write(yuyv_fd , yuv[dequeue.index] , dequeue.length);
  146.  
  147. enqueue.index = dequeue.index ;
  148. ret = ioctl(video_fd , VIDIOC_QBUF , &enqueue);
  149. if(ret != 0)
  150. {
  151. perror("enqueue fail");
  152. return -2 ;
  153. }
  154. return 0 ;
  155. }

运行结果:楼主本人,长得丑别喷。

画面其实是一直在动的,只是我拍了一张图片而已。












C语言高级应用---操作linux下V4L2摄像头应用程序的更多相关文章

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

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

  2. Linux 下V4l2摄像头采集图片,实现yuyv转RGB,RGB转BMP,RGB伸缩,jpeglib 库实现压缩RGB到内存中,JPEG经UDP发送功(转)

    ./configure CC=arm-linux-gnueabihf-gcc LD=arm-linux-gnueabihf-ld --host=arm-linux --prefix=/usr/loca ...

  3. 不错的linux下通用的java程序启动脚本

    不错的linux下通用的java程序启动脚本(转载) 虽然写起动shell的频率非常不高...但是每次要写都要对付一大堆的jar文件路径,新加jar包也必须要修改起动shell. 在网上找到一个挺好的 ...

  4. 在64位linux下编译32位程序

    在64位linux下编译32位程序 http://blog.csdn.net/xsckernel/article/details/38045783

  5. linux之V4L2摄像头应用流程【转】

    本文转载自:http://blog.csdn.net/tommy_wxie/article/details/11486907 对于v4l2,上次是在调试收音机驱动的时候用过,其他也就只是用i2c配置一 ...

  6. Linux编程环境介绍(3) -- linux下的c/c++程序开发

    目录: 1. 编辑器( Vi ) [vi 与 vim] vi(visual interface)是linux系统最重要的文本编辑器, 所有的 Unix-Like 系统都会内置vi文本编辑器.  vim ...

  7. 在linux下如何编译C++程序

    一.GCC(GNU Compiler Collection)是linux下最主要的编译工具,GCC不仅功能非常强大,结构也异常灵活.它可以通过不同的前端模块来支持各种语言,如Java.Fortran. ...

  8. .NET作品集:linux下的博客程序

    博客程序架构 本博客程序是博主11年的时候参考loachs小泥鳅博客内核开发的.net跨平台博客cms,距今已有6年多了,个人博客网站一直在用,虽然没有wordpress那么强大,但是当时在深究.ne ...

  9. 不错的linux下通用的java程序启动脚本(转载)

    转自:http://www.cnblogs.com/langtianya/p/4164151.html 虽然写起动shell的频率非常不高...但是每次要写都要对付一大堆的jar文件路径,新加jar包 ...

随机推荐

  1. Jstorm与RocketMQ整合

    如果是经常关注阿里巴巴的朋友们,看到我这篇博客的题目,就知道我在参加今年的中间件比赛. 好了,废话不说,开始了. 首先我们知道,rocketmq的consumer有两种,一种是DefaultMQPus ...

  2. C库源码中的移位函数

    #include <stdio.h> /* _lrotr()将一个无符号长整形数左循环移位的函数 原形:unsigned long _lrotr(unsigned long value,i ...

  3. iOS7 CookBook精彩瞬间(一)property、selector细节、__unused

    1.我们常常使用nonatomic,很多人只知道它的效率较高,却不知道其含义,其含义是非线程安全的,也就是说多线程修改时不加锁,可能出现多个线程先后修改而成为脏数据的情况. 2.unsafe_unre ...

  4. python在windows下使用setuptools安装egg文件

    最近和同学做个东西,需要安装python的第三方函数库,看了网上的介绍,很是麻烦,这是我实践总结出来的,希望对大家有用. 以安装第三方库networkx 为例,其余函数库都是一个套路,看完就会滴. 1 ...

  5. 1068. Find More Coins (30)

    题目如下: Eva loves to collect coins from all over the universe, including some other planets like Mars. ...

  6. 【一天一道LeetCode】#344. Reverse String

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...

  7. IOS中 浅谈iOS中MVVM的架构设计与团队协作

    今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...

  8. tomcat服务器虚拟目录的映射方式

    lWEB应用程序指供浏览器问的程序,通常也简称为web应用 l l一个web应用由多个静态web资源和动态web资源组成,如: •html.css.js文件 •jsp文件.servlet程序.支持ja ...

  9. 如何在Cocos2D游戏中实现A*寻路算法(四)

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...

  10. UNIX环境高级编程——守护进程列表

    amd:自动安装NFS(网络文件系统)守侯进程apmd:高级电源治理Arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和ip地址对数据库Autofs:自动安装治理进程automount ...