圖像採集--V4L2

這是我很久以前寫的一個程序,如今需要使用,由於東西太多,差點就沒有找到。爲了以後方便使用,所以把它粘出來。

我這個程序,使用的是USB camera,採集到的圖像是以YUV形式的存在的,所以在查看的時候需要使用YUV格式查看工具。


//圖像採集

#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 <time.h>

#include <asm/types.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <time.h>


//要訪問的camera設備
#define CAMERA_DEVICE "/dev/video0"

//圖片最大邊界
#define MAXWIDTH 2560
#define MAXHEIGHT 1920

//默認圖片的大小
#define DEFWIDTH 720
#define DEFHEIGHT 576

//採集圖像的大小
#define WIDTH 640
#define HEIGHT 480

//申請緩衝區的個數
#define BUFFER_COUNT 4

//保存圖片的數目
#define NUMBER 10

static int fd;

static   struct   v4l2_capability   cap;

struct v4l2_fmtdesc fmtdesc;
struct v4l2_format fmt;
struct v4l2_streamparm setfps;
struct v4l2_requestbuffers req;

struct v4l2_cropcap cropcap;
struct v4l2_crop crop;


//用戶空間
typedef struct buffer
{
    void * start;
    unsigned int length;
}BUFFERS;
BUFFERS *buffers;

struct v4l2_buffer buf;


enum v4l2_buf_type type;



//初始化 v4l2

int init_v4l2(void)
{
        int i;
        int ret = 0;

        //opendev
        fd = open(CAMERA_DEVICE, O_RDWR|O_NONBLOCK, 0);

        if (fd < 0)
        {
                printf("Error opening V4L2 interface\n");
                return fd;
        }
        else
        {
                printf("Open camera device %d\n ", fd);
        }

        //query cap
        ret = ioctl(fd, VIDIOC_QUERYCAP, &cap);

        if(ret < 0)
        {
                printf("Error: unable to query device.\n");
                return  ret;
        }
        else
        {
                printf("\ndriver:\t\t%s\n",cap.driver);
                printf("card:\t\t%s\n",cap.card);
                printf("bus_info:\t%s\n",cap.bus_info);
                printf("version:\t%d\n",cap.version);
                printf("capabilities:\t%x\n",cap.capabilities);

                if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
                {
                        printf("Camera Device: supports capture.\n");
                }
                else
                {
                        printf("The camera not  support capture.\n");
                }

                if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
                {
                        printf("Camera Device: supports streaming.\n");
                }
                else
                {
                        printf("The camera not support streaming.\n");
                }
                if ((cap.capabilities & V4L2_CAP_READWRITE) == V4L2_CAP_READWRITE)
                {
                        printf("Camera Device: supports read/writ.\n");
                }
                else
                {
                        printf("The camera support not read/write.\n");
                }
        }



        //emu all support fmt
        fmtdesc.index=0;
        fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

        printf("\nSupport format:\n");
        while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
        {
                printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
                fmtdesc.index++;
        }


        //set fmt
        memset(&fmt, 0, sizeof(fmt));

        fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
        fmt.fmt.pix.height = HEIGHT;
        fmt.fmt.pix.width = WIDTH;
        fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

        ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
        if(ret < 0)
        {
                printf("\nUnable to set format\n");
                return ret;
        }


        //check the fmt is set to sucessed
        if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
        {
                printf("Unable to get format\n");
                return ret;
        }
        else
        {
                printf("\nfmt.type:\t\t%d\n",fmt.type);
                printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF,
                                                (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
                                                (fmt.fmt.pix.pixelformat >> 16) & 0xFF,
                                                (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
                printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
                printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
                printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
        }

        //set fps (stream)
        setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        setfps.parm.capture.timeperframe.numerator = 10;
        setfps.parm.capture.timeperframe.denominator = 10;
        ret = ioctl(fd,VIDIOC_S_PARM,&setfps);
        if(ret < 0)
        {
                printf("set streamparm error\n");
                exit(EXIT_FAILURE);
        }


        //檢查驅動的修剪能力
        ret = ioctl(fd, VIDIOC_CROPCAP, &cropcap);
        if(ret < 0)
        {
                printf("set VIDIOC_CROPCAP failed. errno = %s\n", strerror(errno));
        }
        else
        {
                printf("The camera: max_width = %d\n\t\tmax_height = %d\n",
                                cropcap.bounds.width, cropcap.bounds.height);

                //設置縮放
                memset(&crop, 0, sizeof(crop));
                crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                crop.c.width = WIDTH;
                crop.c.height = HEIGHT;

                ret = ioctl(fd, VIDIOC_S_CROP, &crop);
                if(ret < 0)
                {
                        printf("set VIDIOC_S_CROP failed. errno = %s\n", strerror(errno));
                        exit(EXIT_FAILURE);
                }
                printf("VIDIOC_S_CROP sucessful.\n");
        }
        printf("\ninit %s \t[OK]\n\n",CAMERA_DEVICE);

        return 1;
}


//mmap
int mmap_v4l2()
{
        int ret;
        unsigned int n_buffers;

        //request for 4 buffers 
        req.count=BUFFER_COUNT;
        req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory=V4L2_MEMORY_MMAP;

        ret = ioctl(fd,VIDIOC_REQBUFS,&req);
        if(ret < 0)
        {
                printf("request for buffers error\n");
                exit(EXIT_FAILURE);
        }

        //mmap for buffers
        buffers = (BUFFERS*)calloc(req.count, sizeof (BUFFERS));
        if (!buffers)
        {
                printf ("Out of memory\n");
                exit(EXIT_FAILURE);
        }

        for (n_buffers = 0; n_buffers < req.count; n_buffers++)
        {
                memset(&buf, 0, sizeof(buf));
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = n_buffers;

                //query buffers 讀取緩存
                ret = ioctl (fd, VIDIOC_QUERYBUF, &buf);
                if(ret < 0)
                {
                        printf("query buffer error\n");
                        exit(EXIT_FAILURE);
                }

                buffers[n_buffers].length = buf.length;

                //map
                buffers[n_buffers].start = mmap(NULL,buf.length,
                                                PROT_READ |PROT_WRITE, MAP_SHARED,
                                                fd, buf.m.offset);
                if (buffers[n_buffers].start == MAP_FAILED)
                {
                        printf("buffer map error\n");
                        exit(EXIT_FAILURE);
                }

                //Queen buffer  
                if(-1 == ioctl(fd, VIDIOC_QBUF, &buf))
                {
                        printf("%d queue fail\n", n_buffers + 1);
                        exit(EXIT_FAILURE);
                }
        }
        printf("mmap_v4l2\t\t[OK]\n\n");
        return 1;
}



//Need to file number
time_t rawtime[NUMBER];

//把獲得的圖片 寫進文件
int process_file(int m, int n)
{
        int ret;
        char file_name[128];
        time(&rawtime[n]);
        sprintf(file_name, "%ld%d", rawtime[n], n);
        ret = creat(file_name, 0777);
        if( ret < 0 )
        {
                printf("create %d file failed\n", n+1);
                exit(EXIT_FAILURE);
        }
        FILE *fp;
        fp = fopen(file_name, "rb+");
        if( !fp )
        {
                printf("open %d  error\n", n);
                exit(EXIT_FAILURE);
        }

        //fwrite(frame_buffer, sizeof(frame_buffer), 1,fp);
        fwrite(buffers[m].start, buffers[m].length, 1,fp);

        fclose(fp);
        return 1;
}



//圖像採集    image acquisition
int image_acq()
{
        int ret;
        //開始視頻採集 
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

        ret = ioctl (fd, VIDIOC_STREAMON, &type);
        if(ret < 0)
        {
                printf("Fail to VIDIOC_STERAMON\n");
                exit(EXIT_FAILURE);
        }



                int nu, times = 0;

                /*select的最後一個參數,表示該調用在返回前等待多久
                struct timeval time;
                   //Timeout
                time.tv_sec = TimeOut;
                time.tv_usec = 0;
                */

        while(1)
        {
                //select
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(fd, &fds);

                nu = select(fd + 1, &fds, NULL, NULL, NULL);

                if(nu < 0)
                {

                        if(EINTR == errno)
                                continue;
                        printf("Fail to select, errno = %s\n", strerror(errno));
                        exit(EXIT_FAILURE);

                }

                //把數據拿出來
                ret = ioctl(fd, VIDIOC_DQBUF, &buf);
                if(ret < 0)
                {
                        printf("Fail to VIDIOC_DQBUF, errno = %s\n", strerror(errno));
                        exit(EXIT_FAILURE);
                }

//              printf("buffer index=%d, times=%d\n", buf.index, times % 4);

                //Data processing 
                process_file(buf.index, times);

                //Queen buffer again 
                ret = ioctl(fd, VIDIOC_QBUF, &buf);
                if(ret < 0)
                {
                        printf("Fail to VIDIOC_QBUF, errno = %s\n", strerror(errno));
                        exit(EXIT_FAILURE);

                }

//              printf("bytesuesd = %d\n", buf.bytesused);/* 判斷採集的圖>像以什麼方式存儲 */
                printf(" %d VIDIOC_QBUF again\t [OK]\n", times + 1);

                times ++ ;
                if(times >= 10)
                        break;

                //sleep(1);
        }

        printf("image_acq \t\t[OK]\n\n");
        return 1;
}


//停止採集 及 關閉設備

void close_v4l2( void )
{

        //stop_capturing
        int ret;

        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret == ioctl(fd,VIDIOC_STREAMOFF,&type);
        if(ret < 0)
        {
                perror("Fail to ioctl 'VIDIOC_STREAMOFF'");
                exit(EXIT_FAILURE);
        }

        //release mmap
        unsigned int i;

        for(i = 0; i < BUFFER_COUNT; i++)
        {
                if(-1 == munmap(buffers[i].start, buffers[i].length))
                {
                        exit(EXIT_FAILURE);
                }
        }

        free(buffers);

        //close camera device 
        if(-1 == close(fd))
        {
                printf("Fail to close fd\n");
                exit(EXIT_FAILURE);
        }
        printf("close_v4l2\t\t[OK]\n");

}

int main()
{

        if(1 != init_v4l2())
        {
                return -1;
        }

        mmap_v4l2();

        image_acq();

        close_v4l2();

        printf("\n");
        return 0;
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章