V4L2应用层开发

常用的命令标识符和结构体

// 在 include/linux/videodev2.h 中定义
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;
// 在include/linux/videodev2.h中定义
struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS
struct v4l2_capability 	   //视频设备的功能,对应命令VIDIOC_QUERYCAP 
struct v4l2_input   	   //视频输入信息,对应命令VIDIOC_ENUMINPUT
struct v4l2_standard 	   //视频的制式,比如PAL,NTSC,对应命令     VIDIOC_ENUMSTD 
struct v4l2_format 	 	   //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_buffer 		   //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF
struct v4l2_crop  		   //视频信号矩形边
v4l2_std_id   			   //视频制式

应用层流程图

在这里插入图片描述
详细使用可查看:https://www.dandroid.cn/21599

下面是我写的一个事例:

#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 <unistd.h>
#include <asm/types.h>         
#include <linux/videodev2.h>
#include <linux/fb.h>

/* TCP */
#include "tcp_client.h"

#define CLEAR(x)               memset (&(x), 0, sizeof (x))
#define BUFF_NUM           	   4                       // 缓存帧个数

static char *                  dev_name                = "/dev/video2";
static int                     camera_fd                 = -1;
static char*                   buffers[BUFF_NUM];
static unsigned int     	   n_buffers[BUFF_NUM];

/************************************
 * 函数名称: camera_init
 * 函数功能: 初始化相机
 * 参         数: width 相片宽度
 *                        height 相片高度
 * 返  回  值: 
 ***********************************/
int camera_init(int width, int height)
{
        struct v4l2_capability cap;
        struct v4l2_format fmt;
        int i;
        enum v4l2_buf_type type;
        struct v4l2_fmtdesc fmt1;
        int ret;

        /* 1、打开设备,并查询设备属性 */
        camera_fd = open(dev_name, O_RDWR | O_NONBLOCK, 0);
        ret = ioctl(camera_fd, VIDIOC_QUERYCAP, &cap);
        if(ret < 0)
        {
                printf("VIDIOC_QUERYCAP error!\n");
                return -1;
        }

        /* 2、设置图像帧格式 */
        CLEAR(fmt);
        fmt.type                                =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fmt.fmt.pix.width             =       width;
        fmt.fmt.pix.height            =       height;
        fmt.fmt.pix.pixelformat =       V4L2_PIX_FMT_JPEG;
        //fmt.fmt.pix.pixelformat =       V4L2_PIX_FMT_MJPEG;
        fmt.fmt.pix.field                =      V4L2_FIELD_ANY;

        ret = ioctl(camera_fd, VIDIOC_S_FMT, &fmt);
        //ret = ioctl(camera_fd, VIDIOC_G_FMT, &fmt);
	if(ret < 0)
        {
		printf("VIDIOC_S_FMT error!\n");
		return -1;
	}

        /* 3、申请缓冲区 */
        struct v4l2_requestbuffers req;
        CLEAR(req);
        req.count       =       BUFF_NUM;
        req.type          =       V4L2_BUF_TYPE_VIDEO_CAPTURE;
        req.memory =       V4L2_MEMORY_MMAP;

        ret = ioctl(camera_fd, VIDIOC_REQBUFS, &req);
        if(ret < 0)
        {
		printf("VIDIOC_REQBUFS error!\n");
		return -1;
	}
        if(req.count < 1)
                printf("Insufficient buffer memory\n");
        
        /* 4、将申请的缓冲帧从内核空间映射到用户空间 */
        for(i = 0; i < req.count; i++)
        {
                struct v4l2_buffer buf;
                CLEAR(buf);
                buf.type          =     V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory =     V4L2_MEMORY_MMAP;
                buf.index       =     i;
                ret = ioctl(camera_fd, VIDIOC_QUERYBUF, &buf);
		if(ret < 0)
                {
			printf("VIDIOC_QUERYBUF error!\n");
			return -1;
		}

                n_buffers[i] =buf.length;
                buffers[i] = mmap(NULL, //start anywhere
                                                      buf.length,
                                                      PROT_READ | PROT_WRITE /* required */,
                                                      MAP_SHARED /* recommended */,
                                                      camera_fd,
                                                      buf.m.offset);

                if(MAP_FAILED == buffers[i])
                {
                        printf("mmap failed\n");
                        return -1;
                }
                memset(buffers[i], 0, n_buffers[i]);
                printf("buf.length[%d] = %d\n",i, buf.length);
        }

        /* 5、将申请的缓冲帧放入队列,并启动数据流 */
        for(i = 0; i < req.count; i++)
        {
                struct v4l2_buffer buf;
                CLEAR(buf);
                buf.type          =     V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory =     V4L2_MEMORY_MMAP;
                buf.index       =     i;
                ret = ioctl(camera_fd, VIDIOC_QBUF, &buf);
		if(ret < 0)
                {
			printf("VIDIOC_QBUF error!\n");
			return -1;
		}
        }
                return 0;
}

/************************************
 * 函数名称: camera_start
 * 函数功能: 启动捕捉图像数据
 * 参         数: 无
 * 返  回  值: 
 ***********************************/
int camera_start()
{
        int ret;
        enum v4l2_buf_type type;
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl (camera_fd, VIDIOC_STREAMON, &type);
                if ( ret < 0 )
                {
                        printf ("VIDIOC_STREAMON failed\n");
                        return -1;
                }
        return 0;
}

/************************************
 * 函数名称: camera_stop
 * 函数功能: 停止捕捉图像数据
 * 参         数: 无
 * 返  回  值: 
 ***********************************/
int camera_stop()
{
        int ret;
        enum v4l2_buf_type type;
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        ret = ioctl (camera_fd, VIDIOC_STREAMOFF, &type);
                if ( ret < 0 )
                {
                        printf ("VIDIOC_STREAMOFF failed\n");
                        return -1;
                }
        return 0;
}

/************************************
 * 函数名称: camera_read_frame
 * 函数功能: 获取一帧图像数据,并且将取出的帧缓冲重新放回图像输入队列
 * 参         数: 存放数据文件的句柄
 * 返  回  值: 
 ***********************************/
int camera_read_frame(int fd)
{
        struct v4l2_buffer buf;
        int ret;

        /* 取出缓冲区的数据 */
        CLEAR(buf);
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.memory = V4L2_MEMORY_MMAP;

        ret = ioctl(camera_fd, VIDIOC_DQBUF, &buf);
	if(ret < 0){
		printf("VIDIOC_DQBUF %s\n", __func__);
		return -1;
	}

        /* 将数据写入文件中 */
        write(fd,buffers[buf.index], n_buffers[buf.index]);

        /* 将取出的帧缓冲重新放回图像输入队列 */
        ret =  ioctl (camera_fd, VIDIOC_QBUF, &buf); 
	if(ret < 0){
		printf("error: VIDIOC_QBUF %s\n", __func__);
		return -1;
	}

        return 0;
}

/************************************
 * 函数名称: capture_an_image
 * 函数功能: 捕获一张图片
 * 参         数: 无
 * 返  回  值: 
 ***********************************/
void capture_an_image(void)
{
        int i;
        int fd;
        while(1)
        {
                fd_set fds;
                int ret;

                camera_start();

                fd = open("image.jpg",O_CREAT| O_RDWR, 0666);
                if(fd < 0)
                {
                        printf("open image.jpg error!\n");
                        exit(0);
                }

                FD_ZERO(&fds);
                FD_SET(camera_fd, &fds);

                ret = select(camera_fd + 1, &fds, NULL, NULL, NULL);
                if(ret == -1)
                {
                        if(EINTR == errno)
                        {
                                continue;
                        }
                        printf("select error!\n");
                }
                else if(ret == 0)
                {
                        fprintf (stderr, "select timeout\n");
	    	        exit (EXIT_FAILURE);
                }

                camera_read_frame(fd);

                camera_stop();
                break;
        }
        close(fd);
}

/************************************
 * 函数名称: camera_tcp_send_frame
 * 函数功能: 获取一帧图像数据,通过TCP发送,并且将取出的帧缓冲重新放回图像输入队列
 * 参         数: 存放数据文件的句柄
 * 返  回  值: 
 ***********************************/
int camera_tcp_send_frame()
{
        int ret;
        int server_socket;
        struct v4l2_buffer buf;

        camera_start();

        server_socket = my_socket();
        if(server_socket < 0)
        {
                perror("socket error\n");
		return -1;
        }

        ret = my_connect(server_socket);
        if(ret < 0)
        {
                perror("connect error\n");
		return -1;
        }

        while(1)
        {
                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(camera_fd, &fds);

                ret = select(camera_fd + 1, &fds, NULL, NULL, NULL);
                if(ret == -1)
                {
                        if(EINTR == errno)
                        {
                                continue;
                        }
                        printf("select error!\n");
                }
                else if(ret == 0)
                {
                        fprintf (stderr, "select timeout\n");
	    	        exit (EXIT_FAILURE);
                }

                
                /* 取出缓冲区的数据 */
                CLEAR(buf);
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;

                ret = ioctl(camera_fd, VIDIOC_DQBUF, &buf);
                if(ret < 0)
                {
                        printf("ret = %d\n", ret);
                        printf("%s %s line %d: VIDIOC_DQBUF error\n",__FILE__, __FUNCTION__, __LINE__);
                        return -1;
                }

                /* 将数据通过 TCP 发送 */
                my_send(server_socket, buffers[buf.index], n_buffers[buf.index]);

                /* 将取出的帧缓冲重新放回图像输入队列 */
                ret =  ioctl (camera_fd, VIDIOC_QBUF, &buf); 
                if(ret < 0)
                {
                        printf("VIDIOC_QBUF %s\n", __func__);
                        return -1;
                }
        }

        camera_stop();
        my_close(server_socket);

        return 0;
}

int main()
{
	/* 初始化,传递宽高像素 */
	camera_init(1920,1080);
	/* 在当前目录下创建 image.jpg 文件,并将捕获到的一帧 jpeg 图像数据存入该文件 */
	capture_an_image();
	/* 将捕获到的 jpeg 格式的图像数据通过 TCP 发送给服务器进行显示 */
	camera_tcp_send_frame();
	retrun 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值