Android 利用V4L2 调用camera

您所在的位置:网站首页 android获取摄像头方式 Android 利用V4L2 调用camera

Android 利用V4L2 调用camera

2024-07-02 15:14| 来源: 网络整理| 查看: 265

为何要使用V4L2进行开发

出于安全原因,使用Android 原生的Camera接口,必须要使用可见的surface显示摄像头的preview图像,即必须要让用户看到你的应用正在使用摄像头。另外Android Camera framework经过层层封装,同时必须调用到显示和MediaPlayer两个模块,数据处理的环节比较多。 在开发过程中,可能会有需求只需要去获取camera数据结合AI进行处理。通过V4L2接口可以直接从驱动获取camera数据,省去了很多中间环节,同时可以在后台处理数据,不需要作为前台应用运行。

因为V4L2 是通过打开设备文件"/dev/video*"进行操作,所以想通过V4L2直接在其他手机厂商上开发应用是行不通的,一般应用没有权限去操作设备

简介

在Linux中,摄像头方面的标准化程度比较高,这个标准就是V4L2驱动程序,这也是业界比较公认的方式。 V4L全称是Video for Linux,是Linux内核中标准的关于视频驱动程序,目前使用比较多的版本是Video for Linux 2,简称V4L2。它为Linux下的视频驱动提供了统一的接口,使得应用程序可以使用统一的API操作不同的视频设备。从内核空间到用户空间,主要的数据流和控制类均由V4L2驱动程序的框架来定义。 V4L2支持三类设备:视频输入输出设备、VBI设备和radio设备,分别会在/dev目录下产生video*、radio和vbi设备节点。

V4L2基本框架

image.png

这里我们只考虑应用程序如何调用V4L2接口。V4L2本身就是一个字符设备,跟其他字符设备驱动一样,其实使用V4L2只要3个接口:

open :打开设备文件ioclt : 设置或获取camera相关参数,读写控制等mmap: 内存映射, 用户空间通过内存映射获取到camera的数据close:关闭设备文件描述符

详细的内容可以参考官网 http://v4l.videotechnology.com/dwg/ 下面介绍的只是一个使用的demo,demo代码可参考 https://github.com/yizhongliu/AnV4L2Camera

V4L2 采集camera数据

下面是应用利用V4L2 采集camera数据的大体流程: v4l2camera.png

打开文件描述符 int open(constchar *device_name, int flags);

跟打开其他的设备文件是一样的,camera的设备节点是**/dev/video***

int fd=open("/dev/video0",O_RDWR, 0);// 打开设备 获取camera的参数 先直观地了解下用v4l2获取camera的参数的格式 :~/software/squashfs-root$ v4l2-ctl -d0 --list-formats-ext ioctl: VIDIOC_ENUM_FMT Index : 0 Type : Video Capture Pixel Format: 'MJPG' (compressed) Name : Motion-JPEG Size: Discrete 1280x720 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 848x480 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 960x540 Interval: Discrete 0.033s (30.000 fps) Index : 1 Type : Video Capture Pixel Format: 'YUYV' Name : YUYV 4:2:2 Size: Discrete 640x480 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 160x120 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 320x180 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 320x240 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 424x240 Interval: Discrete 0.033s (30.000 fps) Size: Discrete 640x360 Interval: Discrete 0.033s (30.000 fps)

这是在电脑上面用命令获取到的打印,可以看到组织的方式是 第一层是支持的图像格式, 第二层是支持的分辨率,第三层的帧率,所以我们写代码时是先去获取支持的图片格式,再用图片格式去获取该格式下支持的分辨率,再利用图片格式和 分辨率去获取该条件下支持的帧率

获取支持的格式:

struct v4l2_fmtdesc fmtd; fmtd.index = i; fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (ioctl(fd, VIDIOC_ENUM_FMT, &fmtd) < 0) break;

获取支持的分辨率

struct v4l2_frmsizeenum frmsize; frmsize.index = j; frmsize.pixel_format = fmtd.pixelformat; if (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) < 0) break;

获取支持的帧率

struct v4l2_frmivalenum framival; framival.index = k; framival.pixel_format = fmtd.pixelformat; framival.width = frmsize.discrete.width; framival.height = frmsize.discrete.height; if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &framival) < 0) break; 设置camera参数 这里主要设置视频的格式,宽高等 struct v4l2_format format; format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; format.fmt.pix.width = width; format.fmt.pix.height = height; format.fmt.pix.pixelformat = pixelformat; // MUST set format.fmt.pix.field = V4L2_FIELD_ANY; ret = ioctl(fd, VIDIOC_S_FMT, &format); if (ret < 0) { ALOGE("Unable to set format: %s", strerror(errno)); return -1; } 申请帧缓冲区

V4L2支持内存映射方式(mmap)和直接读取方式(read)来采集数据,前者一般用于连续视频数据的采集,后者常用于静态图片数据的采集,这里只讨论内存映射方式的视频采集。

视频数据采集过程有两个队列,一个输入队列和一个输出队列。视频开始采集后,启动视频采集后,驱动程序开始采集一帧数据,把采集的数据放入视频采集输入队列的第一个帧缓冲区,一帧数据采集完成后,驱动程序将该帧缓冲区移至视频采集输出队列。应用程序从输出队列中获取数据,处理完后将缓冲区放到输入队列的队尾。

1353038230_2495.png

/* V4L2: request buffers, only 1 frame */ rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = V4L2_MEMORY_MMAP; rb.count = 1; ret = ioctl(fd, VIDIOC_REQBUFS, &rb); if (ret < 0) { ALOGE("Unable request buffers: %s", strerror(errno)); return -1; } /* V4L2: map buffer */ memset(&buf, 0, sizeof(struct v4l2_buffer)); buf.index = 0; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_QUERYBUF, &buf); if (ret < 0) { ALOGE("Unable query buffer: %s", strerror(errno)); return -1; } /* Only map one */ mem = (unsigned char *)mmap(0, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset); if (mem == MAP_FAILED) { ALOGE("Unable map buffer: %s", strerror(errno)); return -1; } /* V4L2: queue buffer */ ret = ioctl(fd, VIDIOC_QBUF, &buf);

这里是先申请了帧缓冲队列,只申请了一个,然后查询buff是否申请成功,最后做了内存映射,这里只用了一个buf,多个的话需要映射多个buf。最后一步ioctl(fd, VIDIOC_QBUF, &buf)是把缓冲区放到视频输入采集队列。

开始数据采集 enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMON, &type); 循环数据采集处理 一般会起一个线程对数据进行采集处理 int V4L2Camera::GrabRawFrame(void *raw_base) { int ret; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; /* V4L2: dequeue buffer */ ret = ioctl(fd, VIDIOC_DQBUF, &buf); if (ret < 0) { ALOGE("Unable query buffer: %s", strerror(errno)); return ret; } ALOGD("copy size :%d", buf.bytesused); /* copy to userspace */ memcpy(raw_base, mem, buf.bytesused); /* V4l2: queue buffer again after that */ ret = ioctl(fd, VIDIOC_QBUF, &buf); if (ret < 0) { ALOGE("Unable query buffer: %s", strerror(errno)); return ret; } return 0; }

这里的处理方法是先取出视频输出队列的一个缓冲区,将数据拷贝到raw_base, 最后将缓冲区放到视频输入队列

结束数据采集 enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(fd, VIDIOC_STREAMOFF, &type); 关闭文件描述符 close(fd)

关于demo中jni和视频显示部分,可以参考我的专题里面的介绍 Android FFmpeg专题结构

https://www.cnblogs.com/zhangsx/p/5803162.html https://blog.csdn.net/li123128/article/details/89499935



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3