Android平台之不预览获取照相机预览数据帧及精确时间截
在android平台上要获取预览数据帧是一件极其容易的事儿,但要获取每帧数据对应的时间截并不那么容易,网络上关于这方面的资料也比较少。之所以要获取时间截,是因为某些情况下需要加入精确时间轴才能解决问题,如果自己给获取到的时间截打上时间截,则必定引入很多误差,文档主要以理论为主,我想作为一名合格的程序员,有了一个想法,则一定会有办法去编码实现的。
因为项目需要,查找了大量的资料,发现网络上关于获取预览数据的资料都是通过实现PreviewCallback接口来获取。这种方法能获取到照相机的预览数据,但是系统不提供时间截服务,自己打上时间截,可能会导致预览数据帧发生时间截偏移。具体分析来说,如果你实现了PreviewCallback接口,调用setPreviewDisplay使用一个SurfaceHolder来显示预览数据,并且在onPreviewFrame回调函数中获取到了帧数据,但是你不能获取该帧产生时的时间截。你需要为他手工编码打上时间截,或许你就是以程序运行到那行代码时刻的时间截,但在帧生成到调用回调onPreviewFrame,再到运行该行代码,已经消耗了一些时间,如果你的预览频率设置不当的话,会使得消耗是时间是你设定的预览帧间隔的几倍,这样误差可能就导致了错误的时间截。
使用PreviewCallback和SurfaceView来获取预览数据的方法,还有很大的问题就是你必须把预览得到的数据显示出来,才能在onPreviewFrame回调函数中获取到数据。官方API是这么解释的,但这一点在2.3及以前版本的android中并不一定成立,因为我已经在2.3,2.2的系统中测试关闭输出,仍然能在onPreviewFrame中获取数据,但同样的方法在4.0以上的系统中则不可以。获取你可能会问,为什么要关闭预览输出?这个问题可能会有各种答案,但很明显的是它可以明显减少系统资源的消耗,从而可以使得照相机Camera能够以更大的预览频率输出。那么,怎么样才能使得高版本的android在不显示预览的情况下也能获得预览数据呢?这种情况下,一个叫SurfaceTexture的类登场了。
SurfaceTexture是直接继承自Object类, 最低版本api 11,关于SurfaceTexture的介绍,官方是这么介绍的——"A Surface created from a SurfaceTexture can be used as an output destination for the android.hardware.camera2, MediaCodec, MediaPlayer, and Allocation APIs."和"A SurfaceTexture may also be used in place of a SurfaceHolder when specifying the output destination of the older Camera API. Doing so will cause all the frames from the image stream to be sent to the SurfaceTexture object rather than to the device's display."。也就是说SurfaceTexture可以作为视频或图像流的输出载体。说明一下,因为android5.0的推出,要废弃Camera类,使用Camera2来替代,所以说"older Camera API"。总之,如果你使用SurfaceTexure来作为Camera的输出载体(调用Camera的setPreviewTexture即可把生成的SurfaceTexture对象设置了输出载体),那么就可以把SurfaceTexture作为预览数据缓存地方,而不用再屏幕上显示出来,显然你要为设置一个足够大的缓存区域。有了SurfaceTexture,那么接下来的工作就变得容易多了,下面说说本文提到的另一个重点就是获取到精确的时间截。
查阅SurfaceTexture类API就会发现它还提供了一个getTimeStamp()函数,官方介绍"Retrieve the timestamp associated with the texture image set by the most recent call to updateTexImage.",也就是说它可以获得SurfaceTexture最新数据帧的时间截,但在这之前需要调用updateTexImage()更新数据,另外getTimeStamp返回值的单位是纳秒。而updateTexImage()的调用对SurfaceTexture有要求,必须把SurfaceTexture设置为 GL_TEXTURE_EXTERNAL_OES texture 类型。可以这样编写代码:
surfaceTexture = new SurfaceTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);
使用SurfaceTexture获取预览数据也是要实现PreviewCallback,方法同前文提到的PreviewCallback获取预览数据,不同的是在startPreview之前,不再调用setPreviewDisplay,而是使用Camera的setPreviewTexture。
int version = android.os.Build.VERSION.SDK_INT;
if (version >= OSSURPORTFORSURFACETEXTURE) {
try {
camera.setPreviewTexture(surfaceTexture);
int buffersize = WIDTH_COLLECT * HEIGHT_COLLECT
* ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8;
previewBuffer = new byte[buffersize];
camera.addCallbackBuffer(previewBuffer);
camera.setPreviewCallbackWithBuffer(this);
camera.startPreview();
isView = true;
} catch (IOException e) {
camera.release();
camera = null;
e.printStackTrace();
}
} else {
......
}
另外还要主要的就是要记得onPreviewFrame回调函数中添加addCallbackBuffer调用,不然缓存不会自动更新,就不能获取到后续的数据帧;要获取精确时间截(这里说精确,是因为这个时间截是系统在数据发送到SurfaceTexture时设置的,非常接近预览数据生成的时间,要远比手工在onPreviewFrame中打上数据的时间截准确),还要调用updateTexImage()。可以像下面这样编写程序:
long timestampx;
if (osversion >= OSSURPORTFORSURFACETEXTURE) {
surfaceTexture.updateTexImage();
timestampx = surfaceTexture.getTimestamp()/1000000;
camera.addCallbackBuffer(previewBuffer);
}
上述代码是写在onPreviewFrame回调函数中的,有一个值得注意的地方是不要在onPreviewFrame中做耗时的工作,因为那么极可能会导致丢掉一些预览数据帧。
通过上面的方法,已经可以在不显示预览的情况下获取到数据帧,并打上极为精确的生成时间截,这对于需要精确计算时间的程序来说是非常有用的。当然是用SurfaceTexture也可以将预览图像显示出来,你可以开一个线程专门来做这件事,而不是在onPreviewFrame中完成。下面提供一段显示预览图像的参考代码:
try {
YuvImage image = new YuvImage(data, ImageFormat.NV21,
WIDTH_COLLECT, HEIGHT_COLLECT, null);
if (image != null) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, WIDTH_COLLECT,
HEIGHT_COLLECT), 100, stream);
Bitmap bm = BitmapFactory.decodeByteArray(
stream.toByteArray(), 0, stream.size());
stream.close();
Canvas canvas = previewHolder.lockCanvas();
canvas.drawBitmap(bm, 0, 0, null);
previewHolder.unlockCanvasAndPost(canvas);
}
} catch (Exception e) {
// TODO: handle exception
}
previewHolder就是要显示预览数据的SurfaceView的SurfaceHolder,当然你要可以加上synchronized同步机制。
Demo就没有上传了,如果有什么问题可以直接留言讨论。虽然写得比较水,欢迎复制粘贴,转载请带上原文地址:http://www.cnblogs.com/HackingProgramer/p/4062119.html
Android平台之不预览获取照相机预览数据帧及精确时间截的更多相关文章
- 基于ANDROID平台,U3D对蓝牙手柄键值的获取
对于ANDROID平台,物理蓝牙手柄已被封装,上层应用不可见,也就是说对于上层应用,不区分蓝牙手柄还是其它手柄: 完成蓝牙手柄和ANDROID手机的蓝牙连接后,即可以UNITY3D中获取其键值: 在U ...
- android平台获取手机IMSI,IMEI ,序列号,和 手机号的方法
1)获取运营商sim卡imsi号, String IMSI =android.os.SystemProperties.get( android.telephony.TelephonyPropertie ...
- Android平台的Swift—Kotlin
WeTest 导读 Kotlin 已经出来较长一段时间了,有些同学已经对Kotlin进行了深入的学习,甚至已经运用到了自己的项目当中,但是还有较多同学可能只是听过Kotlin或简单了解过,这篇文章的目 ...
- Android平台上最好的几款免费的代码编辑器
使用正确的开发工具能够快速有效地完成源代码的编写和测试,使编程事半功倍.在网络信息高速发展的今天,移动设备的方便快捷已经深入人心,越来越多的程序员会选择在任何感觉舒适的地方使用移动设备查看或者编辑源代 ...
- Android平台Camera实时滤镜实现方法探讨(三)--通过Shader实现YUV转换RBG
http://blog.csdn.net/oshunz/article/details/50055057 文章例如该链接通过将YUV分成三个纹理,在shader中取出并且经过公式变换,转换成RGB.我 ...
- Android平台的开发环境的发展演变
因为之前学习java语言的时候安装过了eclipse,所以想在eclipse上搭建android平台,在参照知乎上大神们的意见,发现了AS强大的代码提示.实时预览和搜索匹配等出色功能,最后还是选择在A ...
- 【转】Android平台下利用zxing实现二维码开发
http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...
- Android平台下利用zxing实现二维码开发
Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平 ...
- Android平台介绍
一.Android平台介绍 什么是智能手机 具有独立的操作系统,独立的运行空间,可以由用户自行安装软件.游戏.导航等第三方应用程序,并可以通过移动通讯网络来实现无线网络接入的手机类型总称. 智能手机操 ...
随机推荐
- Delphi 函数指针(函数可以当参数)
首先学习: 指向非对象(一般的)函数/过程的函数指针 Pascal 中的过程类型与C语言中的函数指针相似,为了统一说法,以下称函数指针.函数指针的声明只需要参数列表:如果是函数,再加个返回值.例如声明 ...
- redis maxmemory设置
关于maxmemory的设置,如果redis的应用场景是作为db使用,那不要设置这个选项,因为db是不能容忍丢失数据的. 如果作为cache使用,则可以启用这个选项(其实既然有淘汰策略,那就是cach ...
- docker_openwrt
http://wiki.openwrt.org/doc/howto/docker_openwrt_image https://www.baidu.com/s?wd=lxc%20docker&r ...
- (2) 假设字符串类似这样的aba和aab就相等,现在随便给你二组字符串,请编程比较他们看是否相等
/** * 第一种方式: * 实现思路:将字符串通过getBytes方法转换为byte数组,或者通过toCharArray()转换为char数组 * 然后先调用Arrays的sort方法进行排序,再调 ...
- SIP学习之网络链接
风清扬的CSDN博客 文章分类SIP http://blog.csdn.net/ppy521/article/category/1227390
- vim编辑器的设置文件
vim配置特点: 1.按F5可以直接编译并执行C.C++.java代码以及执行shell脚本,按“F8”可进行C.C++代码的调试 2.自动插入文件头 ,新建C.C++源文件时自动插入表头:包括文件名 ...
- Hive 1、什么是Hive,Hive有什么用
一.什么是Hive Hive是建立在 Hadoop 上的数据仓库基础构架.它提供了一系列的工具,可以用来进行数据提取转化加载(ETL),这是一种可以存储.查询和分析存储在 Hadoop 中的大规模数据 ...
- NetAnalyzer笔记 之 二. 简单的协议分析
[创建时间:2015-08-27 22:15:17] NetAnalyzer下载地址 上篇我们回顾完了NetAnalyzer一些可有可无的历史,在本篇,我决定先不对NetAnalyzer做介绍,而是先 ...
- itoa的源代码实现
由于通过socket传递数据的时候,仅仅能够通过字符串类型,可是,当我们要传递的数据是整型的是,应该怎么办呢?本来我想着使用for循环,可是,总感觉太麻烦了,后来别人告诉我能够使用itoa,以下是it ...
- [Err] 1449 - The user specified as a definer ('admin_isbox'@'localhost') does not exist
晚上加班调用一个远程拷贝的本地Mysql的储存过程,报错:[Err] 1449 - The user specified as a definer ('admin_isbox'@'localhost' ...