Linux下V4L2应用编程

v4l2介绍,点击打开链接

下面是我的Demo源码

camera.h

#ifndef _CAMERA_H_
#define _CAMERA_H_

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

typedef struct _ReqBufInfo
{
    void *start;
    unsigned int length;
}ReqBufInfo;

typedef enum _PixelFormat
{
     PIX_FMT_MJPEG,
     PIX_FMT_YUYV
}PixelFormat;

class  Camera
{
    public:
	    Camera(const char* dev_name = "");                                                 //! 需要操作的设备名
	    ~Camera(); 
	    int openCamera();
	    int closeCamera();
            int camera_query_cap();                                                            //! 查询设备信息
            int camera_query_support_format();                                                 //! 查询支持的帧格式类型
            int enum_frame_sizes(const int pix_fmt);                                           //! 枚举帧大小
            int enum_frame_intervals(int pix_fmt, const int width, const int height);          //! 枚举帧间隔
            int camera_set_format(const int width, const int height, const int format_t);      //! 设置帧格式类型
	    int camera_get_format(struct v4l2_format &format);                                 //! 获取当前设置的帧格式类型
            int camera_set_streamparm(const int fps);                                          //! 设置是视频流属性
            int camera_get_streamparm(struct v4l2_streamparm &streamparm);                     //! 获取视频流属性      
            int camera_req_buf(const int req_buffer_count);                                    //! 向内核申请帧缓冲
            int camera_mmap(const int mmap_buffer_count);                                      //! 把内核空间地址映射到用户空间
            int camera_all_put_queue(const int buffer_count);                                  //! 将buffer放入视频输入队列
            int camera_start_capture();                                                        //! 开启视频流
            int camera_stop_capture();                                                         //! 关闭视频流
            int camera_unmmap(const int buffer_count);                                         //! 解除映射关系
            int camera_is_read_ready(const int sec,  const int usec);                          //! 测试视频缓存是否可读
            int camera_get_a_frame(unsigned char **data, int &datalen);                        //! 获取一帧数据 



    private:
	    int m_fd;
	    int m_is_open;
	    char* m_name;
	    ReqBufInfo *m_req_buf_info;
};


#endif

camera.cpp

#include "camera.h"
  

static int xioctl(int fd, int request, void *arg)
{
    int ret = 0;
    
    while ((ret = ioctl(fd, request, arg))  == -1 && 
		errno == EINTR);
    return ret;
}

Camera::Camera(const char* dev_name) : m_name(new char[strlen(dev_name)+1])
{
    strcpy(m_name, dev_name);
}


Camera::~Camera()
{
    if(m_req_buf_info != NULL)
    {
	free(m_req_buf_info);
	m_req_buf_info = NULL;
    }
    delete [] m_name;
}

int Camera::openCamera()
{
    m_fd = open(m_name, O_RDWR);

    if(m_fd < 0)
    {
	perror("open Camera");
        return -1;
    }

    return 0;
}

int Camera::closeCamera()
{
    int ret = -1;
     
    ret = close(m_fd);

    if(ret < 0)
    {
       perror("colse Camera");
    }

    return ret;
}

int Camera::camera_query_cap()
{
    struct v4l2_capability cap;
    memset(&cap, 0, sizeof(struct v4l2_capability));
    
    if(xioctl(m_fd, VIDIOC_QUERYCAP, &cap) == -1)           //! 查询设备信息
    {
	perror("query capabilities failed");
	return -1;
    }
    
    //! 打印信息
    printf("driver name:\t\t%s\n", cap.driver);          //! 驱动名称
    printf("dev name:\t\t%s\n", cap.card);               //! 设备名字
    printf("buf info:\t%s\n", cap.bus_info);             //! 设备在系统中的位置
    printf("dirve vereion \t%d\n",cap.version);          //! 查询驱动版本号 
    printf("capabilities\t%x\n", cap.capabilities);

    if((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)            //! 是否支持图像获取
    {
	printf("capabilities:\tsupport capture\n");
    }

    if((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)                    //! 是否支持流操作
    {
	printf("capabilities:\tsupport srteaming\n");
    }
    
    return 0;
}

int Camera::camera_query_support_format()                                                 //! 查询支持的帧格式类型
{
    int ret;
    struct v4l2_fmtdesc fmt;

    memset(&fmt, 0, sizeof(struct v4l2_fmtdesc));
    fmt.index = 0;                                                                        //! 要查询的格式序号
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;                                               //! 帧类型
    
    printf("camera format: \n");

    while((ret = xioctl(m_fd, VIDIOC_ENUM_FMT, &fmt)) == 0)                               //! 循环打印出所有支持的格式
    {
        fmt.index++;
	printf("{ pixelformat = '%c%c%c%c', description = '%s'}\n",
		    fmt.pixelformat & 0xFF,
		    (fmt.pixelformat >> 8) & 0xFF,
		    (fmt.pixelformat >> 16) & 0xFF,
		    (fmt.pixelformat >> 24) & 0xFF,
		    fmt.description);
        ret =  enum_frame_sizes(fmt.pixelformat);
	
	if(ret != 0)
	{
	    printf("Unable to enumerate frame sizes.\n");
	}
    }

    if(errno != EINVAL)
    {
	printf("ERROR enumerating frame formats: %d\n",errno);
	return  errno;
    }
    
    return 0;

}

int Camera::enum_frame_sizes(const int pix_fmt)                                           //! 枚举帧大小
{
    int ret;
    struct v4l2_frmsizeenum fsize;

    memset(&fsize, 0, sizeof(struct v4l2_frmsizeenum));
    fsize.index = 0;                                                                      //! 要查询所支持的帧大小的对应序号  
    fsize.pixel_format = pix_fmt;                                                         //! 根据他支持的格式去查询支持帧的大小
    
    while((ret = xioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &fsize))  == 0)
    {
	if(fsize.type == V4L2_FRMSIZE_TYPE_DISCRETE)                                      //! 网上查询说UVC驱动固定的这种类型  
	{
	    printf("{ discrete:  width  = %u, height = %u}\n",
			    fsize.discrete.width, fsize.discrete.height);
            
            ret = enum_frame_intervals(pix_fmt, fsize.discrete.width,                     //! 枚举帧间隔 
                                       fsize.discrete.height);
            if(ret != 0)
            { 
                printf("Unable to enumerate  frame sizes.\n");
            } 
	} 
        else if (fsize.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)
        {
            printf("{ continuous: min { width = %u, height = %u } .. "
                   "max { width = %u, height = %u } }\n",
                   fsize.stepwise.min_width, fsize.stepwise.min_height,
                   fsize.stepwise.max_width, fsize.stepwise.max_height);
            printf("  Refusing to enumerate frame intervals.\n");
            break;
        }
        else if (fsize.type == V4L2_FRMSIZE_TYPE_STEPWISE)
        {
            printf("{ stepwise: min { width = %u, height = %u } .. "
                   "max { width = %u, height = %u } / "
                   "stepsize { width = %u, height = %u } }\n",
                   fsize.stepwise.min_width, fsize.stepwise.min_height,
                   fsize.stepwise.max_width, fsize.stepwise.max_height,
                   fsize.stepwise.step_width, fsize.stepwise.step_height);
            printf("  Refusing to enumerate frame intervals.\n");
            break;
        }
        fsize.index++;
    }

    if(ret != 0 && errno != EINVAL)
    {
	printf("ERROR enumerating frame size: %d\n", errno);
	return errno;
    }

    return 0;

}

int Camera::enum_frame_intervals(int pix_fmt, const int width, const int height)          //! 枚举帧间隔
{
     int ret;
     struct v4l2_frmivalenum fival;

     memset(&fival, 0, sizeof(struct v4l2_frmivalenum));

     fival.index = 0;                                                                     //! 设置对应序号
     fival.pixel_format = pix_fmt;                                                        //! 设置对应要查询的格式
     fival.width = width;                                                                 //! 设置宽度
     fival.height = height;                                                               //! 设置高度
     
     printf("\tTime  interval between frame");

     while((ret = xioctl(m_fd, VIDIOC_ENUM_FRAMEINTERVALS, &fival)) == 0)                 //! 根据设置内容轮询帧间隔
     {
	 if(fival.type == V4L2_FRMIVAL_TYPE_DISCRETE)
	 {
             printf("%u/%u, ",fival.discrete.numerator,  fival.discrete.denominator);
	 }
	 else if (fival.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)
         {
	     printf("{min { %u/%u } .. max { %u/%u } }, ",
             fival.stepwise.min.numerator, fival.stepwise.min.numerator,
             fival.stepwise.max.denominator, fival.stepwise.max.denominator);
	     break;
    	 }        
	 else if (fival.type == V4L2_FRMIVAL_TYPE_STEPWISE)
	 {			             
            printf("{min { %u/%u } .. max { %u/%u } / ""stepsize { %u/%u } }, ",
            fival.stepwise.min.numerator, fival.stepwise.min.denominator,
            fival.stepwise.max.numerator, fival.stepwise.max.denominator,
	    fival.stepwise.step.numerator, fival.stepwise.step.denominator);
	    break;
	 }

	 fival.index++;
     }
     printf("\n\n");

     if(ret != 0 && errno != EINVAL)
     {
	 printf("ERROR enumerating frame intervals:  %d\n",errno);
	 return errno;
     }
     
     return  0;

}

int Camera::camera_set_format(const int width, const int height, const int format_t)   //! 设置帧格式类型
{
    struct  v4l2_format format;

    memset(&format, 0, sizeof(struct v4l2_format));

    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = width;
    format.fmt.pix.height = height;

    switch(format_t)
    {
	case PIX_FMT_MJPEG:
		format.fmt.pix.pixelformat =  V4L2_PIX_FMT_MJPEG;
		break;
	case PIX_FMT_YUYV:
		format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
		break;
	default:
		printf("unsupported video format\n");
		return -1;
    }

    if(xioctl(m_fd, VIDIOC_S_FMT, &format) == -1)
    {
	perror("set format failed");
	return -1;
    }

    return 0;
}

int Camera::camera_get_format(struct v4l2_format &format)                             //! 获取当前设置的帧格式类型
{

    memset(&format, 0, sizeof(struct v4l2_format));

    if(xioctl(m_fd, VIDIOC_G_FMT, &format) == -1)
    {
	perror("get format failed\n");
	return -1;
    }
    
    return 0;   

}

int Camera::camera_set_streamparm(const int fps)           //! 设置是视频流属性
{
    struct v4l2_streamparm streamparm;

    memset(&streamparm, 0, sizeof(struct v4l2_streamparm));

    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    streamparm.parm.capture.timeperframe.numerator = 1;
    streamparm.parm.capture.timeperframe.denominator = fps;
    if(xioctl(m_fd, VIDIOC_S_PARM, &streamparm) == -1)
    {
        perror("set fps failed\n");
	return -1;
    }
    return 0;
}

int Camera::camera_get_streamparm(struct v4l2_streamparm &streamparm)                    //! 获取视频流属性  
{
    memset(&streamparm, 0, sizeof(struct v4l2_streamparm));

    streamparm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    
    if(xioctl(m_fd, VIDIOC_G_PARM, &streamparm) == -1)
    {
	perror("get streamparm failed\n");
	return -1;
    }
}

int Camera::camera_req_buf(const int req_buffer_count)     //! 向内核申请帧缓冲
{
    struct v4l2_requestbuffers req;

    memset(&req, 0, sizeof(struct v4l2_requestbuffers));

    req.count = req_buffer_count;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    if(req.count < 2)
    {
	printf("insufficient buffer memory\n");
	return -1;
    } 

    if(xioctl(m_fd, VIDIOC_REQBUFS, &req) == -1)
    {
        perror("request buffer failed");
	return -1;
    }
    
    return 0;

}

int Camera::camera_mmap(const int mmap_buffer_count)       //! 把内核空间地址映射到用户空间
{
    struct v4l2_buffer buf;   
    
    m_req_buf_info = (ReqBufInfo *)calloc(mmap_buffer_count, sizeof(ReqBufInfo));   //! 开辟出与内核申请到的缓存大小一直的空间

    for(int i = 0; i < mmap_buffer_count; i++)                                     //! 开始循环映射
    {
	memset(&buf, 0, sizeof(struct v4l2_buffer));

	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	buf.index = i;

	if(xioctl(m_fd, VIDIOC_QUERYBUF, &buf) == -1)
	{
	    perror("query buffer failed");
	    return -1;
	}

	m_req_buf_info[i].length = buf.length;

	m_req_buf_info[i].start = mmap(NULL, buf.length,
			               PROT_READ | PROT_WRITE,
			               MAP_SHARED,
	      	                       m_fd,
                                       buf.m.offset);
	if(m_req_buf_info[i].start == MAP_FAILED)
	{
	    perror("mmap buffers failed");
	    return -1;
	}
    }

    return 0;
}

int Camera::camera_all_put_queue(const int buffer_count)      //! 将buffer放入视频输入队列
{
    struct v4l2_buffer buf;

    for(int i = 0; i < buffer_count; i++)                     //! 循环将所有缓存块放入
    {
	memset(&buf, 0, sizeof(struct v4l2_buffer));

	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_MMAP;
	buf.index = i;
	if(xioctl(m_fd, VIDIOC_QBUF, &buf) == -1)
	{
	    perror("queue buffer failed");
	    return -1;
	}
    }

    return  0;
}

int Camera::camera_start_capture()                            //! 开启视频流
{
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if(xioctl(m_fd, VIDIOC_STREAMON, &type) == -1)
    {
	perror("stream on failed");
	return -1;
    }

    return 0;
}

int Camera::camera_stop_capture()                            //! 关闭视频流
{
    int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if(xioctl(m_fd, VIDIOC_STREAMOFF, &type) == -1)
    {
	perror("stream off failed");
	return -1;
    }

    return 0;
}

int Camera::camera_unmmap(const int buffer_count)           //! 解除映射关系
{
    for(int i = 0; i < buffer_count; i++)
    {
	if(munmap(m_req_buf_info[i].start, m_req_buf_info[i].length) == -1)
	{
	    perror("mnumap failed");
	    return -1;
	}
    }
    return 0;
}

int Camera::camera_is_read_ready(const int sec,  const int usec)  //! 测试视频缓存是否可读
{
    int sel_ret;

    fd_set my_set;
    
    FD_ZERO(&my_set);
    FD_SET(m_fd, &my_set);

    struct timeval tv;
    tv.tv_sec = sec;
    tv.tv_usec = usec;
    
    sel_ret = select(m_fd + 1, &my_set, NULL, NULL, &tv);         //! 阻塞等待一定时间

    if(sel_ret == -1)
    {
	if(errno == EINTR)
	{
	    return 0;
	}
	return -1;
    }

    if(sel_ret == 0)
    {
	printf("select time out\n");
	return 0;
    }
    
    return 1;

}

int Camera::camera_get_a_frame(unsigned char **data, int &datalen)  //! 获取一帧数据 
{
    struct v4l2_buffer buf;
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;

    if(xioctl(m_fd, VIDIOC_DQBUF, &buf))
    {
	perror("de-queue buffers  failed");
	return -1;
    }

    *data = (unsigned char*)m_req_buf_info[buf.index].start;
    datalen = m_req_buf_info[buf.index].length;

    if(xioctl(m_fd, VIDIOC_QBUF, &buf) == -1)
    {
	perror("queue buffers failed");
	return -1;
    }

    return 0;
}




main.cpp

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

int main(int argc, char *argv[])
{
    if(argc < 2)
    {
	printf("Invalid argument\n");
	exit(EXIT_FAILURE);
    }

    char *dev_name = argv[1];
    Camera *my_camera = new Camera(dev_name);

    if(my_camera->openCamera() == -1)
    {
	exit(EXIT_FAILURE);
    }

    my_camera->camera_query_cap();                                 //! 查询

    my_camera->camera_query_support_format();

    if(my_camera->camera_set_format(640, 480, PIX_FMT_MJPEG) < 0)  //! 设置
    {
        exit(EXIT_FAILURE);
    }

    if(my_camera->camera_set_streamparm(30) == -1)
    {
        exit(-1);
    }

    if( my_camera->camera_req_buf(4) == -1)
    {
        exit(EXIT_FAILURE);
    }

    if(my_camera->camera_mmap(4) == -1)
    {
        exit(-1);         
    } 

    if(my_camera->camera_all_put_queue(4) == -1)
    {
        exit(-1);
    }

    if(my_camera->camera_start_capture() == -1)
    {
        exit(-1);
    }

    int len = 0;
    unsigned char *mjpeg;

    if(my_camera->camera_is_read_ready(5,0) > 0)
    {
	printf("start get jpeg\n");
	my_camera->camera_get_a_frame(&mjpeg, len);
	int file = open("./temp.jpg",O_CREAT | O_RDWR);
	if(file < 0)
	{
	    printf("file is error\n");
	    my_camera->camera_unmmap(4);
	    my_camera->closeCamera();
	    exit(-1);
	}

	write(file,mjpeg,len);                           //! 数据写入文件
        my_camera->camera_unmmap(4);
	printf("write is succeed\n");
	close(file);
    }

    my_camera->closeCamera();
    delete  my_camera;
   
    return 0;
}
实现效果是打开摄像头采一幅图,保存成文件。如有错误请指正,一起学习
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值