Linux下USB摄像头驱动研究

知识点

video4linux

Video4linux2(简称V4L2),是linux中关于视频设备的内核驱动。在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev下。

使用命令ls /dev/video* 就可以显示所有的摄像头。


格式

首先我们必须要弄清楚自己使用的USB摄像头的输出格式

方法一:

使用命令lsusb

Bus 001 Device 007: ID 1871:01f0 AveoTechnology Corp.---上海嘉映微电子有限公司

再输入命令

lsusb -s 001:007 -v

这样可以显示详细的信息。

方法二:

利用ffmpeg的命令,可以打印信息:

[video4linux2,v4l2 @ 0x4cdd0] The driver changed the time per frame from 1/2 to 1/1
Input #0, video4linux2,v4l2, from '/dev/video2':
  Duration: N/A, start: 55.844440, bitrate: 1228 kb/s
    Stream #0:0: Video: rawvideo (YUY2 / 0x32595559), yuyv422, 320x240, 1228 kb/s, 1 fps, 1 tbr, 1000k tbn, 1000k tbc
Output #0, mp4, to '/home/f.mp4':
  Metadata:
    encoder         : Lavf55.12.100
    Stream #0:0: Video: mpeg4 ( [0][0][0] / 0x0020), yuv420p, 320x240, q=2-31, 200 kb/s, 16384 tbn, 2 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (rawvideo -> mpeg4)

可以看出,摄像头输出的是yuyv422。


标识符和结构体

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。

struct v4l2_requestbuffers reqbufs;//向驱动申请帧缓冲的请求,里面包含申请的个数
struct v4l2_capability cap;        //这个设备的功能,比如是否是视频输入设备
struct v4l2_input input;           //视频输入
struct v4l2_standard std;          //视频的制式,比如PAL,NTSC
struct v4l2_format fmt;            //帧的格式,比如宽度,高度等
struct v4l2_buffer buf;            //代表驱动中的一帧
v4l2_std_id stdid;                 //视频制式,例如:V4L2_STD_PAL_B
struct v4l2_queryctrl query;       //查询的控制
struct v4l2_control control;       //具体控制的值

操作摄像头流程

1. 打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);
2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input
4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
6. 将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
7. 将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
8. 开始视频的采集。VIDIOC_STREAMON
9. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
10. 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
11. 停止视频的采集。VIDIOC_STREAMOFF
12. 关闭视频设备。close(fd);


程序剖析

常用函数介绍

首先讲一下常用的一些功能函数:

void *calloc(unsigned n,unsigned size) 

功 能: 在内存的动态存储区中分配n个长度为size的连续空间,函数返回一个指向分配起始地址的指针;如果分配不成功,返回NULL。跟malloc的区别:calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不初始化,里边数据是随机的垃圾数据 。

void *memcpy(void *dest, const void *src, size_t n);
从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中
所需头文件include <string.h>

#include <sys/mman.h>
void *mmap(void *start, size_t length, int prot, int flags,int fd, off_t offset);
int munmap(void *start, size_t length);

mmap将一个文件或者其它对象映射进内存。文件被映射到多个页上,如果文件的大小不是所有页的大小之和,最后一个页不被使用的空间将会清零

start:映射区的开始地址,设置为0时表示由系统决定映射区的起始地址
length:映射区的长度
prot:期望的内存保护标志,不能与文件的打开模式冲突。是以下的某个值,可以通过or运算合理地组合在一起	PROT_EXEC //页内容可以被执行
	PROT_READ //页内容可以被读取
	PROT_WRITE //页可以被写入
	PROT_NONE //页不可访问
flags:指定映射对象的类型,映射选项和映射页是否可以共享
	MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新
fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射
offset:被映射对象内容的起点

成功执行时,mmap()返回被映射区的指针,munmap()返回0。失败时,mmap()返回MAP_FAILED[其值为(void *)-1],munmap返回-1




在程序中,我们使用摄像头,当然就要先打开摄像头

int fd = open("/dev/video0",O_RDWR,0);


然后就可以获取摄像头的信息,比如支持那些格式等

int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);  

获取到的信息都保存在cap结构体中,这个结构体就是

struct v4l2_capability {  
        __u8    driver[16];     //i.e. "bttv"            //驱动名称,  
        __u8    card[32];       // i.e. "Hauppauge WinTV"         //  
        __u8    bus_info[32];   // "PCI:" + pci_name(pci_dev)     //PCI总线信息  
        __u32   version;        // should use KERNEL_VERSION()   
        __u32   capabilities;   // Device capabilities         //设备能力  
        __u32   reserved[4];  
}; 

其中,最重要的就是:视频捕获功能V4L2_CAP_VIDEO_CAPTURE或V4L2_CAP_STREAMING。


接下来就需要知道视频设备支持的视频格式,视频格式保存在一个结构体v4l2_fmtdesc 中,可以保存多中格式,

int ret;  
fmtdesc.index=0;  
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
 ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);  
 while (ret != 0)  
  {  
     fmtdesc.index++;  
      ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);  
  }  
  printf("--------VIDIOC_ENUM_FMT---------\n");  
  printf("get the format what the device support\n{ pixelformat = ''%c%c%c%c'', description = ''%s'' }\n",fmtdesc.pixelformat & 0xFF, (fmtdesc.pixelformat >> 8) & 0xFF, (fmtdesc.pixelformat >> 16) & 0xFF,(fmtdesc.pixelformat >> 24) & 0xFF, fmtdesc.description);  

其中这个结构体的定义为:

struct v4l2_fmtdesc {  
        __u32               index;             // Format number        
        enum v4l2_buf_type  type;              // buffer type          
        __u32               flags;  
        __u8                description[32];   // Description string   
        __u32               pixelformat;       // Format fourcc        
        __u32               reserved[4];  
};

其中最重要的就是视频的格式,例如YUV4:2:2


这里,可以先编写一个简单的例子

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>


#define CAMERA_DEVICE "/dev/video0"


#define CAPTURE_FILE "frame_yuyv_new.jpg"
#define CAPTURE_RGB_FILE "frame_rgb_new.bmp"
#define CAPTURE_show_FILE "a.bmp"


#define VIDEO_WIDTH 640
#define VIDEO_HEIGHT 480
#define VIDEO_FORMAT V4L2_PIX_FMT_YUYV
#define BUFFER_COUNT 4

int fd;
struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt;
struct v4l2_requestbuffers reqbuf;
struct v4l2_buffer buf;
int main()
{
	int i,ret;
	fd = open(CAMERA_DEVICE, O_RDWR, 0);
	if (fd < 0) {
		printf("Open %s failed\n",CAMERA_DEVICE);
	}
	ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
	if (ret < 0) {
		printf("VIDIOC_QUERYCAP failed (%d)\n", ret);
	}

	printf("------------VIDIOC_QUERYCAP-----------\n");
	printf("Capability Informations:\n");
	printf(" driver: %s\n", cap.driver);
	printf(" card: %s\n", cap.card);
	printf(" bus_info: %s\n", cap.bus_info);
	printf(" version: %08X\n", cap.version);
	printf(" capabilities: %08X\n\n", cap.capabilities);

	memset(&fmtdesc,0,sizeof(fmtdesc));
	fmtdesc.index=0;
	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
	while (ret != 0)
	{
		fmtdesc.index++;
		ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
	}
	printf("--------VIDIOC_ENUM_FMT---------\n");
	printf("get the format what the device support\n");
	printf("pixelformat = %c%c%c%c, description = %s \n",fmtdesc.pixelformat & 0xFF, (fmtdesc.pixelformat >> 8) & 0xFF, (fmtdesc.pixelformat >> 16) & 0xFF,(fmtdesc.pixelformat >> 24) & 0xFF, fmtdesc.description);


}


运行结果如下:












接下来就是设定视频的格式,设置的时候并不一定会成功,因此需要重新读取视频格式

VIDIOC_S_FMT 设置

VIDIOC_G_FMT 读取

VIDIOC_TRY_FMT 验证

先定义一个格式结构体

struct v4l2_format {
        enum v4l2_buf_type type;    //数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
        union {
                struct v4l2_pix_format          pix;     // V4L2_BUF_TYPE_VIDEO_CAPTURE 
                struct v4l2_window              win;     // V4L2_BUF_TYPE_VIDEO_OVERLAY 
                struct v4l2_vbi_format          vbi;     // V4L2_BUF_TYPE_VBI_CAPTURE 
                struct v4l2_sliced_vbi_format   sliced;  // V4L2_BUF_TYPE_SLICED_VBI_CAPTURE 
                __u8    raw_data[200];                   // user-defined 
        } fmt;
};
struct v4l2_pix_format {
        __u32                   width;         // 宽,必须是16的倍数
        __u32                   height;        // 高,必须是16的倍数
        __u32                   pixelformat;   // 视频数据存储类型,例如是YUV4:2:2还是RGB
        enum v4l2_field       field;
        __u32                   bytesperline;
        __u32                   sizeimage;
        enum v4l2_colorspace colorspace;
        __u32                   priv;
};
具体程序:

	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width=VIDEO_WIDTH;
	fmt.fmt.pix.height=VIDEO_HEIGHT;
	fmt.fmt.pix.pixelformat=fmtdesc.pixelformat;//V4L2_PIX_FMT_YUYV;
	fmt.fmt.pix.field=V4L2_FIELD_INTERLACED;
	ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
	if (ret < 0) {
		printf("VIDIOC_S_FMT failed (%d)\n", ret);
		return;
	}
	ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
	if (ret < 0) {
		printf("VIDIOC_G_FMT failed (%d)\n", ret);
		return;
	}
	printf("Stream Format Informations:\n");
	printf(" type: %d\n", fmt.type);
	printf(" width: %d\n", fmt.fmt.pix.width);
	printf(" height: %d\n", fmt.fmt.pix.height);

假如一致,那么说明设置成功。



接下来就是分配内存,获取空间后,将其映射到用户空间,然后投放到视频输入队列中。因为程序无法直接访问内核空间。

申请缓冲区的一个重要的结构体就是:

struct v4l2_requestbuffers {
        u32    count;                                   //缓存数量,也就是说在缓存队列里保持多少张照片
        enum v4l2_buf_type    type;         //数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
        enum v4l2_memory      memory;  //V4L2_MEMORY_MMAP或V4L2_MEMORY_USERPTR
        u32                   reserved[2];
};

reqbuf.count = BUFFER_COUNT;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);

接下来程序就使用查询命令VIDIOC_QUERYBUF来查询缓冲区的状态,保存在结构体中,结构体是

struct v4l2_buffer {
        __u32                   index;
        enum v4l2_buf_type      type;
        __u32                   bytesused;
        __u32                   flags;
        enum v4l2_field         field;
        struct timeval          timestamp;
        struct v4l2_timecode    timecode;
        __u32                   sequence;
        // memory location 
        enum v4l2_memory        memory;
        union {
                __u32           offset;
                unsigned long   userptr;
        } m;
        __u32                   length;
        __u32                   input;
        __u32                   reserved;
};
一般情况下,应用程序中调用VIDIOC_QUERYBUF取得了内核缓冲区信息后,紧接着调用mmap函数把内核空间地址映射到用户空间方便用户空间应用程序的访问

	reqbuf.count = BUFFER_COUNT;
	reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuf.memory = V4L2_MEMORY_MMAP;
	ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);
	if(ret < 0) {
	printf("VIDIOC_REQBUFS failed (%d)\n", ret);

	}
	printf("the buffer has been assigned successfully!\n");

	i=0;
	for(i=0;i<reqbuf.count;i++)
	{
		buf.index =i;
		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		ret = ioctl(fd , VIDIOC_QUERYBUF, &buf);
		framebuf[i].length = buf.length;
		framebuf[i].start = (char *) mmap(0, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
		if (framebuf[i].start == MAP_FAILED) {
			printf("mmap (%d) failed: %s\n", i, strerror(errno));
			return;
		}
	
		ret = ioctl(fd , VIDIOC_QBUF, &buf);
		if (ret < 0) {
		printf("VIDIOC_QBUF (%d) failed (%d)\n", i, ret);
		return;
		}
		printf("Frame buffer %d: address=0x%x, length=%d\n", i, (unsigned int)framebuf[i].start, framebuf[i].length);
	}

接下来就是启动视频采集命令



完整截图程序如下

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <asm/types.h>
#include <linux/videodev2.h>


#define CAMERA_DEVICE "/dev/video0"


#define CAPTURE_FILE "frame_yuyv_new.yuyv"
#define CAPTURE_RGB_FILE "frame_rgb_new.bmp"
#define CAPTURE_show_FILE "a.bmp"


#define VIDEO_WIDTH 640
#define VIDEO_HEIGHT 480
#define VIDEO_FORMAT V4L2_PIX_FMT_YUYV
#define BUFFER_COUNT 4


#pragma pack(1)
typedef struct BITMAPFILEHEADER
{
unsigned short bfType;//位图文件的类型,
unsigned long bfSize;//位图文件的大小,以字节为单位
unsigned short bfReserved1;//位图文件保留字,必须为0
unsigned short bfReserved2;//同上
unsigned long bfOffBits;//位图阵列的起始位置,以相对于位图文件   或者说是头的偏移量表示,以字节为单位
} BITMAPFILEHEADER;
#pragma pack()
typedef struct BITMAPINFOHEADER//位图信息头类型的数据结构,用于说明位图的尺寸
{
unsigned long biSize;//位图信息头的长度,以字节为单位
unsigned long biWidth;//位图的宽度,以像素为单位
unsigned long biHeight;//位图的高度,以像素为单位
unsigned short biPlanes;//目标设备的级别,必须为1
unsigned short biBitCount;//每个像素所需的位数,必须是1(单色),4(16色),8(256色)或24(2^24色)之一
unsigned long biCompression;//位图的压缩类型,必须是0-不压缩,1-BI_RLE8压缩类型或2-BI_RLE4压缩类型之一
unsigned long biSizeImage;//位图大小,以字节为单位
unsigned long biXPelsPerMeter;//位图目标设备水平分辨率,以每米像素数为单位
unsigned long biYPelsPerMeter;//位图目标设备垂直分辨率,以每米像素数为单位
unsigned long biClrUsed;//位图实际使用的颜色表中的颜色变址数
unsigned long biClrImportant;//位图显示过程中被认为重要颜色的变址数
} BITMAPINFOHEADER;






typedef struct VideoBuffer {
void *start; //视频缓冲区的起始地址
size_t length;//缓冲区的长度
}VideoBuffer;
VideoBuffer framebuf[BUFFER_COUNT];
int fd;
struct v4l2_capability cap;
struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt;
struct v4l2_requestbuffers reqbuf;
struct v4l2_buffer buf;

unsigned char *starter;
unsigned char *newBuf;
struct BITMAPFILEHEADER bfh;
struct BITMAPINFOHEADER bih;

void create_bmp_header()
{
bfh.bfType = (unsigned short)0x4D42;
bfh.bfSize = (unsigned long)(14 + 40 + VIDEO_WIDTH * VIDEO_HEIGHT*3);
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits= (unsigned long)(14 + 40);


bih.biBitCount = 24;
bih.biWidth = VIDEO_WIDTH;
bih.biHeight = VIDEO_HEIGHT;
bih.biSizeImage = VIDEO_WIDTH * VIDEO_HEIGHT * 3;
bih.biClrImportant = 0;
bih.biClrUsed = 0;
bih.biCompression = 0;
bih.biPlanes = 1;
bih.biSize = 40;//sizeof(bih);
bih.biXPelsPerMeter = 0x00000ec4;
bih.biYPelsPerMeter=0x00000ec4;
}


void yuyv2rgb()
{
unsigned char YUYV[4],RGB[6];
int j,k,i;
	unsigned int location=0;
	j=0;
for(i=0;i < framebuf[buf.index].length;i+=4)
{
		YUYV[0]=starter[i];//Y0
		YUYV[1]=starter[i+1];//U
		YUYV[2]=starter[i+2];//Y1
		YUYV[3]=starter[i+3];//V
		if(YUYV[0]<1)
		{
			RGB[0]=0;
			RGB[1]=0;
			RGB[2]=0;
		}
		else
		{
			RGB[0]=YUYV[0]+1.772*(YUYV[1]-128);//b
			RGB[1]=YUYV[0]-0.34413*(YUYV[1]-128)-0.71414*(YUYV[3]-128);//g
			RGB[2]=YUYV[0]+1.402*(YUYV[3]-128);//r
		}
		if(YUYV[2]<0)
		{
			RGB[3]=0;
			RGB[4]=0;
			RGB[5]=0;
		}
		else
		{
			RGB[3]=YUYV[2]+1.772*(YUYV[1]-128);//b
			RGB[4]=YUYV[2]-0.34413*(YUYV[1]-128)-0.71414*(YUYV[3]-128);//g
			RGB[5]=YUYV[2]+1.402*(YUYV[3]-128);//r


		}


		for(k=0;k<6;k++)
		{
			if(RGB[k]<0)
				RGB[k]=0;
			if(RGB[k]>255)
				RGB[k]=255;
		}


		//请记住:扫描行在位图文件中是反向存储的!
		if(j%(VIDEO_WIDTH*3)==0)//定位存储位置
		{
			location=(VIDEO_HEIGHT-j/(VIDEO_WIDTH*3))*(VIDEO_WIDTH*3);
		}
		bcopy(RGB,newBuf+location+(j%(VIDEO_WIDTH*3)),sizeof(RGB));


		j+=6;		
}
	return;
}





int main()
{
	int i,ret;
	fd = open(CAMERA_DEVICE, O_RDWR, 0);
	ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);

	memset(&fmtdesc,0,sizeof(fmtdesc));
	fmtdesc.index=0;
	fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
	while (ret != 0)
	{
		fmtdesc.index++;
		ret=ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc);
	}
	memset(&fmt,0,sizeof(fmt));
	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	fmt.fmt.pix.width=VIDEO_WIDTH;
	fmt.fmt.pix.height=VIDEO_HEIGHT;
	fmt.fmt.pix.pixelformat=fmtdesc.pixelformat;//V4L2_PIX_FMT_YUYV;
	fmt.fmt.pix.field=V4L2_FIELD_INTERLACED;
	ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
	ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
	reqbuf.count = BUFFER_COUNT;
	reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	reqbuf.memory = V4L2_MEMORY_MMAP;
	ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);

	i=0;
	for(i=0;i<reqbuf.count;i++)
	{
		buf.index =i;
		buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		buf.memory = V4L2_MEMORY_MMAP;
		ret = ioctl(fd , VIDIOC_QUERYBUF, &buf);
		framebuf[i].length = buf.length;
		framebuf[i].start = (char *) mmap(0, buf.length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
		if (framebuf[i].start == MAP_FAILED) {
			printf("mmap (%d) failed: %s\n", i, strerror(errno));
			return;
		}
	
		ret = ioctl(fd , VIDIOC_QBUF, &buf);
		if (ret < 0) {
		printf("VIDIOC_QBUF (%d) failed (%d)\n", i, ret);
		return;
		}
		printf("Frame buffer %d: address=0x%x, length=%d\n", i, (unsigned int)framebuf[i].start, framebuf[i].length);
	}



	enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	ret = ioctl(fd, VIDIOC_STREAMON, &type);
	ret = ioctl(fd, VIDIOC_DQBUF, &buf);

	FILE *fp = fopen(CAPTURE_FILE, "wb");
	fwrite(framebuf[buf.index].start, 1, buf.length, fp);
	fclose(fp);	

int n_len=framebuf[buf.index].length*3/2;
newBuf=calloc((unsigned int)n_len,sizeof(unsigned char));
starter=(unsigned char *)framebuf[buf.index].start;



yuyv2rgb();
create_bmp_header();

FILE *fp1 = fopen(CAPTURE_RGB_FILE, "wb");
fwrite(&bfh,sizeof(bfh),1,fp1);
fwrite(&bih,sizeof(bih),1,fp1);
fwrite(newBuf, 1, n_len, fp1);
fclose(fp1);
ret = ioctl(fd, VIDIOC_QBUF, &buf);
for (i=0; i< 4; i++)
{
munmap(framebuf[i].start, framebuf[i].length);
}
close(fd);
}











未完待续














  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值