嵌入式linux应用摄像头编程拍照简单示例基于V4L2接口

linux上面摄像头有专用的接口V4L和V4L2,这里分享一个V4L2接口采集图片的示例,这里硬件野火的imx开发板,摄像头为uvc摄像头,pcl上ubuntu也运行通过

1、先要确定/dev/video*设备文件是否存在,不存在就需要修改内核启用摄像头支持

  Device Drivers  --->
    <*> Multimedia support  --->
       [*] Video capture adapters  --->
           [*]V4L USB devices  ---> 
               <*> USB Video Class (UVC)
                  [*] UVC input events device support    

2、打开设备文件

video_dev = open("\dev\video0",O_RDWR));

3、获取摄像头信息

ioctl(video_dev,VIDIOCGCAP,&video_cap) 

4、获取摄像头通道

ioctl(video_dev,VIDIOCGCHAN,&video_chan)

5、获取捕捉属性

ioctl(video_dev,VIDIOCGPICT,&video_pic)

6、设置捕捉属性

ioctl(video_dev,VIDIOCSPICT,&video_pic)

7、映射地址

ioctl(video_dev,VIDIOCGMBUF,&mbuf) 
mmap(0,mbuf.size,PROT_READ|PROT_WRITE,MAP_SHARED,video_dev,0);

8、获取摄像头图像

ioctl(video_dev,VIDIOCMCAPTURE,&mmap)

9、保存

c程序

camera.c

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/videodev2.h>
#include <sys/mman.h>
#include <sys/time.h>

int fd;
struct v4l2_buffer buf;
struct buffer
{
    
    
    void * start;
    unsigned int length;
    long long int timestamp;
} *buffers;
volatile int IMAGEWIDTH,IMAGEHEIGHT,FRAME_NUM;


//申请缓存并映用户空间地址
static int v4l2_mem_ops()
{
    
    
    unsigned int n_buffers;
    struct v4l2_requestbuffers req;
    
    //申请帧缓冲
    req.count=FRAME_NUM;
    req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory=V4L2_MEMORY_MMAP;
    if(ioctl(fd,VIDIOC_REQBUFS,&req)==-1)
    {
    
    
        printf("request for buffers error\n");
        return -1;
    }

    // 申请用户空间的地址列
    buffers = malloc(req.count*sizeof (*buffers));
    if (!buffers) 
    {
    
    
        printf ("out of memory!\n");
        return -1;
    }
    
    // 进行内存映射
    for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++) 
    {
    
    
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = n_buffers;
        //查询
        if (ioctl (fd, VIDIOC_QUERYBUF, &buf) == -1)
        {
    
    
            printf("query buffer error\n");
            return -1;
        }

        //映射
        buffers[n_buffers].length = buf.length;
        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");
            return -1;
        }
    }
    return 0;    
}


int camera_init(char *camera_dev,int img_w,int img_h,int frame_n)
{
    
    
	struct v4l2_capability cap;
    struct v4l2_fmtdesc fmtdesc;
    struct v4l2_format fmt;
    struct v4l2_streamparm stream_para;
    
    IMAGEWIDTH = img_h;
    IMAGEWIDTH = img_w;
    FRAME_NUM = frame_n;
	//打开摄像头设备
	fd = open(camera_dev,  O_RDWR)if (fd == -1) 
    {
    
    
        printf("[%s]:[%d] open camera file error\r\n", __FUNCTION__, __LINE__);
        return -1;
    }
	//查询设备属性
    if (ioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) 
    {
    
    
        printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
        return -1;
    }
    else
    {
    
    
        printf("driver:\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("Device %s: supports capture.\n",FILE_VIDEO);
        }

        if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING) 
        {
    
    
            printf("Device %s: supports streaming.\n",FILE_VIDEO);
        }
    }
	//显示所有支持帧格式
    fmtdesc.index=0;
    fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    printf("Support format:\n");
    while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
    {
    
    
        printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
        fmtdesc.index++;
    }

    //检查是否支持某帧格式
    struct v4l2_format fmt_test;
    fmt_test.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt_test.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;
    if(ioctl(fd,VIDIOC_TRY_FMT,&fmt_test)==-1)
    {
    
    
        printf("not support format RGB32!\n");      
    }
    else
    {
    
    
        printf("support format RGB32\n");
    }
	//查看及设置当前格式
    printf("set fmt...\n");
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; //jpg格式
    //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//yuv格式

    fmt.fmt.pix.height = img_h;
    fmt.fmt.pix.width = img_w;
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
    printf("fmt.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);
    if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1)
    {
    
    
        printf("Unable to set format\n");
        return -1;
    }

    printf("get fmt...\n"); 
    if(ioctl(fd, VIDIOC_G_FMT, &fmt) == -1)
    {
    
    
        printf("Unable to get format\n");
        return -1;
    }
    {
    
    
        printf("fmt.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);
    }
 //设置及查看帧速率,这里只能是30帧,就是1秒采集30张图
    memset(&stream_para, 0, sizeof(struct v4l2_streamparm));
    stream_para.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    stream_para.parm.capture.timeperframe.denominator = 30;
    stream_para.parm.capture.timeperframe.numerator = 1;

    if(ioctl(fd, VIDIOC_S_PARM, &stream_para) == -1)
    {
    
    
        printf("Unable to set frame rate\n");
        return -1;
    }
    if(ioctl(fd, VIDIOC_G_PARM, &stream_para) == -1)
    {
    
    
        printf("Unable to get frame rate\n");
        return -1;       
    }
    {
    
    
        printf("numerator:%d\ndenominator:%d\n",stream_para.parm.capture.timeperframe.numerator,stream_para.parm.capture.timeperframe.denominator);
    }
    int ret = v4l2_mem_ops();
    if(ret == -1) return ret;
    return 0;
}

int camera_get_pic(char*picname, int picnum)
{
    
    
	unsigned int n_buffers;
    enum v4l2_buf_type type;
    char file_name[100];
    char index_str[10];
    long long int extra_time = 0;
    long long int cur_time = 0;
    long long int last_time = 0;

    //入队和开启采集
    for (n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
    {
    
    
        buf.index = n_buffers;
        ioctl(fd, VIDIOC_QBUF, &buf);
    }
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd, VIDIOC_STREAMON, &type);
    
    //出队,处理,写入yuv文件,入队,循环进行
    int loop = picnum/FRAME_NUM;
    if(loop == 0) loop = 1;
    while(loop > 0)
    {
    
    
        for(n_buffers = 0; n_buffers < FRAME_NUM; n_buffers++)
        {
    
    
        	if(picnum == (n_buffers+FRAME_NUM*loop))
        		return 0;
            //出队
            buf.index = n_buffers;
            ioctl(fd, VIDIOC_DQBUF, &buf);

            //查看采集数据的时间戳之差,单位为微妙
            buffers[n_buffers].timestamp = buf.timestamp.tv_sec*1000000+buf.timestamp.tv_usec;
            cur_time = buffers[n_buffers].timestamp;
            extra_time = cur_time - last_time;
            last_time = cur_time;
            printf("time_deta:%lld\n\n",extra_time);
            printf("buf_len:%d\n",buffers[n_buffers].length);

            //处理数据只是简单写入文件,名字以loop的次数和帧缓冲数目有关
            printf("grab image data OK\n");
            memset(file_name,0,sizeof(file_name));
            memset(index_str,0,sizeof(index_str));
            sprintf(index_str,"%d",loop*4+n_buffers);
            strcpy(file_name, picname);
            strcat(file_name, index_str);
            strcat(file_name,".jpg");
            //strcat(file_name,".yuv");
            FILE *fp2 = fopen(file_name, "wb");
            if(!fp2)
            {
    
    
                printf("open %s error\n",file_name);
                return (-1);
            }
            fwrite(buffers[n_buffers].start, IMAGEHEIGHT*IMAGEWIDTH*2,1,fp2);
            fclose(fp2);
            printf("save %s OK\n",file_name);

            //入队循环
            ioctl(fd, VIDIOC_QBUF, &buf);       
        }

        loop--;
    }
}

int close_camera()
{
    
    
    unsigned int n_buffers;
    enum v4l2_buf_type type;

    //关闭流
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd, VIDIOC_STREAMON, &type);
    
    //关闭内存映射
    for(n_buffers=0;n_buffers<FRAME_NUM;n_buffers++)
    {
    
    
        munmap(buffers[n_buffers].start,buffers[n_buffers].length);
    }
    
    //释放自己申请的内存
    free(buffers);
    
    //关闭设备
    close(fd);
    return 0;
}

main.c

#include <stdio.h>
#include "camera.h"

int main()
{
    
    
	printf("init camera\n");
	camera_init("/dev/video0", 640, 480, 4);
	printf("read camera\n");
	camera_get_pic("/img/mypic", 20);
	printf("finish,close\n");
	close_camera();
	return 0;
}

运行程序之前先建一个img目录用来存照片

猜你喜欢

转载自blog.csdn.net/u010835747/article/details/108641914