V4L2通過攝像頭採集圖片

V4L2通過攝像頭採集圖片
#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


typedef struct VideoBuffer {
    void   *start; //視頻緩衝區的起始地址
    size_t  length;//緩衝區的長度
} VideoBuffer;


/*
void *calloc(unsigned n,unsigned size) 功 能: 在內存的動態存儲區中分配n個長度爲size的連續空間,函數返回一個指向分配起始地址的指針;如果分配不成功,返回NULL。跟malloc的區別:calloc在動態分配完內存後,自動初始化該內存空間爲零,而malloc不初始化,裏邊數據是隨機的垃圾數據
*/
//位圖文件頭數據結構含有位圖文件的類型,大小和打印格式等信息
//進行數據字節的對齊
#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;




VideoBuffer framebuf[BUFFER_COUNT];   //修改了錯誤,2012-5.21
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;
}


int open_device()
{
/*
在linux下設備都是以文件的形式進行管理的
ioctl是設備驅動程序中對設備的I/O通道進行管理的函數int ioctl(int fd,int cmd,...)?
成功返回0,出錯返回-1
其中fd--就是用戶程序打開設備使用open函數返回的文件標識符
    cmd--就是用戶程序對設備的控制命令,至於後面都省略號,有或沒有和cmd的意義相關
*/
	int fd;
    fd = open(CAMERA_DEVICE, O_RDWR, 0);//
    if (fd < 0) {
        printf("Open %s failed\n", CAMERA_DEVICE);
        return -1;
    }
	return fd;
}


void get_capability()
{// 獲取驅動信息
/*
控制命令VIDIOC_QUERYCAP
功能:查詢設備驅動的功能;
參數說明:參數類型爲V4L2的能力描述類型struct v4l2_capability;
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];
};
返回值說明: 執行成功時,函數返回值爲 0;
函數執行成功後,struct v4l2_capability 結構體變量中的返回當前視頻設備所支持的功能
例如支持視頻捕獲功能V4L2_CAP_VIDEO_CAPTURE或V4L2_CAP_STREAMING
*/
    int ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);
	if (ret < 0) {
        printf("VIDIOC_QUERYCAP failed (%d)\n", ret);
        return;
    }
    // Print capability infomations
    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);
	return;
}


void get_format()
{
/*獲取當前視頻設備支持的視頻格式
控制命令 VIDIOC_ENUM_FMT
功能: 獲取當前視頻設備支持的視頻格式 。
參數說明:參數類型爲V4L2的視頻格式描述符類型 struct v4l2_fmtdesc
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];
};
返回值說明: 執行成功時,函數返回值爲 0;
*/
	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);
    
	return;
}


int set_format()
{
/*
控制命令VIDIOC_S_FMT
功能:設置視頻設備的視頻數據格式,例如設置視頻圖像數據的長、寬,圖像格式JPEG、YUYV格式);
參數說明:參數類型爲V4L2的視頻數據格式類型struct v4l2_format;
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;
};
返回值說明: 執行成功時,函數返回值爲 0;




注意:如果該視頻設備驅動不支持你所設定的圖像格式,視頻驅動會重新修改struct v4l2_format結構體變量的值爲該視頻設備所支持的圖像格式,所以在程序設計中,設定完所有的視頻格式後,要獲取實際的視頻格式,要重新讀取 struct v4l2_format結構體變量。
使用VIDIOC_G_FMT設置視頻設備的視頻數據格式,VIDIOC_TRY_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;
    int ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
	if (ret < 0) {
        printf("VIDIOC_S_FMT failed (%d)\n", ret);
        return;
    }


  /*  // 設置視頻格式VIDIOC_G_FMT和VIDIOC_S_FMT相同
    ret = ioctl(fd, VIDIOC_G_FMT, &fmt);
    if (ret < 0) {
        printf("VIDIOC_G_FMT failed (%d)\n", ret);
        return ret;
    }*/
    // Print Stream Format
	printf("------------VIDIOC_S_FMT---------------\n");
    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);


    char fmtstr[8];
    memset(fmtstr, 0, 8);
/*
void *memcpy(void *dest, const void *src, size_t n);
從源src所指的內存地址的起始位置開始拷貝n個字節到目標dest所指的內存地址的起始位置中
所需頭文件include <string.h>
*/
    memcpy(fmtstr, &fmt.fmt.pix.pixelformat, 4);
    printf(" pixelformat: %s\n", fmtstr);
    printf(" field: %d\n", fmt.fmt.pix.field);
    printf(" bytesperline: %d\n", fmt.fmt.pix.bytesperline);
    printf(" sizeimage: %d\n", fmt.fmt.pix.sizeimage);
    printf(" colorspace: %d\n", fmt.fmt.pix.colorspace);
    printf(" priv: %d\n", fmt.fmt.pix.priv);
    printf(" raw_date: %s\n", fmt.fmt.raw_data);
	return;
}


void request_buf()
{
/*
控制命令VIDIOC_REQBUFS
功能: 請求V4L2驅動分配視頻緩衝區(申請V4L2視頻驅動分配內存),V4L2是視頻設備的驅動層,位於內核空間,所以通過VIDIOC_REQBUFS控制命令字申請的內存位於內核空間,應用程序不能直接訪問,需要通過調用mmap內存映射函數把內核空間內存映射到用戶空間後,應用程序通過訪問用戶空間地址來訪問內核空間。
參數說明:參數類型爲V4L2的申請緩衝區數據結構體類型struct v4l2_requestbuffers;
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];
};
返回值說明: 執行成功時,函數返回值爲 0,V4L2驅動層分配好了視頻緩衝區;


注意:VIDIOC_REQBUFS會修改tV4L2_reqbuf的count值,tV4L2_reqbuf的count值返回實際申請成功的視頻緩衝區數目;
*/
    reqbuf.count = BUFFER_COUNT;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    
    int ret = ioctl(fd , VIDIOC_REQBUFS, &reqbuf);
    if(ret < 0) {
        printf("VIDIOC_REQBUFS failed (%d)\n", ret);
        return;
    }
	printf("the buffer has been assigned successfully!\n");
	return;
}


void query_map_qbuf()
{
/*
控制命令VIDIOC_QUERYBUF
功能:查詢已經分配的V4L2的視頻緩衝區的相關信息,包括視頻緩衝區的使用狀態、在內核空間的偏移地址、緩衝區長度等。在應用程序設計中通過調VIDIOC_QUERYBUF來獲取內核空間的視頻緩衝區信息,然後調用函數mmap把內核空間地址映射到用戶空間,這樣應用程序才能夠訪問位於內核空間的視頻緩衝區
參數說明:參數類型爲V4L2緩衝區數據結構類型struct v4l2_buffer;
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;
};
返回值說明: 執行成功時,函數返回值爲 0;
struct v4l2_buffer結構體變量中保存了指令的緩衝區的相關信息;一般情況下,應用程序中調用VIDIOC_QUERYBUF取得了內核緩衝區信息後,緊接着調用mmap函數把內核空間地址映射到用戶空間方便用戶空間應用程序的訪問
*/
	int i,ret;
    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);//buf取得內存緩衝區的信息
        if(ret < 0) {
            printf("VIDIOC_QUERYBUF (%d) failed (%d)\n", i, ret);
            return;
        }


        // mmap buffer
        framebuf[i].length = buf.length;//framebuf是程序最前面定義的一個結構體類型的數據
/*
#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
*/
        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;
        }
    
        // Queen buffer
/*
控制命令VIDIOC_QBUF
功能:投放一個空的視頻緩衝區到視頻緩衝區輸入隊列中
參數說明:參數類型爲V4L2緩衝區數據結構類型struct v4l2_buffer;
返回值說明: 執行成功時,函數返回值爲 0;
函數執行成功後,指令(指定)的視頻緩衝區進入視頻輸入隊列,在啓動視頻設備拍攝圖像時,相應的視頻數據被保存到視頻輸入隊列相應的視頻緩衝區中
*/
        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);
    }//空的視頻緩衝區都已經在視頻緩衝的輸入隊列中了
	return;
}




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;
}


void move_noise()
{//雙濾波器
	int i,j,k,temp[3],temp1[3];
	unsigned char BGR[13*3];
	unsigned int sq,sq1,loc,loc1;
	int h=VIDEO_HEIGHT,w=VIDEO_WIDTH;
	for(i=2;i<h-2;i++)
	{
		for(j=2;j<w-2;j++)
		{
			memcpy(BGR,newBuf+(i-1)*w*3+3*(j-1),9);
			memcpy(BGR+9,newBuf+i*w*3+3*(j-1),9);
			memcpy(BGR+18,newBuf+(i+1)*w*3+3*(j-1),9);
			memcpy(BGR+27,newBuf+(i-2)*w*3+3*j,3);
			memcpy(BGR+30,newBuf+(i+2)*w*3+3*j,3);
			memcpy(BGR+33,newBuf+i*w*3+3*(j-2),3);
			memcpy(BGR+36,newBuf+i*w*3+3*(j+2),3);


			memset(temp,0,4*3);
			
			for(k=0;k<9;k++)
			{
				temp[0]+=BGR[k*3];
				temp[1]+=BGR[k*3+1];
				temp[2]+=BGR[k*3+2];
			}
			temp1[0]=temp[0];
			temp1[1]=temp[1];
			temp1[2]=temp[2];
			for(k=9;k<13;k++)
			{
				temp1[0]+=BGR[k*3];
				temp1[1]+=BGR[k*3+1];
				temp1[2]+=BGR[k*3+2];
			}
			for(k=0;k<3;k++)
			{
				temp[k]/=9;
				temp1[k]/=13;
			}
			sq=0xffffffff;loc=0;
			sq1=0xffffffff;loc1=0;
			unsigned int a;			
			for(k=0;k<9;k++)
			{
				a=abs(temp[0]-BGR[k*3])+abs(temp[1]-BGR[k*3+1])+abs(temp[2]-BGR[k*3+2]);
				if(a<sq)
				{
					sq=a;
					loc=k;
				}
			}
			for(k=0;k<13;k++)
			{
				a=abs(temp1[0]-BGR[k*3])+abs(temp1[1]-BGR[k*3+1])+abs(temp1[2]-BGR[k*3+2]);
				if(a<sq1)
				{
					sq1=a;
					loc1=k;
				}
			}
			
			newBuf[i*w*3+3*j]=(unsigned char)((BGR[3*loc]+BGR[3*loc1])/2);
			newBuf[i*w*3+3*j+1]=(unsigned char)((BGR[3*loc+1]+BGR[3*loc1+1])/2);
			newBuf[i*w*3+3*j+2]=(unsigned char)((BGR[3*loc+2]+BGR[3*loc1+2])/2);
			/*還是有些許的噪點
			temp[0]=(BGR[3*loc]+BGR[3*loc1])/2;
			temp[1]=(BGR[3*loc+1]+BGR[3*loc1+1])/2;
			temp[2]=(BGR[3*loc+2]+BGR[3*loc1+2])/2;
			sq=abs(temp[0]-BGR[loc*3])+abs(temp[1]-BGR[loc*3+1])+abs(temp[2]-BGR[loc*3+2]);
			sq1=abs(temp[0]-BGR[loc1*3])+abs(temp[1]-BGR[loc1*3+1])+abs(temp[2]-BGR[loc1*3+2]);
			if(sq1<sq) loc=loc1;
			newBuf[i*w*3+3*j]=BGR[3*loc];
			newBuf[i*w*3+3*j+1]=BGR[3*loc+1];
			newBuf[i*w*3+3*j+2]=BGR[3*loc+2];*/
		}
	}
	return;
}


void yuyv2rgb1()
{
    unsigned char YUYV[3],RGB[3];
	memset(YUYV,0,3);
    int j,k,i;   
	unsigned int location=0;
	j=0;
    for(i=0;i < framebuf[buf.index].length;i+=2)
    {
		YUYV[0]=starter[i];//Y0
		if(i%4==0)
			YUYV[1]=starter[i+1];//U
		//YUYV[2]=starter[i+2];//Y1
		if(i%4==2)
			YUYV[2]=starter[i+1];//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[2]-128);//g
			RGB[2]=YUYV[0]+1.402*(YUYV[2]-128);//r
		}


		for(k=0;k<3;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+=3;		
    }
	return;
}


void store_yuyv()
{
    FILE *fp = fopen(CAPTURE_FILE, "wb");
    if (fp < 0) {
        printf("open frame data file failed\n");
        return;
    }
    fwrite(framebuf[buf.index].start, 1, buf.length, fp);
    fclose(fp);
    printf("Capture one frame saved in %s\n", CAPTURE_FILE);
    return;
}




void store_bmp(int n_len)
{
	FILE *fp1 = fopen(CAPTURE_RGB_FILE, "wb");
    if (fp1 < 0) {
        printf("open frame data file failed\n");
        return;
    }
	fwrite(&bfh,sizeof(bfh),1,fp1);
	fwrite(&bih,sizeof(bih),1,fp1);
	fwrite(newBuf, 1, n_len, fp1);
    fclose(fp1);
    printf("Change one frame saved in %s\n", CAPTURE_RGB_FILE);
	return;
}


int main()
{
    int i, ret;


    // 打開設備
    fd=open_device();
    
    // 獲取驅動信息
    //struct v4l2_capability cap;
    get_capability();
 	
	//獲取當前視頻設備支持的視頻格式
    //struct v4l2_fmtdesc fmtdesc;
    memset(&fmtdesc,0,sizeof(fmtdesc));
	get_format();
	
    // 設置視頻格式
    //struct v4l2_format fmt;
    //memset在一段內存塊中填充某個給定的值,它是對較大的結構體或數組進行清零操作的一種最快的方法
    memset(&fmt, 0, sizeof(fmt));//將fmt中的前sizeof(fmt)字節用0替換並返回fmt
    set_format();
    
    // 請求分配內存
	//struct v4l2_requestbuffers reqbuf;
	request_buf();


    // 獲取空間,並將其映射到用戶空間,然後投放到視頻輸入隊列
	//struct v4l2_buffer buf;
	query_map_qbuf();




    // 開始錄製
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
/*
控制命令VIDIOC_STREAMON
功能:啓動視頻採集命令,應用程序調用VIDIOC_STREAMON啓動視頻採集命令後,視頻設備驅動程序開始採集視頻數據,並把採集到的視頻數據保存到視頻驅動的視頻緩衝區中
參數說明:參數類型爲V4L2的視頻緩衝區類型 enum v4l2_buf_type ;
enum v4l2_buf_type {
        V4L2_BUF_TYPE_VIDEO_CAPTURE        = 1,
        V4L2_BUF_TYPE_VIDEO_OUTPUT         = 2,
        V4L2_BUF_TYPE_VIDEO_OVERLAY        = 3,
        V4L2_BUF_TYPE_VBI_CAPTURE          = 4,
        V4L2_BUF_TYPE_VBI_OUTPUT           = 5,
        V4L2_BUF_TYPE_SLICED_VBI_CAPTURE   = 6,
        V4L2_BUF_TYPE_SLICED_VBI_OUTPUT    = 7,
#if 1
        //// Experimental ////
        V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8,
#endif
        V4L2_BUF_TYPE_PRIVATE              = 0x80,
};
返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,視頻設備驅動程序開始採集視頻數據,此時應用程序一般通過調用select函數來判斷一幀視頻數據是否採集完成,當視頻設備驅動完成一幀視頻數據採集並保存到視頻緩衝區中時,select函數返回,應用程序接着可以讀取視頻數據;否則select函數阻塞直到視頻數據採集完成
*/
    ret = ioctl(fd, VIDIOC_STREAMON, &type);
    if (ret < 0) {
        printf("VIDIOC_STREAMON failed (%d)\n", ret);
        return ret;
    }


    // Get frame
/*
控制命令VIDIOC_DQBUF
功能:從視頻緩衝區的輸出隊列中取得一個已經保存有一幀視頻數據的視頻緩衝區
參數說明:參數類型爲V4L2緩衝區數據結構類型struct v4l2_buffer;
返回值說明: 執行成功時,函數返回值爲 0;函數執行成功後,相應的內核視頻緩衝區中保存有當前拍攝到的視頻數據,應用程序可以通過訪問用戶空間來讀取該視頻數據(前面已經通過調用函數 mmap做了用戶空間和內核空間的內存映射).


說明: VIDIOC_DQBUF命令結果, 使從隊列刪除的緩衝幀信息傳給了此buf
V4L2_buffer結構體的作用就相當於申請的緩衝幀的代理,找緩衝幀的都要先問問它,通過它來聯繫緩衝幀,起了中間橋樑的作用
*/
    ret = ioctl(fd, VIDIOC_DQBUF, &buf);//VIDIOC_DQBUF命令結果, 使從隊列刪除的緩衝幀信息傳給了此buf
    if (ret < 0) {
        printf("VIDIOC_DQBUF failed (%d)\n", ret);
        return ret;
    }


    // Process the frame 此時我們需要進行數據格式的改變
	store_yuyv();
    
    
    //對採集的數據進行轉變,變換成RGB24模式,然後進行存儲
/*
(1)開闢出來一段內存區域來存放轉換後的數據
(2)循環讀取buf內存段的內容,進行轉換,轉換後放入到新開闢的內存區域中
(3)將新開闢出來的內存區的內容讀到文件中
*/
    printf("********************************************\n");
    int n_len;
    n_len=framebuf[buf.index].length*3/2;
    newBuf=calloc((unsigned int)n_len,sizeof(unsigned char));
  
    if(!newBuf)
    {
		printf("cannot assign the memory !\n");
		exit(0);
    }


    printf("the information about the new buffer:\n start Address:0x%x,length=%d\n\n",(unsigned int)newBuf,n_len);


	printf("----------------------------------\n");
	
	//YUYV to RGB
    starter=(unsigned char *)framebuf[buf.index].start;
	yuyv2rgb();//還是這個採集的圖片的效果比較好
	move_noise();
	//yuyv2rgb1();
	//設置bmp文件的頭和bmp文件的一些信息
	create_bmp_header();
	
	store_bmp(n_len);
	
	
    // Re-queen buffer
    ret = ioctl(fd, VIDIOC_QBUF, &buf);
    if (ret < 0) {
        printf("VIDIOC_QBUF failed (%d)\n", ret);
        return ret;
    }
	printf("re-queen buffer end\n");
    // Release the resource
/*
表頭文件 #include<unistd.h>
        #include<sys/mman.h>
        定義函數 int munmap(void *start,size_t length);
        函數說明 munmap()用來取消參數start所指的映射內存起始地址,參數length則是欲取消的內存大小。當進程結束或利用exec相關函數來執行其他程序時,映射內存會自動解除,但關閉對應的文件描述詞時不會解除映射
        返回值 如果解除映射成功則返回0,否則返回-1
*/
    for (i=0; i< 4; i++)
    {
	
        munmap(framebuf[i].start, framebuf[i].length);
    }
	//free(starter);
printf("free starter end\n");
	//free(newBuf);
printf("free newBuf end\n");
    close(fd);


	
    printf("Camera test Done.\n");
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章