为何要使用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基本框架

这里我们只考虑应用程序如何调用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数据的大体流程:

  • 打开文件描述符
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_FMTIndex       : 0Type        : Video CapturePixel Format: 'MJPG' (compressed)Name        : Motion-JPEGSize: Discrete 1280x720Interval: Discrete 0.033s (30.000 fps)Size: Discrete 848x480Interval: Discrete 0.033s (30.000 fps)Size: Discrete 960x540Interval: Discrete 0.033s (30.000 fps)Index       : 1Type        : Video CapturePixel Format: 'YUYV'Name        : YUYV 4:2:2Size: Discrete 640x480Interval: Discrete 0.033s (30.000 fps)Size: Discrete 160x120Interval: Discrete 0.033s (30.000 fps)Size: Discrete 320x180Interval: Discrete 0.033s (30.000 fps)Size: Discrete 320x240Interval: Discrete 0.033s (30.000 fps)Size: Discrete 424x240Interval: Discrete 0.033s (30.000 fps)Size: Discrete 640x360Interval: 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)来采集数据,前者一般用于连续视频数据的采集,后者常用于静态图片数据的采集,这里只讨论内存映射方式的视频采集。

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

    /* 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

Android 利用V4L2 调用camera相关推荐

  1. Android 利用V4L2 预览MJPEG格式 USB camera

    介绍 上一篇文章Android 利用V4L2 调用camera介绍了使用V4L2 接口预览camera的基本方法.目前接触过的usb camera支持的图像格式基本上只包括3种: YUV MJPEG ...

  2. Android编程中调用Camera时预览画面有旋转问题的解决方法

    本文实例讲述了Android编程中调用Camera时预览画面有旋转问题的解决方法.分享给大家供大家参考,具体如下: 在调用Camera写应用的时候,前后摄像头的情况有时候是不一样的.有时候,明明后摄像 ...

  3. android利用反射调用截屏api,Android利用反射机制调用截屏方法和获取屏幕宽高的方法...

    想要在应用中进行截屏,可以直接调用 View 的 getDrawingCache 方法,但是这个方法截图的话是没有状态栏的,想要整屏截图就要自己来实现了. 还有一个方法可以调用系统隐藏的 screen ...

  4. Android开发:调用camera API 创建MediaRecorder

    本文出自 "whithin's" 博客,请务必保留此出处http://whithin.blog.51cto.com/690417/1063573 1. add  below thr ...

  5. android 相机故障 代码 解决,android – 如何在调用Camera时解决ANR错误?

    我在主菜单中有两个按钮.我按下第一个按钮时调用相机.在这里,我没有遇到任何问题.相机正常工作.拍完照片后,我回到主菜单,再次按下第一个按钮.我在这里得到了这个问题.相机正确调用.但是当我拍照时,我得到 ...

  6. Unity Vuforia 之 简单实现Vuforia调用Android设备外接摄像头camera实现AR

    Unity Vuforia 之 简单实现Vuforia调用Android设备外接摄像头camera实现AR 目录 Unity Vuforia 之 简单实现Vuforia调用Android设备外接摄像头 ...

  7. Android源码之Camera系统架构

    2019独角兽企业重金招聘Python工程师标准>>> https://blog.csdn.net/ljsbuct/article/details/7094670 https://w ...

  8. Android -- 利用Broadcast开启Service

    Broadcast和Service都是Android四大组建之一的. 这里的广播是动态的,自己注册的一个广播. 这种最典型的用法就是利用开机广播,然后再起自己的服务,也就是在Android手机中做到开 ...

  9. android编程 自动裁剪图片,Android编程实现调用相册、相机及拍照后直接裁剪的方法...

    本文实例讲述了Android编程实现调用相册.相机及拍照后直接裁剪的方法.分享给大家供大家参考,具体如下: package com.cvte.health.phone; import java.io. ...

最新文章

  1. ubuntu/debian/centos/rhel使用镜像源一键安装gitlab-ce服务
  2. Dubbo 迈出云原生重要一步 - 应用级服务发现解析
  3. Swift - 重写导航栏返回按钮
  4. NOIP2008 普及组T4 立体图 解题报告-S.B.S.(施工未完成)
  5. MC缓存序列化php,PHP serialize()序列化的使用
  6. Total Physical Response TPR
  7. [SQL实战]之从titles表获取按照title进行分组
  8. CSS3---渲染属性
  9. 想将有色彩的视频进行去色处理就这样做
  10. python爬虫淘宝视频_python爬虫视频教程:一篇文章教会你用Python爬取淘宝评论数据...
  11. 飞行堡垒fx80g拆卸电源_集成度较高 华硕飞行堡垒FX50拆机解析
  12. 使用DOSBox自动进入debug模式
  13. ★用辩证数学解答“缸中之脑”
  14. java面向对象基础练习1(坐标点移动)
  15. OpenFlow网络基本介绍
  16. 2012服务器如何做网站,做网站你真的就那么急吗?
  17. puppet重申证书
  18. git clone 出现fatal: unable to access ‘https://github.com/xxx: Failed to connect to github.com
  19. 组合框快速输入(二)
  20. RTX 51 FULL源码学习(2.1)

热门文章

  1. 如何运用Microsoft Office Project 2003来做项目计划
  2. 2022-2028年全球与中国汽车自动变速箱控制单元产业市场前瞻与投资战略规划分析
  3. js html等比例放大后生成图片 html2canvas
  4. Linux ubuntu 装openCV,Linux(ubuntu 16.04) 安装 opencv C++
  5. java 几种生成海报的方式
  6. 微信公众平人数多服务器会崩溃吗,微信公众号平台推新功能 再也不怕文章崩溃...
  7. MES系统功能在石油化工行业的应用
  8. 华为机试真题 C++ 实现【最短木板长度】【100%通过率】【2022.11 Q4 新题】
  9. MSDC 4.3 接口规范(24)
  10. ChatGLM-6B 安装试用