C语言高级应用---操作linux下V4L2摄像头应用程序
我们都知道,想要驱动linux下的摄像头,其实很简单,照着V4L2的手册一步步来写,很快就可以写出来,但是在写之前我们要注意改变系统的一些配置,使系统支持framebuffer,在dev下产生fb0这样的节点,这样我们才能在linux系统上操作Camera摄像头,framebuffer在之前的博文已经有说过了,这里就不再提了。
有需要了解framebuffer的那么请点击:http://baike.baidu.com/view/3351639.htm
最重要的,我们需要改一个脚本,在/dev/grub.conf,我们来看看怎么改:
- # 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=???(这里是参数),简单介绍一下:
色彩 | 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的主要功能。
采集方式
- 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

- #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
- #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
- #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
- #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
- #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
- #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
- #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 ;
- }
运行结果:楼主本人,长得丑别喷。
C语言高级应用---操作linux下V4L2摄像头应用程序的更多相关文章
- C语言高级应用---操作linux下V4L2摄像头应用程序【转】
转自:http://blog.csdn.net/morixinguan/article/details/51001713 版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog ...
- 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 ...
- 不错的linux下通用的java程序启动脚本
不错的linux下通用的java程序启动脚本(转载) 虽然写起动shell的频率非常不高...但是每次要写都要对付一大堆的jar文件路径,新加jar包也必须要修改起动shell. 在网上找到一个挺好的 ...
- 在64位linux下编译32位程序
在64位linux下编译32位程序 http://blog.csdn.net/xsckernel/article/details/38045783
- linux之V4L2摄像头应用流程【转】
本文转载自:http://blog.csdn.net/tommy_wxie/article/details/11486907 对于v4l2,上次是在调试收音机驱动的时候用过,其他也就只是用i2c配置一 ...
- Linux编程环境介绍(3) -- linux下的c/c++程序开发
目录: 1. 编辑器( Vi ) [vi 与 vim] vi(visual interface)是linux系统最重要的文本编辑器, 所有的 Unix-Like 系统都会内置vi文本编辑器. vim ...
- 在linux下如何编译C++程序
一.GCC(GNU Compiler Collection)是linux下最主要的编译工具,GCC不仅功能非常强大,结构也异常灵活.它可以通过不同的前端模块来支持各种语言,如Java.Fortran. ...
- .NET作品集:linux下的博客程序
博客程序架构 本博客程序是博主11年的时候参考loachs小泥鳅博客内核开发的.net跨平台博客cms,距今已有6年多了,个人博客网站一直在用,虽然没有wordpress那么强大,但是当时在深究.ne ...
- 不错的linux下通用的java程序启动脚本(转载)
转自:http://www.cnblogs.com/langtianya/p/4164151.html 虽然写起动shell的频率非常不高...但是每次要写都要对付一大堆的jar文件路径,新加jar包 ...
随机推荐
- Jstorm与RocketMQ整合
如果是经常关注阿里巴巴的朋友们,看到我这篇博客的题目,就知道我在参加今年的中间件比赛. 好了,废话不说,开始了. 首先我们知道,rocketmq的consumer有两种,一种是DefaultMQPus ...
- C库源码中的移位函数
#include <stdio.h> /* _lrotr()将一个无符号长整形数左循环移位的函数 原形:unsigned long _lrotr(unsigned long value,i ...
- iOS7 CookBook精彩瞬间(一)property、selector细节、__unused
1.我们常常使用nonatomic,很多人只知道它的效率较高,却不知道其含义,其含义是非线程安全的,也就是说多线程修改时不加锁,可能出现多个线程先后修改而成为脏数据的情况. 2.unsafe_unre ...
- python在windows下使用setuptools安装egg文件
最近和同学做个东西,需要安装python的第三方函数库,看了网上的介绍,很是麻烦,这是我实践总结出来的,希望对大家有用. 以安装第三方库networkx 为例,其余函数库都是一个套路,看完就会滴. 1 ...
- 1068. Find More Coins (30)
题目如下: Eva loves to collect coins from all over the universe, including some other planets like Mars. ...
- 【一天一道LeetCode】#344. Reverse String
一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Write a ...
- IOS中 浅谈iOS中MVVM的架构设计与团队协作
今天写这篇文章是想达到抛砖引玉的作用,想与大家交流一下思想,相互学习,博文中有不足之处还望大家批评指正.本篇文章的内容沿袭以往博客的风格,也是以干货为主,偶尔扯扯咸蛋(哈哈~不好好工作又开始发表博客啦 ...
- tomcat服务器虚拟目录的映射方式
lWEB应用程序指供浏览器问的程序,通常也简称为web应用 l l一个web应用由多个静态web资源和动态web资源组成,如: •html.css.js文件 •jsp文件.servlet程序.支持ja ...
- 如何在Cocos2D游戏中实现A*寻路算法(四)
大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请告诉我,如果觉得不错请多多支持点赞.谢谢! hopy ;) 免责申明:本博客提供的所有翻译文章原稿均来自互联网,仅供学习交流 ...
- UNIX环境高级编程——守护进程列表
amd:自动安装NFS(网络文件系统)守侯进程apmd:高级电源治理Arpwatch:记录日志并构建一个在LAN接口上看到的以太网地址和ip地址对数据库Autofs:自动安装治理进程automount ...