V4L2(Video for Linux 2)是一个用于Linux操作系统的视频捕捉和输出框架。它提供了一组API和驱动程序接口,用于与摄像头设备进行交互和控制。
如果你想进行V4L2摄像头编程,你可以按照以下步骤进行:
1. 确保你的摄像头设备已连接到计算机上并正确识别。你可以使用`ls /dev/video*`命令来检查设备是否存在。
2. 创建一个C或C++程序,并包含V4L2的头文件。例如,在C++中,你可以包含 `<linux/videodev2.h>` 头文件。
3. 打开摄像头设备文件。你可以使用`open()`函数来打开设备文件,例如`/dev/video0`。
4. 查询摄像头设备的能力和属性。使用`ioctl()`函数和`VIDIOC_QUERYCAP`命令来获取设备的能力和属性信息。
5. 配置摄像头的捕捉参数。使用`ioctl()`函数和`VIDIOC_S_FMT`命令来设置图像格式、分辨率和帧率等参数。
6. 请求和映射摄像头的视频缓冲区。使用`ioctl()`函数和`VIDIOC_REQBUFS`命令来请求摄像头的视频缓冲区,并使用`mmap()`函数将缓冲区映射到用户空间。
7. 启动视频流捕捉。使用`ioctl()`函数和`VIDIOC_STREAMON`命令来启动视频流捕捉。
8. 捕捉图像数据。使用`ioctl()`函数和`VIDIOC_DQBUF`命令来从摄像头的视频缓冲区中获取图像数据。
9. 处理和使用图像数据。你可以对图像数据进行处理、存储或者展示。
10. 释放视频缓冲区并停止视频流捕捉。使用`ioctl()`函数和`VIDIOC_STREAMOFF`命令停止视频流捕捉,并使用`munmap()`函数释放视频缓冲区的映射。
11. 关闭摄像头设备。使用`close()`函数关闭设备文件。
v4l2camera没有使用多路复用.c
#include <stdio.h>
#include <linux/videodev2.h> //V4L2对应的头文件
#include <stropts.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#define W 640
#define H 480
//自定义结构体,用来存放每个缓冲块的首地址和大小
struct bufmsg
{
void *start; //缓冲块的首地址
int somelen; //缓冲块的大小
};
//封装函数--》把一组YUV--》转换ARGB数据
int yuvtoargb(int y,int u,int v)
{
int r,g,b;
int pix;
r = 1164*(y-16)/1000 + 1596*(v-128)/1000;
g = 1164*(y-16)/1000 + 813*(v-128)/1000 - 391*(u-128)/1000;
b = 1164*(y-16)/1000 + 2018*(u-128)/1000;
//修正计算结果 0---255之间
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;
//把rgb拼接得到ARGB
pix=0x00<<24|r<<16|g<<8|b;
return pix;
}
//封装函数--》把一帧画面数据中所有的YUYV数据转换成ARGB数据
//参数:yuv --》指向一帧yuyv画面首地址
// argbbuf --》存放转换得到的完整的ARGB数据
//allyuvtoargb(array[i].start,argbbuf)
/*
yuv[0]--第一个Y
yuv[1]-- U
yuv[2]--第二个Y
yuv[3]-- V
*/
int allyuvtoargb(char *yuv,int *argbbuf)
{
int i,j;
for(i=0,j=0; i<W*H; i+=2,j+=4)
{
//一组YUYV转换得到两组ARGB数据
argbbuf[i]=yuvtoargb(yuv[j],yuv[j+1],yuv[j+3]);
argbbuf[i+1]=yuvtoargb(yuv[j+2],yuv[j+1],yuv[j+3]);
}
return 0;
}
int main()
{
int camerafd;
int ret;
int i,j;
int lcdfd;
int *lcdmem;
//打开lcd
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
perror("打开lcd失败!\n");
return -1;
}
//映射得到lcd的首地址
lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
if(lcdmem==NULL)
{
perror("映射lcd失败了!\n");
return -1;
}
//打开摄像头的驱动
camerafd=open("/dev/video7",O_RDWR);
if(camerafd==-1)
{
perror("打开摄像头失败!\n");
return -1;
}
//设置摄像头采集格式
struct v4l2_format myfmt;
bzero(&myfmt,sizeof(myfmt));
myfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
myfmt.fmt.pix.width=W;
myfmt.fmt.pix.height=H;
myfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
ret=ioctl(camerafd,VIDIOC_S_FMT,&myfmt);
if(ret==-1)
{
perror("设置采集格式失败!\n");
return -1;
}
//申请4个缓冲块
struct v4l2_requestbuffers mybuf;
bzero(&mybuf,sizeof(mybuf));
mybuf.count=4; //4个缓冲块
mybuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
mybuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_REQBUFS,&mybuf);
if(ret==-1)
{
perror("申请缓冲块失败!\n");
return -1;
}
//分配缓冲块顺便映射得到4个缓冲块的首地址
struct v4l2_buffer otherbuf;
//定义结构体数组存放4个缓冲块的首地址和大小
struct bufmsg array[4];
for(i=0; i<4; i++)
{
bzero(&otherbuf,sizeof(otherbuf));
otherbuf.index=i; //索引号
otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
otherbuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_QUERYBUF,&otherbuf);
if(ret==-1)
{
perror("分配缓冲块失败!\n");
return -1;
}
//顺便映射得到4个缓冲块的首地址
array[i].somelen=otherbuf.length;
array[i].start=mmap(NULL,otherbuf.length,PROT_READ|PROT_WRITE,MAP_SHARED,camerafd,otherbuf.m.offset);
if(array[i].start==NULL)
{
perror("映射缓冲块失败了!\n");
return -1;
}
//顺便发送入队申请(一旦摄像头启动了,立马把画面入队)
ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
//启动摄像头捕捉
enum v4l2_buf_type mytype;
mytype=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl(camerafd,VIDIOC_STREAMON,&mytype);
if(ret==-1)
{
perror("启动摄像头捕捉失败!\n");
return -1;
}
//定义一个数组存放转换得到的一帧完整的ARGB数据
int argbbuf[W*H];
//让摄像头画面循环出队,入队形成视频流在lcd上显示出来
while(1)
{
for(i=0; i<4; i++)
{
//出队
bzero(&otherbuf,sizeof(otherbuf));
otherbuf.index=i; //索引号
otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
otherbuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_DQBUF,&otherbuf);
if(ret==-1)
{
perror("出队失败!\n");
return -1;
}
//出队画面(在结构体数组里面存放着)在lcd上显示出来
//array[i].start --》 当前出队的画面数据(YUV格式)首地址
//array[i].somelen --》当前出队的画面数据大小
//YUV格式无法直接在lcd上显示,原因是lcd只能显示ARGB数据
/*
第一行数据: argbbuf[0]--argbbuf[W-1]
lcdmem
第二行数据: argbbuf[W]--argbbuf[2*W-1]
lcdmem+下一行
*/
allyuvtoargb(array[i].start,argbbuf);
//把argbbuf中的数据填充到lcd对应的位置
for(j=0; j<H; j++)
memcpy(lcdmem+800*j,&argbbuf[W*j],W*4);
//入队
ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
}
//收尾
ret=ioctl(camerafd,VIDIOC_STREAMOFF,&mytype);
if(ret==-1)
{
perror("关闭摄像头捕捉失败!\n");
return -1;
}
munmap(lcdmem,800*480*4);
for(i=0; i<4; i++)
munmap(array[i].start,array[i].somelen);
close(lcdfd);
close(camerafd);
return 0;
}
v4l2camera使用了多路复用.c:
#include <stdio.h>
#include <linux/videodev2.h> //V4L2对应的头文件
#include <stropts.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#define W 640
#define H 480
//自定义结构体,用来存放每个缓冲块的首地址和大小
struct bufmsg
{
void *start; //缓冲块的首地址
int somelen; //缓冲块的大小
};
//封装函数--》把一组YUV--》转换ARGB数据
int yuvtoargb(int y,int u,int v)
{
int r,g,b;
int pix;
r = 1164*(y-16)/1000 + 1596*(v-128)/1000;
g = 1164*(y-16)/1000 + 813*(v-128)/1000 - 391*(u-128)/1000;
b = 1164*(y-16)/1000 + 2018*(u-128)/1000;
//修正计算结果 0---255之间
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;
//把rgb拼接得到ARGB
pix=0x00<<24|r<<16|g<<8|b;
return pix;
}
//封装函数--》把一帧画面数据中所有的YUYV数据转换成ARGB数据
//参数:yuv --》指向一帧yuyv画面首地址
// argbbuf --》存放转换得到的完整的ARGB数据
//allyuvtoargb(array[i].start,argbbuf)
/*
yuv[0]--第一个Y
yuv[1]-- U
yuv[2]--第二个Y
yuv[3]-- V
*/
int allyuvtoargb(char *yuv,int *argbbuf)
{
int i,j;
for(i=0,j=0; i<W*H; i+=2,j+=4)
{
//一组YUYV转换得到两组ARGB数据
argbbuf[i]=yuvtoargb(yuv[j],yuv[j+1],yuv[j+3]);
argbbuf[i+1]=yuvtoargb(yuv[j+2],yuv[j+1],yuv[j+3]);
}
return 0;
}
int main()
{
int camerafd;
int ret;
int i,j;
int lcdfd;
int *lcdmem;
//打开lcd
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
perror("打开lcd失败!\n");
return -1;
}
//映射得到lcd的首地址
lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
if(lcdmem==NULL)
{
perror("映射lcd失败了!\n");
return -1;
}
//打开摄像头的驱动
camerafd=open("/dev/video7",O_RDWR);
if(camerafd==-1)
{
perror("打开摄像头失败!\n");
return -1;
}
//设置摄像头采集格式
struct v4l2_format myfmt;
bzero(&myfmt,sizeof(myfmt));
myfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
myfmt.fmt.pix.width=W;
myfmt.fmt.pix.height=H;
myfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
ret=ioctl(camerafd,VIDIOC_S_FMT,&myfmt);
if(ret==-1)
{
perror("设置采集格式失败!\n");
return -1;
}
//申请4个缓冲块
struct v4l2_requestbuffers mybuf;
bzero(&mybuf,sizeof(mybuf));
mybuf.count=4; //4个缓冲块
mybuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
mybuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_REQBUFS,&mybuf);
if(ret==-1)
{
perror("申请缓冲块失败!\n");
return -1;
}
//分配缓冲块顺便映射得到4个缓冲块的首地址
struct v4l2_buffer otherbuf;
//定义结构体数组存放4个缓冲块的首地址和大小
struct bufmsg array[4];
for(i=0; i<4; i++)
{
bzero(&otherbuf,sizeof(otherbuf));
otherbuf.index=i; //索引号
otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
otherbuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_QUERYBUF,&otherbuf);
if(ret==-1)
{
perror("分配缓冲块失败!\n");
return -1;
}
//顺便映射得到4个缓冲块的首地址
array[i].somelen=otherbuf.length;
array[i].start=mmap(NULL,otherbuf.length,PROT_READ|PROT_WRITE,MAP_SHARED,camerafd,otherbuf.m.offset);
if(array[i].start==NULL)
{
perror("映射缓冲块失败了!\n");
return -1;
}
//顺便发送入队申请(一旦摄像头启动了,立马把画面入队)
ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
//启动摄像头捕捉
enum v4l2_buf_type mytype;
mytype=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl(camerafd,VIDIOC_STREAMON,&mytype);
if(ret==-1)
{
perror("启动摄像头捕捉失败!\n");
return -1;
}
//定义一个数组存放转换得到的一帧完整的ARGB数据
int argbbuf[W*H];
//定义集合存放要监测的文件描述符
fd_set myset;
FD_ZERO(&myset);
FD_SET(camerafd,&myset);
//让摄像头画面循环出队,入队形成视频流在lcd上显示出来
while(1)
{
for(i=0; i<4; i++)
{
//监测摄像头队列中是否有数据可读
ret=select(camerafd+1,&myset,NULL,NULL,NULL);
if(ret>0) //说明摄像头的缓冲区中有画面入队了
{
//出队
bzero(&otherbuf,sizeof(otherbuf));
otherbuf.index=i; //索引号
otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
otherbuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_DQBUF,&otherbuf);
if(ret==-1)
{
perror("出队失败!\n");
return -1;
}
//出队画面(在结构体数组里面存放着)在lcd上显示出来
//array[i].start --》 当前出队的画面数据(YUV格式)首地址
//array[i].somelen --》当前出队的画面数据大小
//YUV格式无法直接在lcd上显示,原因是lcd只能显示ARGB数据
/*
第一行数据: argbbuf[0]--argbbuf[W-1]
lcdmem
第二行数据: argbbuf[W]--argbbuf[2*W-1]
lcdmem+下一行
*/
allyuvtoargb(array[i].start,argbbuf);
//把argbbuf中的数据填充到lcd对应的位置
for(j=0; j<H; j++)
memcpy(lcdmem+800*j,&argbbuf[W*j],W*4);
//入队
ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
}
}
//收尾
ret=ioctl(camerafd,VIDIOC_STREAMOFF,&mytype);
if(ret==-1)
{
perror("关闭摄像头捕捉失败!\n");
return -1;
}
munmap(lcdmem,800*480*4);
for(i=0; i<4; i++)
munmap(array[i].start,array[i].somelen);
close(lcdfd);
close(camerafd);
return 0;
}
按比例缩小.c
#include <stdio.h>
#include <linux/videodev2.h> //V4L2对应的头文件
#include <stropts.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#define W 320
#define H 240
//自定义结构体,用来存放每个缓冲块的首地址和大小
struct bufmsg
{
void *start; //缓冲块的首地址
int somelen; //缓冲块的大小
};
//封装函数--》把一组YUV--》转换ARGB数据
int yuvtoargb(int y,int u,int v)
{
int r,g,b;
int pix;
r = 1164*(y-16)/1000 + 1596*(v-128)/1000;
g = 1164*(y-16)/1000 + 813*(v-128)/1000 - 391*(u-128)/1000;
b = 1164*(y-16)/1000 + 2018*(u-128)/1000;
//修正计算结果 0---255之间
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;
//把rgb拼接得到ARGB
pix=0x00<<24|r<<16|g<<8|b;
return pix;
}
//封装函数--》把一帧画面数据中所有的YUYV数据转换成ARGB数据
//参数:yuv --》指向一帧yuyv画面首地址
// argbbuf --》存放转换得到的完整的ARGB数据
//allyuvtoargb(array[i].start,argbbuf)
/*
yuv[0]--第一个Y
yuv[1]-- U
yuv[2]--第二个Y
yuv[3]-- V
*/
int allyuvtoargb(char *yuv,int *argbbuf)
{
int i,j;
for(i=0,j=0; i<W*H; i+=2,j+=4)
{
//一组YUYV转换得到两组ARGB数据
argbbuf[i]=yuvtoargb(yuv[j],yuv[j+1],yuv[j+3]);
argbbuf[i+1]=yuvtoargb(yuv[j+2],yuv[j+1],yuv[j+3]);
}
return 0;
}
int main()
{
int camerafd;
int ret;
int i,j;
int lcdfd;
int *lcdmem;
//打开lcd
lcdfd=open("/dev/fb0",O_RDWR);
if(lcdfd==-1)
{
perror("打开lcd失败!\n");
return -1;
}
//映射得到lcd的首地址
lcdmem=mmap(NULL,800*480*4,PROT_READ|PROT_WRITE,MAP_SHARED,lcdfd,0);
if(lcdmem==NULL)
{
perror("映射lcd失败了!\n");
return -1;
}
//打开摄像头的驱动
camerafd=open("/dev/video7",O_RDWR);
if(camerafd==-1)
{
perror("打开摄像头失败!\n");
return -1;
}
//设置摄像头采集格式
struct v4l2_format myfmt;
bzero(&myfmt,sizeof(myfmt));
myfmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
myfmt.fmt.pix.width=W;
myfmt.fmt.pix.height=H;
myfmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;
ret=ioctl(camerafd,VIDIOC_S_FMT,&myfmt);
if(ret==-1)
{
perror("设置采集格式失败!\n");
return -1;
}
//申请4个缓冲块
struct v4l2_requestbuffers mybuf;
bzero(&mybuf,sizeof(mybuf));
mybuf.count=4; //4个缓冲块
mybuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
mybuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_REQBUFS,&mybuf);
if(ret==-1)
{
perror("申请缓冲块失败!\n");
return -1;
}
//分配缓冲块顺便映射得到4个缓冲块的首地址
struct v4l2_buffer otherbuf;
//定义结构体数组存放4个缓冲块的首地址和大小
struct bufmsg array[4];
for(i=0; i<4; i++)
{
bzero(&otherbuf,sizeof(otherbuf));
otherbuf.index=i; //索引号
otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
otherbuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_QUERYBUF,&otherbuf);
if(ret==-1)
{
perror("分配缓冲块失败!\n");
return -1;
}
//顺便映射得到4个缓冲块的首地址
array[i].somelen=otherbuf.length;
array[i].start=mmap(NULL,otherbuf.length,PROT_READ|PROT_WRITE,MAP_SHARED,camerafd,otherbuf.m.offset);
if(array[i].start==NULL)
{
perror("映射缓冲块失败了!\n");
return -1;
}
//顺便发送入队申请(一旦摄像头启动了,立马把画面入队)
ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
//启动摄像头捕捉
enum v4l2_buf_type mytype;
mytype=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret=ioctl(camerafd,VIDIOC_STREAMON,&mytype);
if(ret==-1)
{
perror("启动摄像头捕捉失败!\n");
return -1;
}
//定义一个数组存放转换得到的一帧完整的ARGB数据
int argbbuf[W*H];
//定义集合存放要监测的文件描述符
fd_set myset;
FD_ZERO(&myset);
FD_SET(camerafd,&myset);
//让摄像头画面循环出队,入队形成视频流在lcd上显示出来
while(1)
{
for(i=0; i<4; i++)
{
//监测摄像头队列中是否有数据可读
ret=select(camerafd+1,&myset,NULL,NULL,NULL);
if(ret>0) //说明摄像头的缓冲区中有画面入队了
{
//出队
bzero(&otherbuf,sizeof(otherbuf));
otherbuf.index=i; //索引号
otherbuf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
otherbuf.memory=V4L2_MEMORY_MMAP;
ret=ioctl(camerafd,VIDIOC_DQBUF,&otherbuf);
if(ret==-1)
{
perror("出队失败!\n");
return -1;
}
//出队画面(在结构体数组里面存放着)在lcd上显示出来
//array[i].start --》 当前出队的画面数据(YUV格式)首地址
//array[i].somelen --》当前出队的画面数据大小
//YUV格式无法直接在lcd上显示,原因是lcd只能显示ARGB数据
/*
第一行数据: argbbuf[0]--argbbuf[W-1]
lcdmem
第二行数据: argbbuf[W]--argbbuf[2*W-1]
lcdmem+下一行
*/
allyuvtoargb(array[i].start,argbbuf);
//把argbbuf中的数据填充到lcd对应的位置
for(j=0; j<H; j++)
memcpy(lcdmem+80+800*j,&argbbuf[W*j],W*4);
//入队
ret=ioctl(camerafd,VIDIOC_QBUF,&otherbuf);
if(ret==-1)
{
perror("入队失败!\n");
return -1;
}
}
}
}
//收尾
ret=ioctl(camerafd,VIDIOC_STREAMOFF,&mytype);
if(ret==-1)
{
perror("关闭摄像头捕捉失败!\n");
return -1;
}
munmap(lcdmem,800*480*4);
for(i=0; i<4; i++)
munmap(array[i].start,array[i].somelen);
close(lcdfd);
close(camerafd);
return 0;
}
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。