usb相机的经验总结

来源:互联网 发布:主人网络和访客网络 编辑:程序博客网 时间:2024/06/05 17:19

有一段时间没更新了。。。深表遗憾啊!最近接手了一个新的项目,基本上都是从头开始写源代码,所以进度慢效率也一般。新项目的第一个模块就是关于usb采集视频数据的,目前基本完成了对本模块的代码编写。在此总结一下心路历程(默认你对ndk linux命令 和 jni有一定的认识)。

http://blog.csdn.net/mirkerson/article/details/50764314#reply 这个是我刚接触usb相机对我有极大帮助的文章,里面也有相应的源码。对相机的操作是通过底层的C实现的,ndk-build编译成库,Android通过jni进行调用,主要说一下在此期间遇到的一些问题吧

1 .怎么跑起来的?
下载好的源码是在Android Studiod上面的运行的,本人习惯了eclipse所以直接将代码改成了自己需要的(看个人的习惯),同时 我连接设备是通过无线的方式(这么高大上你竟然不知道。。。),保证电脑和设备在同一个网段,在电脑cmd下运行 adb connect 设备的IP 回车即可(期间出现过一个问题 死后连不上 后来是你的设备开发者选项开了吗?) 连接成功之后 cmd下接着运行 adb shell 回车进入设备 ,接着执行cd /dev 到dev目录下执行ls -l 插上相机会出现videoX(X代表一个数字,如果前面有一个类似的设备那么X就是1,这个数字很重要,因为它代表的是哪一个相机),usb相机作为一种即插即用的设备,需要赋予权限的。如果直接运行程序,安装什么的都是顺利的,会报一个 Permit…什么的错误,熟悉linux系统的应该知道是权限问题,所以在命令行执行 chmod 777 /dev/videoX 就可以了。

如果报一个busy什么的,应该是你选择的相机资源被占用了,你应该选择你信插上的相机,根据刚才的相机数字修改代码CameraPreview里面的private int cameraId=X; 现在应该是可以跑起来的(可能我运气好吧,手上的相机支持 V4L2_PIX_FMT_YUYV格式吧),如果还是无法运行,报一个VIDIOC_S_FMT的错误,接着往下看吧。。。

2 .相机的格式支持
接下来的操作涉及的是底层c和ndk-build这块(java代码少 ,我也没有遇到java这块的问题)。出现上述的问题是因为c代码写好了支持的帧格式是 V4L2_PIX_FMT_YUYV ,而你的相机刚好不支持,所以会报错。c里面的那些函数不懂的话可以自行百度,网上有人说的很清楚。解决这个问题首先需要了解你的相机支持什么样的格式,在这里我贴上自己总结的几种打印方法,直接看java logcat的输出就可以了。

//打印设备支持的图像帧格式1.方法一struct v4l2_fmtdesc fmtdesc;CLEAR(fmtdesc);fmtdesc.index=0;fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;LOGE("Support format:\n");while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1){     LOGE("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);     fmtdesc.index++;}2.方法二int ret;ret = ioctl(fd, VIDIOC_G_FMT, &fmt);LOGI("pixelformat is '%c%c%c%c'\n",fmt.fmt.pix.pixelformat & 0xFF,     (fmt.fmt.pix.pixelformat >> 8) & 0xFF, (fmt.fmt.pix.pixelformat >> 16) & 0xFF,     (fmt.fmt.pix.pixelformat >> 24) & 0xFF);LOGI("fmt.fmt.pix.field is %d", fmt.fmt.pix.field);

知道支持就可以直接设置了,当然你的程序还是一样的不可以运行,因为后面的处理函数还是针对YUYV格式的,我只和接触了一个相机只支持mjpeg的格式,源代码

void processimage (const void *p){    yuyv422toABGRY((unsigned char *)p);}

也没法处理相机采集的数据,如果你还感兴趣 接着来

3 . mjpeg的解压缩
由于采集的数据是不相同的所以就需要重新写处理函数,把yuyv422toABGRY((unsigned char *)p);改成我们的想要的就可以,其他的可以不需要进行大的改动。首先相机采集的一帧jpeg格式的数据大小是 width*height*2(打印出来计算一下就会知道),jpeg是一种压缩封装的格式,使用的话就需要进行解压缩,解压缩之后的大小是width*height*3 这些对于数据的存储很重要。解压缩的话 http://blog.sina.com.cn/s/blog_643634b80101hzi8.html 这篇文章有很大的帮助(对代码的结构也有很大的帮助,我相信你看了也会按照他的方法在自己项目的jni根目录下新建一个jpeg的目录),我当时就是先下载了libjpeg的源码然后根据这篇文章把用到的C文件一个个拷贝到我的项目文件夹下重新编译成功的。编译通过之后jpeg解压缩需要的函数就有了,接下来就是如何在ImageProc.c里面进行改写了, 在v4l2读出的帧中找到SOF0(Start Of Frame 0),插入个huffman表就可以用libjpeg解码成rgb。可以参考mjpg-streamer中input_uvc目录下的代码,或者我刚调试好的 :https://github.com/joeshang/joycar/blob/master/module/v4l2_camera/decoder_mjpeg.c 只需要一下3个函数,用于解压缩就可以了

is_huffman(unsigned char *buf)decoder_jpeg_decompress(unsigned char *out_buf,     unsigned char *jpeg_buf,int jpeg_size)static void decoder_mjpeg_decode(这里我改了一点unsigned char *out_buf,     unsigned char *in_buf,int buf_size)

这些size就是我之前说的大小 你对号入座就可以。期间还会有一些乱七八糟的问题我也记不清楚了,编译说缺什么你需要在ImageProc.h里面添加相关的声明(前提你需要懂一点C),问题最多的是decoder_jpeg_decompress这个函数,这个可以网上搜索jpeg解压缩,千篇一律都一样的,说的很清楚,应该可以搞定。
如果搞定了,我再给你一个测试程序

//将jpeg图像刘保存成本地.jpg图片FILE *fp = NULL;char str[1000];sprintf(str, "/sdcard/img/test%d.jpg", img_index++);fp = fopen(str, "w");if(fp != NULL){    fwrite(buffers[buf.index].start, 1,buffers[buf.index].length, fp);//buffers[buf.index].length换成buf.bytesused就行了    sync();    fclose(fp);}

这个是检测相机获取的jpeg格式的数据对不对,可以直接保存为本地图片,方便检查

//将rgb24数据保存为bmpint jjk = 0;//width:图像宽; height:图像高;rgbbuf:图像RGB数据;fileName:保存文件名;void MySaveBmp(unsigned char *rgbbuf,int width,int height) { LOGI(" ......from MySaveBmp.....");BITMAPFILEHEADER bfh;  BITMAPINFOHEADER bih;  /* Magic number for file. It does not fit in the header structure due to alignment requirements, so put it outside */  unsigned short bfType=0x4d42;             bfh.bfReserved1 = 0;  bfh.bfReserved2 = 0;  bfh.bfSize = 2+sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+width*height*3;  bfh.bfOffBits = 0x36;  bih.biSize = sizeof(BITMAPINFOHEADER);  bih.biWidth = width;  bih.biHeight = height;  bih.biPlanes = 1;  bih.biBitCount = 24;  bih.biCompression = 0;  bih.biSizeImage = 0;  bih.biXPelsPerMeter = 5000;  bih.biYPelsPerMeter = 5000;  bih.biClrUsed = 0;  bih.biClrImportant = 0;  char str[1000];sprintf(str, "/sdcard/img/test%d.bmp",jjk);// ffp = fopen(sstr, "w");//sprintf(str, "/sdcard/img/test0018.bmp");FILE *file = fopen(str, "wb");  if (!file)  {      printf("Could not write file\n");      return;  }  /*Write headers*/  fwrite(&bfType,sizeof(bfType),1,file);  fwrite(&bfh,sizeof(bfh),1, file);  fwrite(&bih,sizeof(bih),1, file);  fwrite(rgbbuf,IMG_WIDTH*IMG_WIDTH*3,1,file);  jjk++;fclose(file);  

}
//以下是.h文件的相关声明

typedef struct                       /**** BMP file header structure ****/      {      unsigned int   bfSize;           /* Size of file */  unsigned short bfReserved1;      /* Reserved */  unsigned short bfReserved2;      /* ... */  unsigned int   bfOffBits;        /* Offset to bitmap data */  } BITMAPFILEHEADER;typedef struct                       /**** BMP file info structure ****/  {  unsigned int   biSize;           /* Size of info header */  int            biWidth;          /* Width of image */  int            biHeight;         /* Height of image */unsigned short biPlanes;         /* Number of color planes */  unsigned short biBitCount;       /* Number of bits per pixel */  unsigned int   biCompression;    /* Type of compression to use */  unsigned int   biSizeImage;      /* Size of image data */  int            biXPelsPerMeter;  /* X pixels per meter */  int            biYPelsPerMeter;  /* Y pixels per meter */  unsigned int   biClrUsed;        /* Number of colors used */  unsigned int   biClrImportant;   /* Number of important colors */  } BITMAPINFOHEADER; 

解压缩之后得到的是rgb的数据了,具体的我没有深究是什么格式的,直接用上面的代码检测。如果解压缩得到得到的数据需要显示出来的话 接着来吧。。。

4 .数据的显示
得到的unsigned char *out_buf就是解压好的数据了,显示有两种方式:
可以直接把数据返回给java端 java端将数据转换为bmp格式在显示出来,也可以把数据在c层处理为int *rgb的数组,像源代码一样返回给java bitmap数据,怎么样都是很简单的啦。代码都有,稍作分析就可以

我支持第二张方式,不然第一种方式的延迟绝对会让你的老板不满意。。。

总结

这就是我开发相机模块遇到的一些问题啦,我是站在自己的立场上。首先有些问题时间长我已经记不住了,其次说的也有点啰嗦,最后没有考虑到你的立场等。如果有问题的话欢迎留言,我看的一定回复 大家一起交流学习一下!我是Mr.小艾