C语言高级应用---操作linux下V4L2摄像头应用程序【转】
转自: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,)
# kernel /vmlinuz-version ro root=/dev/sdb2
# initrd /initrd-[generic-]version.img
#boot=/dev/sdb
default=
timeout=
splashimage=(hd0,)/grub/splash.xpm.gz
hiddenmenu
title CentOS (2.6.-.el6.i686)
root (hd0,)
kernel /vmlinuz-2.6.-.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- rd_NO_LVM rd_NO_DM rhgb quiet vga=0x318
initrd /initramfs-2.6.-.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
接下来我们来看看实战部分,下面是我自己写的程序接口,可以实现视频采集:生气
、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*];
char bmp[WIDTH*HIGHT*]; // set_bmp_header((struct bmp_header_t *)bmp, WIDTH, HIGHT);
//初始化摄像头
Init_Cameral(WIDTH , HIGHT );
//初始化framebuffer
Init_FrameBuffer(WIDTH , HIGHT ); //开启摄像头
Start_Cameral();
//采集一张图片
int count = ;
while()
{
Get_Picture(yuyv);
yuyv2rgb24(yuyv, bmp, WIDTH, HIGHT);
Write_FrameBuffer(bmp);
// printf("count:%d \n" , count++);
}
//关闭摄像头
Stop_Cameral();
//关闭Framebuffer
Exit_Framebuffer();
//退出
Exit_Cameral(); return ;
} 、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 */ 、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/;
header->file_size = header->image_size + ;
header->RESERVED1 = ;
header->offset = ; header->head_num = ;
header->width = width;
header->height = height;
header->color_planes = ;
header->bit_count = BIT_COUNT;
header->bit_compression = ;
header->h_resolution = ;
header->v_resolution = ;
header->color_num = ;
header->important_colors = ;
}
//yuyv转rgb24的算法实现
int yuyv2rgb24(u8 *yuyv, u8 *rgb, u32 width, u32 height)
{
u32 i, in, rgb_index = ;
u8 y0, u0, y1, v1;
int r, g, b;
u32 out = , x, y; for(in = ; in < width * height * ; in += )
{
y0 = yuyv[in+];
u0 = yuyv[in+];
y1 = yuyv[in+];
v1 = yuyv[in+]; for (i = ; i < ; i++)
{
if (i)
y = y1;
else
y = y0;
r = y + ( * (v1-))/; //r
g = y - ( * (u0-))/ - ( * (v1-))/; //g
b = y + ( * (u0-))/; //b
if(r > ) r = ;
if(g > ) g = ;
if(b > ) b = ;
if(r < ) r = ;
if(g < ) g = ;
if(b < ) b = ; y = height - rgb_index/width -;
x = rgb_index%width;
rgb[(y*width+x)*+] = b;
rgb[(y*width+x)*+] = g;
rgb[(y*width+x)*+] = r;
rgb_index++;
}
}
return ;
} 、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(- == Frame_fd)
{
perror("open frame buffer fail");
return - ;
} //根本就不用CPU搬运 用DMA做为搬运工
FrameBuffer = mmap(, ** , PROT_READ | PROT_WRITE , MAP_SHARED , Frame_fd , );
if(FrameBuffer == (void *)-)
{
perror("memory map fail");
return - ;
}
return ;
} //写入framebuffer
int Write_FrameBuffer(const char *buffer)
{
int row , col ;
char *p = NULL ;
for(row = ; row < ; row++)
{
for(col = ; col < ; col++)
{
if((row < H) && (col < W))
{
p = (char *)(buffer + (row * W+ col ) * );
FrameBuffer[row*+col] = RGB((unsigned char)(*(p+)),(unsigned char)(*(p+)),(unsigned char )(*p));
}
}
}
return ;
} //退出framebuffer
int Exit_Framebuffer(void)
{
munmap(FrameBuffer , W*H*);
close(Frame_fd);
return ;
} 、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 、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 、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(- == video_fd )
{
perror("open video device fail");
return - ;
} 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 != )
{
perror("set video format fail");
return - ;
} //申请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 != )
{
perror("request buffer fail ");
return - ;
} //querybuffer
struct v4l2_buffer querybuffer ;
querybuffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
querybuffer.memory = V4L2_MEMORY_MMAP ; for(i = ; i < COUNT ; i++)
{
querybuffer.index = i ; ret = ioctl(video_fd , VIDIOC_QUERYBUF , &querybuffer);
if(ret != )
{
perror("query buffer fail");
return - ;
} // printf("index:%d length:%d offset:%d \n" ,
// querybuffer.index , querybuffer.length , querybuffer.m.offset);
length = querybuffer.length ; //将摄像头内存印射到进程的内存地址
yuv[i] = mmap(,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 != )
{
perror("queuebuffer fail");
return - ;
}
}
//初始化入队出队
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 ;
} int Exit_Cameral(void)
{
int i ;
for(i = ; i < COUNT ; i++)
munmap(yuv+i , length);
close(video_fd);
return ;
} int Start_Cameral(void)
{
//开启摄像头
int ret ;
int on = ;
ret = ioctl(video_fd , VIDIOC_STREAMON , &on);
if(ret != )
{
perror("start Cameral fail");
return - ;
}
return ;
}
int Stop_Cameral(void)
{
//停止摄像头
int ret ;
int off= ;
ret = ioctl(video_fd , VIDIOC_STREAMOFF, &off);
if(ret != )
{
perror("stop Cameral fail");
return - ;
}
return ;
} int Get_Picture(char *buffer)
{
int ret ;
//出队
ret = ioctl(video_fd , VIDIOC_DQBUF , &dequeue);
if(ret != )
{
perror("dequeue fail");
return - ;
} //获取图片数据 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 != )
{
perror("enqueue fail");
return - ;
}
return ;
}
C语言高级应用---操作linux下V4L2摄像头应用程序【转】的更多相关文章
- C语言高级应用---操作linux下V4L2摄像头应用程序
我们都知道,想要驱动linux下的摄像头,其实很简单,照着V4L2的手册一步步来写,很快就可以写出来,但是在写之前我们要注意改变系统的一些配置,使系统支持framebuffer,在dev下产生fb0这 ...
- 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包 ...
随机推荐
- Android面试收集录 Android布局
1.请说出Android中的五种布局,并介绍作用? FrameLayout(堆栈布局),层叠方式显示,类似于PhotoShop上的层叠图层. LinearLayout(线性布局),将视图以水平或者垂直 ...
- 算法学习记录-图——应用之关键路径(Critical Path)
之前我们介绍过,在一个工程中我们关心两个问题: (1)工程是否顺利进行 (2)整个工程最短时间. 之前我们优先关心的是顶点(AOV),同样我们也可以优先关心边(同理有AOE).(Activity On ...
- Java-多线程与单例
最近在公司写需求时遇到了多线程与单例一同出现的情况. 这个时候想到的就是线程安全以及单例的定义了,虽然单例指的是在内存中它只有一份,但是并不是说就是线程安全的. 所以,我当时就到网上找了关于多线程下单 ...
- 让菜鸡讲一讲费用流(EK)
让我再讲一个故事吧. 又有一些小精灵要准备从银月城(S)迁徙到Nibel山(T). 这两个地方之间的道路构成了一个网络. 每个道路都有它自己的容量,这决定了每天有多少小精灵可以同时从这儿通过. 和上一 ...
- 最“高大上”的Spring测试:Spring Test
我想给大家介绍一款非常实用.且高端大气上档次的spring测试,在这里,我要强烈推荐使用Spring的Test Context框架,为什么呢?俗话说,“货比三家不上当”,要搞清楚这个问题,我们先来看一 ...
- apache的/etc/httpd/conf/httpd.conf和/usr/local/apache2/conf/httpd.conf区别
一.问题 centos系统用yum安装完apache后,重启后有时会失效,然后去网上找资料,发现有的说重启命令是这样的: /etc/init.d/httpd restart 而有的呢,说重启命令应该是 ...
- python学习笔记-参数带*
#!/usr/bin/python # -*- coding: utf-8 -*- def powersum (power,*args): #所有多余的参数都会作为一个元组存储在args中 s ...
- ThreadLocal 学习
JDK 1.2版本就已经提供了java.lang.ThreadLocal.其为多线程程序的并发问题提供了一种新的思路.使用该工具类可以简洁地编写出优美的多线程程序. 当使用ThreadLocal维护变 ...
- sublime3 Package Control和 中文安装
sublime3中文版需要使用PackageControl,所以首先需要安装PackageControl 一.PackageControl安装: 1.点击Preferences > Browse ...
- linux kernal oom killer 学习
背景 我有2个定时任务,一个任务A是00:00开跑,另一个B是04:00开跑.正常情况下A会在2点多时候跑完,但是某一天因为某一步骤用的时间过久,导致4点还没跑完,这时候A内存占用大约在12g左右.4 ...