一直想自己写一个从JNI层到应用层的Camera例子,android4.0上usb camera用不了 所以决定自己写一个 usb camera和coms原理都是一样的 基本v4l2 只不过源码数据格式不一样而已 下面我们就从JNI层开始
以下是我的代码,先上代码在 一步步说明
fimcgzsd.c
/* * Android USB Camera zc3xx Library * * Copyright (c) 2014 Store information technology guangzhou ltd<http://www.storeinf.com> * Copyright (c) 2014 hclydao <[email protected]> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. */ #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <asm/types.h> #include <linux/videodev2.h> #include <sys/mman.h> #include <string.h> #include <malloc.h> #include <linux/fb.h> #include <jni.h> #include <string.h> #include <android/log.h> #include <syslog.h> #define LOG_TAG "FimcGzsd" #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__) #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__) #define LOGW(...) __android_log_print(ANDROID_LOG_WARN , LOG_TAG, __VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR , LOG_TAG, __VA_ARGS__) struct fimc_buffer { unsigned char *start; size_t length; }; static int fd = -1; struct fimc_buffer *buffers=NULL; struct v4l2_buffer v4l2_buf; static int bufnum = 1; static int mwidth,mheight; /* *open usb camera device */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_open(JNIEnv * env, jclass obj, const jbyteArray devname) { jbyte *dev = (jbyte*)(*env)->GetByteArrayElements(env, devname, 0); fd = open(dev, O_RDWR, 0); if (fd<0) { LOGE("%s ++++ open error\n",dev); return -1; } (*env)->ReleaseByteArrayElements(env, devname, dev, 0); return fd; } /* * init device */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_init(JNIEnv * env, jclass obj, jint width, jint height,jint numbuf) { int ret; int i; bufnum = numbuf; mwidth = width; mheight = height; struct v4l2_format fmt; struct v4l2_capability cap; ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { LOGE("%d :VIDIOC_QUERYCAP failed\n",__LINE__); return -1; } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { LOGE("%d : no capture devices\n",__LINE__); return -1; } memset( &fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { LOGE("++++%d : set format failed\n",__LINE__); return -1; } struct v4l2_requestbuffers req; req.count = numbuf; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_REQBUFS, &req); if (ret < 0) { LOGE("++++%d : VIDIOC_REQBUFS failed\n",__LINE__); return -1; } buffers = calloc(req.count, sizeof(*buffers)); if (!buffers) { LOGE ("++++%d Out of memory\n",__LINE__); return -1; } for(i = 0; i< bufnum; ++i) { memset(&v4l2_buf, 0, sizeof(v4l2_buf)); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = V4L2_MEMORY_MMAP; v4l2_buf.index = i; ret = ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf); if(ret < 0) { LOGE("+++%d : VIDIOC_QUERYBUF failed\n",__LINE__); return -1; } buffers[i].length = v4l2_buf.length; if ((buffers[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset)) < 0) { LOGE("%d : mmap() failed",__LINE__); return -1; } } return 0; } /* *open usb camera device */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_streamon(JNIEnv * env, jclass obj) { int i; int ret; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; for(i = 0; i< bufnum; ++i) { memset(&v4l2_buf, 0, sizeof(v4l2_buf)); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = V4L2_MEMORY_MMAP; v4l2_buf.index = i; ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buf); if (ret < 0) { LOGE("%d : VIDIOC_QBUF failed\n",__LINE__); return ret; } } ret = ioctl(fd, VIDIOC_STREAMON, &type); if (ret < 0) { LOGE("%d : VIDIOC_STREAMON failed\n",__LINE__); return ret; } return 0; } /* *get one frame data */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_dqbuf(JNIEnv * env, jclass obj,const jbyteArray videodata) { int ret; jbyte *data = (jbyte*)(*env)->GetByteArrayElements(env, videodata, 0); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_DQBUF, &v4l2_buf); if (ret < 0) { LOGE("%s : VIDIOC_DQBUF failed, dropped frame\n",__func__); return ret; } memcpy(data,buffers[v4l2_buf.index].start,buffers[v4l2_buf.index].length); (*env)->ReleaseByteArrayElements(env, videodata, data, 0); return v4l2_buf.index; } /* *put in frame buffer to queue */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_qbuf(JNIEnv * env, jclass obj,jint index) { int ret; v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = V4L2_MEMORY_MMAP; v4l2_buf.index = index; ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buf); if (ret < 0) { LOGE("%s : VIDIOC_QBUF failed\n",__func__); return ret; } return 0; } /* *streamoff */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_streamoff(JNIEnv * env, jclass obj,jint index) { enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int ret; ret = ioctl(fd, VIDIOC_STREAMOFF, &type); if (ret < 0) { LOGE("%s : VIDIOC_STREAMOFF failed\n",__func__); return ret; } return 0; } /* *release */ JNIEXPORT jint JNICALL Java_com_dao_usbcam_Fimcgzsd_release(JNIEnv * env, jclass obj) { enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; int ret; int i; ret = ioctl(fd, VIDIOC_STREAMOFF, &type); if (ret < 0) { LOGE("%s : VIDIOC_STREAMOFF failed\n",__func__); return ret; } for (i = 0; i < bufnum; i++) { ret = munmap(buffers[i].start, buffers[i].length); if (ret < 0) { LOGE("%s : munmap failed\n",__func__); return ret; } } free (buffers); close(fd); return 0; }
首先是open这个就不作说明了
第二初始化init函数
ret = ioctl(fd, VIDIOC_QUERYCAP, &cap); if (ret < 0) { LOGE("%d :VIDIOC_QUERYCAP failed\n",__LINE__); return -1; } if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { LOGE("%d : no capture devices\n",__LINE__); return -1; }获取设备相关信息,检查是否支持capture模式
memset( &fmt, 0, sizeof(fmt)); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565; fmt.fmt.pix.width = width; fmt.fmt.pix.height = height; if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) { LOGE("++++%d : set format failed\n",__LINE__); return -1; }设置格式,usb camera获取到的已经是jpeg格式 所以这里设置成RGB565格式
struct v4l2_requestbuffers req; req.count = numbuf; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; ret = ioctl(fd, VIDIOC_REQBUFS, &req); if (ret < 0) { LOGE("++++%d : VIDIOC_REQBUFS failed\n",__LINE__); return -1; }申请缓冲区,这里申请numbuf个缓冲帧
buffers = calloc(req.count, sizeof(*buffers)); if (!buffers) { LOGE ("++++%d Out of memory\n",__LINE__); return -1; } for(i = 0; i< bufnum; ++i) { memset(&v4l2_buf, 0, sizeof(v4l2_buf)); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = V4L2_MEMORY_MMAP; v4l2_buf.index = i; ret = ioctl(fd , VIDIOC_QUERYBUF, &v4l2_buf); if(ret < 0) { LOGE("+++%d : VIDIOC_QUERYBUF failed\n",__LINE__); return -1; } buffers[i].length = v4l2_buf.length; if ((buffers[i].start = (char *)mmap(0, v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, v4l2_buf.m.offset)) < 0) { LOGE("%d : mmap() failed",__LINE__); return -1; } }映射虚拟内存到物理地址,获取每个缓冲区的物理地址
streamon函数
将缓冲区放入队列并开启数据流
dqbuf函数
获取一帧数据 返回当前缓冲区的序列号
qbuf函数
将指定缓冲区放入队列,获取到某一缓冲区的数据后需要重新将这个缓冲区放入队列
后面两个我就不多说明了
Android.mk文件:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := eng LOCAL_SRC_FILES:= fimcgzsd.c LOCAL_MODULE := libfimcgzsd LOCAL_LDLIBS := -llog LOCAL_SHARED_LIBRARIES := libc libcutils include $(BUILD_SHARED_LIBRARY)
参考博文:http://blog.csdn.net/eastmoon502136/article/details/8190262
============================================
作者:hclydao
http://blog.csdn.net/hclydao
版权没有,但是转载请保留此段声明
============================================