公司最近在做新款产品的时候,需要同时用到四路镜头,但是目前高通的开发板上只有三个口,故打算再加上一个USB镜头。于是翻看了一下android的USB镜头的使用,发现得自己写JNI代码的,特此记录一下。

大概流程如下:

打开USB镜头-> 获取USB镜头的信息并设置相应的属性->申请一个图像数据的缓冲区-> 开始捕获数据(让USB往缓冲区写数据)-> 循环从缓冲区获取一帧数据-> 关闭USB镜头

根据这个流程,为了方便理解,我们先定义调用jni层的class文件 然后按照顺序一步一步去实现

import android.util.Log;/*** Created by 601042 on 2018/2/28.*/public class NativeTest {static {System.loadLibrary("native-lib");}/*** 打开USB镜头** @return   0:成功* */public native int openCamera();/*** 获取USB镜头的信息并设置相应的属性** @return   0:成功* */public native String getDevicInfo();/*** 申请一个图像数据的缓冲区** @return   0:成功* */public native int getCache();/*** 开始捕获数据** @return   0:成功* */public native int startCapture();/*** 获取一帧数据** @return   0:成功* */public native int getOneFrame();/*** 关闭USB镜头** @return   0:成功* */public native int closeCamera();/*** 开启图像callback** @return   0:成功* */public  native void start();/*** 图像callback** @param data:一帧图像数据* @param length:一帧图像数据的长度* */public  void myCallback(byte[] data,int length) {Log.e("Test", "Callback: " + data.length + "   "+length);}}

现在先来实现第一步:打开USB镜头

linux是一个文件系统,外接设备也是以一个文件的形式存在,在/dev/目录下可以找到这些,我现在板上有三个镜头的插口,/dev/目录下则有video1、video2、video33、video34四个文件存在,我猜应该是支持四路相机的,只是系统层做了限制,只能同时开三路。现在将USB镜头接到板上,发现/dev/目录下多个video3,拔掉USB镜头又没了,故这个video3就是USB镜头,要打开这个镜头就跟打开一个文件是一样。

static int fd = -1;                        //镜头ID
static char *dev_name = "/dev/video3";     //镜头名/*************************************************
Function:       openUSBCamera
Description:    打开USB摄像头
*************************************************/int openUSBCamera() {struct stat st;//先判断该文件的状态if (-1 == stat(dev_name, &st)) {LOGE("Cannot identify '%s': %d, %s\n", dev_name, errno, strerror(errno));return -1;}//再判断该文件是不是设备if (!S_ISCHR(st.st_mode)) {LOGE("%s is not device/n", dev_name);return -1;}//打开设备fd = open(dev_name, O_RDWR /* required */| O_NONBLOCK, 0);//判断是否打开成功if (-1 == fd) {LOGI("Cannot open '%s': %d, %s\n", dev_name, errno, strerror(errno));return -1;}canCallback = true;//返回该设备的IDreturn fd;};extern "C"JNIEXPORT jint JNICALLJava_demo_xu_usbcanerademo_NativeTest_openCamera(JNIEnv *env, jobject instance) {// TODOreturn openUSBCamera();}

好了,到这里我们就打开USB镜头了。

接下来我们来获取USB镜头的信息并设置

/*************************************************
Function:       getInfo
Description:    获取USB摄像头的信息
Return:         设备信息
*************************************************/string getInfo() {//查看设备名称等信息struct v4l2_capability cap;ioctl(fd, VIDIOC_QUERYCAP, &cap);//把结果封装一下std::stringstream ss;ss << "DriverName:" << cap.driver << "     Card Name:" << cap.card << "     Bus info:"<< cap.bus_info << "     DriverVersion:" << ((cap.version >> 16) & 0XFF)<< ((cap.version >> 8) & 0XFF) << (cap.version & 0xff);//查看支持格式struct v4l2_fmtdesc fmtdesc;fmtdesc.index = 0;fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//获取支持的格式列表  并把结果封装一下ss << "     Supportformat:\n";while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) != -1) {//把结果封装一下ss << (fmtdesc.index + 1) << ":" << (fmtdesc.description) << "\n";fmtdesc.index++;}//如果需要设置的话 使用 ioctl(fd, VIDIOC_S_FMT, &fmtdesc);//查看当前帧的相关信息(长宽)struct v4l2_format fmt;fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;ioctl(fd, VIDIOC_G_FMT, &fmt);//把结果封装一下ss << "Currentdata format information:\ntwidth:" << fmt.fmt.pix.width << "     height:"<< fmt.fmt.pix.height;//如果需要设置的话 使用 ioctl(fd, VIDIOC_S_FMT, &fmt);return ss.str();}extern "C"JNIEXPORT jstring JNICALLJava_demo_xu_usbcanerademo_NativeTest_getDevicInfo(JNIEnv *env, jobject instance) {// TODOstring resule = getInfo();return env->NewStringUTF(resule.c_str());}

由于我的镜头没有支持多种格式或者属性,所以我就只获取一下信息,不进行设置。如果需要设置的话需要记住,一定要先获取设备支持的属性或格式列表,再从中获取到满足要求的设置进去,不然设置设备不支持的属性或者格式会无法正常工作的。
    接下来我们申请一个图形数据缓冲区

应用程序和设备有三种交换数据的方法,直接read/write ,内存映射(memorymapping) ,用户指针。我使用的是内存映射的方式。

/*************************************************
Function:       getCache
Description:    申请一个缓冲区(4帧) 并用buffers把指针存起来
Return:         结果
*************************************************/int getCache(){//定义buffer(缓冲区)的属性struct v4l2_requestbuffers req;//缓存多少帧req.count=4;//buffer的类型req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;//采用内存映射的方式req.memory=V4L2_MEMORY_MMAP;//申请缓冲区int result_getcache = ioctl(fd,VIDIOC_REQBUFS,&req);//如果为-1则说明申请失败if(result_getcache == -1){return -1;}//用buffer存储缓冲区的指针buffers =(buffer*)calloc (req.count, sizeof (*buffers));if (!buffers) {fprintf (stderr,"Out of memory/n");return -2;}//开始映射  四帧图像的区域都要for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {struct v4l2_buffer buf;memset(&buf,0,sizeof(buf));buf.type =V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory =V4L2_MEMORY_MMAP;buf.index =n_buffers;// 查询序号为n_buffers 的缓冲区,得到其起始物理地址和大小 -1则说明失败if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf)){return -3;}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 (MAP_FAILED== buffers[n_buffers].start){return -4;}}return 0;}extern "C"JNIEXPORT jint JNICALLJava_demo_xu_usbcanerademo_NativeTest_getCache(JNIEnv *env, jobject instance) {// TODOreturn getCache();}

缓冲区申请好了我们开始捕获数据(让设备往缓冲区里写数据)

/*************************************************
Function:       startCapture
Description:    开始捕获数据(数据会存入缓冲区)
Return:         结果
*************************************************/int startCapture(void) {unsigned int i;enum v4l2_buf_type type;//把四个帧放入队列for (i = 0; i < n_buffers; ++i) {struct v4l2_buffer buf;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;buf.index = i;//把帧放入队列if (-1 == ioctl(fd, VIDIOC_QBUF, &buf)){LOGE("VIDIOC_QBUF error %d, %s\n", errno, strerror(errno));return -1;}}//类型设置为捕获数据type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//启动数据流if (-1 == ioctl(fd, VIDIOC_STREAMON, &type)){LOGE("VIDIOC_STREAMON error %d, %s\n", errno, strerror(errno));return -2;}return 0;}extern "C"JNIEXPORT jint JNICALLJava_demo_xu_usbcanerademo_NativeTest_startCapture(JNIEnv *env, jobject instance) {// TODOreturn startCapture();}

开启了捕获之后,镜头就会一直往缓冲区里写数据了,数据的格式就是之前你设置的,比如我的就是YUV422(我没设置,因为我的镜头就只有这种格式的)。

现在我们要拿图像数据出来显示,就得从缓存区里拿数据,循环地从缓冲区里拿一帧帧的图像出来显示就是镜头的预览了。

我们先来讲讲如何冲缓冲区获取一帧图像数据出来

  struct buffer *buffers = NULL;              //图像数据void *framebuf = NULL;                      //一帧图像数据
/*************************************************
Function:       getOneFrame
Description:    从缓冲区获取一帧数据
Return:         结果
*************************************************/static int getOneFrame() {struct v4l2_buffer buf;unsigned int i;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;//从队列中取数据到缓冲区if (-1 == ioctl(fd, VIDIOC_DQBUF, &buf)) {LOGE("VIDIOC_DQBUF error %d , %s", errno, strerror(errno));}//assert(buf.index < n_buffers);if (buf.bytesused <= 0xaf) {/* Prevent crash on empty image */LOGI("Ignoring empty buffer ...\n");return -1;}oneFrameLength = buf.bytesused;framebuf = (void *) malloc(oneFrameLength);pthread_mutex_lock(&lock);//从视频数据copy到framebuf中memcpy(framebuf, buffers[buf.index].start, oneFrameLength);pthread_mutex_unlock(&lock);//填充队列if (-1 == ioctl(fd, VIDIOC_QBUF, &buf))LOGE("VIDIOC_QBUF error %d, %s", errno, strerror(errno));return 0;}extern "C"JNIEXPORT jint JNICALLJava_demo_xu_usbcanerademo_NativeTest_getOneFrame(JNIEnv *env, jobject instance) {// TODOreturn getOneFrame();}

这里使用到了一个自己定义结构体buffer

    struct buffer {void *start;size_t length;};

到这里我们就可以获取带USB镜头的一帧图像了,在demo,我使用了jni层回调java代码的方式,通过一个线程把循环从缓冲区里拿数据,然后调用java层的callback给传递出去。

/*************************************************
Function:       thread_entry
Description:    获取图像并callback到java层的线程
*************************************************/void *thread_entry(void* data){//获取全局的*env;JNIEnv *env;mjavaVM->AttachCurrentThread(&env,NULL);jclass clazz = env->GetObjectClass(mjavaobject);//获取到java的方法IDjmethodID mID = env->GetMethodID(clazz, "myCallback", "([BI)V");//开始while循环获取图像并callback出去,用canCallback这个变量控制是否停止while(canCallback) {if(env != NULL && mID != NULL && mjavaobject != NULL){struct v4l2_buffer buf;unsigned int i;memset(&buf, 0, sizeof(buf));buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;buf.memory = V4L2_MEMORY_MMAP;//从队列中取数据到缓冲区if (-1 == ioctl(fd, VIDIOC_DQBUF, &buf)) {LOGE("VIDIOC_DQBUF error %d , %s", errno, strerror(errno));usleep(50000);continue;}//判断获取到的数据是否有效assert(buf.index < n_buffers);if (buf.bytesused <= 0xaf) {LOGI("Ignoring empty buffer ...\n");usleep(50000);continue;}//记录下获取到数据的长度oneFrameLength = buf.bytesused;pthread_mutex_lock(&lock);//实例化一个数组jbyteArray temp_result = env->NewByteArray(oneFrameLength);//把获取到的图像数据赋值给数组env->SetByteArrayRegion(temp_result, 0, oneFrameLength,(jbyte *)buffers[buf.index].start);//调用java层的代码env->CallVoidMethod(mjavaobject, mID, temp_result,oneFrameLength);//销毁掉创建出来的两个临时变量env->DeleteLocalRef(temp_result);pthread_mutex_unlock(&lock);//填充队列if (-1 == ioctl(fd, VIDIOC_QBUF, &buf)){LOGE("VIDIOC_QBUF error %d, %s", errno, strerror(errno));usleep(50000);continue;}}else{LOGD("menv == NULL || obj == NULL || mID == NULL...\n");}//间隔50毫秒//单位:微秒   1000微秒=1毫秒usleep(50000);}//销毁掉mjavaVM->DetachCurrentThread();}extern "C"JNIEXPORT void JNICALLJava_demo_xu_usbcanerademo_NativeTest_start(JNIEnv *env, jobject instance) {// TODOenv->GetJavaVM(&mjavaVM);mjavaobject = env->NewGlobalRef(instance);pthread_t trecv;int result = pthread_create(&trecv,NULL,thread_entry,NULL);LOGD("result%d",result);}

拿到了数据,大家想用什么方法显示都可以,保存成文件也可以,这里就不多讲了。

最后一步就是你用完了就得关闭镜头

/*************************************************
Function:       closeUSBCamera
Description:    关闭USB摄像头
Return:         结果
*************************************************/int closeUSBCamera() {if(fd == -1){return 0;}//释放申请的缓冲unsigned int i;for (i = 0; i < n_buffers; ++i){if (-1 == munmap(buffers[i].start, buffers[i].length)){LOGE("munmap error %d , %s", errno, strerror(errno));}free(buffers);}canCallback = false;//关闭镜头return close(fd);};extern "C"JNIEXPORT jint JNICALLJava_demo_xu_usbcanerademo_NativeTest_closeCamera(JNIEnv *env, jobject instance) {// TODOreturn closeUSBCamera();}

好了,到这里基本整个USB相机的使用就讲完了。由于代码不多也不复杂,demo就不放了,毕竟现在下载都要积分不能免费了,如果实在是需要demo的话,私信联系我吧。

参考资料:https://blog.csdn.net/eastmoon502136/article/details/8190262

Andoid系统usb相机的使用方法相关推荐

  1. macos安装盘第三方工具制作_简单制作OSXYosemite10.10正式版U盘USB启动安装盘方法教程(全新安装Mac系统)下载|异次元软件世界...

    伴随着 iMac 5K Retina 和新的 Mac mini 等硬件的发布,苹果终于都推出了 OS X Yosemite 系统正式版了!相信很多人都已经用上.不过对于一些不想升级,而是打算「全新安装 ...

  2. linux内核不能识别u盘分区,一种在Linux内核中识别特定USB大容量存储设备的方法及系统与流程...

    本发明涉配usb设备识别技术领域,特别是涉及一种在linux内核中识别特定usb大容量存储设备的方法及系统. 背景技术: 在linux系统下对usb设备进行管控,一般而言有两种方法,一种是阻断新插入设 ...

  3. win7计算机usb解除禁用,win7系统USB接口被禁用了怎么办?win7USB被禁用后打开的方法教程...

    有时候我们为了防止别人插U盘在我们电脑上,我们会通过禁用usb来完成这个功能.那如果win7系统usb接口被禁用了我们又想使用该怎么办呢?下面雨林木风小编就来说说win7USB被禁用后打开的方法教程. ...

  4. 服务器系统通用串行总线控制器,win7系统usb设备不能用通用串行总线控制器无法启动的解决方法...

    很多小伙伴都遇到过win7系统usb设备不能用通用串行总线控制器无法启动的困惑吧,一些朋友看过网上零散的win7系统usb设备不能用通用串行总线控制器无法启动的处理方法,并没有完完全全明白win7系统 ...

  5. 海康工业相机USB相机问题排查思路—Windows 系统

    海康机器视觉Visionmaster-缺失检测 ​第一步骤:固件版本 a) 查看相机固件版本 b) 若较老建议升级成最新固件后再测试异常是否消失. 注意切勿跨大版本升级: 第二步骤:驱动排查 查看客户 ...

  6. xp系统usb android,xp系统usb手机网络共享怎么设置,xp设置usb手机网络分享的方法

    随着智能手机的不断普及,如今通过USB数据线将手机与计算机绑定,从而实现计算机共享手机的互联网连接早已成为现实.但于对于Android类型的智能手机而言,通常情况下支持的绑定只限于Windows Vi ...

  7. Win10系统无法识别USB设备的处理方法

    Win10系统提示无法识别USB设备,导致U盘.移动硬盘等无法使用,甚至连本来正常连接的USB鼠标都可能发生异常,这严重影响了Win10系统的使用.下面为给大家带来解决办法,希望可以有帮助. 工具/原 ...

  8. usb相机的经验总结

    有一段时间没更新了...深表遗憾啊!最近接手了一个新的项目,基本上都是从头开始写源代码,所以进度慢效率也一般.新项目的第一个模块就是关于usb采集视频数据的,目前基本完成了对本模块的代码编写.在此总结 ...

  9. Ubuntu下如何获取usb相机的PID/VID并打开指定的相机

    项目需求控制和打开两个USB摄像头,并且根据相机的PID和VID来打开指定的相机,来区分主副相机,在Windows下可以通过AForge.Video.DirectShow库来实现,但是Ubuntu下没 ...

最新文章

  1. 焦点轮播图——myfocus焦点图库
  2. 掌握这些!让Python不再从入门到放弃,初学者容易忽略的一些细节
  3. HDU1054+最小顶点覆盖
  4. UBUNTU : Destination Host Unreachable
  5. Coursera自动驾驶课程第15讲:GNSS and INS Sensing for Pose Estimation
  6. python列表添加元素的三种方法定义集合数据对象_(1) List,tuple,dictionary,Python语法基础集,一,之,列表,元组,字典,集合...
  7. 湖北网络安全的产业机遇在哪里
  8. 装好XP,打开SATA开机蓝屏解决方法
  9. 《Web Load Testing For Dummie》读书笔记
  10. 跳转外部地址 带header_微信公众号如何加入超链接?个人订阅号实现点击跳转链接的方法!...
  11. linux 生成p12证书,Linux下使用openssl制作CA及证书颁发
  12. Sentaurus TCAD SDevice 实例教程
  13. 入门篇——解析Python机器学习中三类无监督学习算法和两个应用实例
  14. 经典论文阅读笔记——VIT、Swin Transformer、MAE、CILP
  15. python中的 zip函数详解
  16. 二维Poisson方程五点差分格式及简单求解方法Python实现
  17. 坚果PRO3搭载Android,安卓 10 来了,坚果 Pro 3 推送 Smartisan OS v7.5.0 早期众测版
  18. 方舟服务器显示等待发布,《明日方舟》开服既炸服的这波操作《方舟生存进化》永远也学不会...
  19. 使用word2vec训练词向量
  20. 计算机主机无信号输出,显示器没信号不显示但电脑主机工作正常的问题

热门文章

  1. 忽略属性 @JsonIgnore、@JsonIgnoreProperties
  2. 电路交换和分组交换的区别
  3. 基于 SpringMvc kisso 的 sso 演示 demo
  4. 新媒体的本质:大数据
  5. 连续三天熬夜游戏建模师终于出手,“疯狂”让老板加薪
  6. 第十二节段 -- 爬虫10:【Scarpy 框架04:练习】
  7. JS-抽象工厂模式小结
  8. 软件测试面试英文自我介绍,软件测试英文面试自我介绍
  9. Spine 由PSD导入
  10. go 字符串的高效拼接