在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平台之不预览获取照相机预览数据帧及精确时间截的更多相关文章

  1. 基于ANDROID平台,U3D对蓝牙手柄键值的获取

    对于ANDROID平台,物理蓝牙手柄已被封装,上层应用不可见,也就是说对于上层应用,不区分蓝牙手柄还是其它手柄: 完成蓝牙手柄和ANDROID手机的蓝牙连接后,即可以UNITY3D中获取其键值: 在U ...

  2. android平台获取手机IMSI,IMEI ,序列号,和 手机号的方法

    1)获取运营商sim卡imsi号, String IMSI =android.os.SystemProperties.get( android.telephony.TelephonyPropertie ...

  3. Android平台的Swift—Kotlin

    WeTest 导读 Kotlin 已经出来较长一段时间了,有些同学已经对Kotlin进行了深入的学习,甚至已经运用到了自己的项目当中,但是还有较多同学可能只是听过Kotlin或简单了解过,这篇文章的目 ...

  4. Android平台上最好的几款免费的代码编辑器

    使用正确的开发工具能够快速有效地完成源代码的编写和测试,使编程事半功倍.在网络信息高速发展的今天,移动设备的方便快捷已经深入人心,越来越多的程序员会选择在任何感觉舒适的地方使用移动设备查看或者编辑源代 ...

  5. Android平台Camera实时滤镜实现方法探讨(三)--通过Shader实现YUV转换RBG

    http://blog.csdn.net/oshunz/article/details/50055057 文章例如该链接通过将YUV分成三个纹理,在shader中取出并且经过公式变换,转换成RGB.我 ...

  6. Android平台的开发环境的发展演变

    因为之前学习java语言的时候安装过了eclipse,所以想在eclipse上搭建android平台,在参照知乎上大神们的意见,发现了AS强大的代码提示.实时预览和搜索匹配等出色功能,最后还是选择在A ...

  7. 【转】Android平台下利用zxing实现二维码开发

    http://www.cnblogs.com/dolphin0520/p/3355728.html 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描 ...

  8. Android平台下利用zxing实现二维码开发

    Android平台下利用zxing实现二维码开发 现在走在大街小巷都能看到二维码,而且最近由于项目需要,所以研究了下二维码开发的东西,开源的二维码扫描库主要有zxing和zbar,zbar在iPos平 ...

  9. Android平台介绍

    一.Android平台介绍 什么是智能手机 具有独立的操作系统,独立的运行空间,可以由用户自行安装软件.游戏.导航等第三方应用程序,并可以通过移动通讯网络来实现无线网络接入的手机类型总称. 智能手机操 ...

随机推荐

  1. layout_gravity与gravity的区别

    1:android:gravity 这个是针对控件里的元素来说的,用来控制元素在该控件里的显示位置. 2:android:layout_gravity 这个是针对控件本身而言,用来控制该控件在包含该控 ...

  2. NOR flash和NAND flash区别,RAM 和ROM区别

    ROM和RAM指的都是半导体存储器,ROM是Read Only Memory的缩写,RAM是Random Access Memory的缩写.ROM在系统停止供电的时候仍然可以保持数据,而RAM通常都是 ...

  3. ubuntu记录

    1. gleboneblack OMAPES=4.x ANDROID_ROOT_DIR=$HOME/aosp W=1 install /bin/sh: 5: ./install.sh: Permiss ...

  4. ubuntu scp

    一.将本机文件复制到远程服务器上 scp -r /Users/Dreamover/Desktop/jsnone dreamover@localserver:/var/www/project/ /Use ...

  5. 完美:adobe premiere cs6破解版下载[序列号+汉化包+破解补丁+破解教程]

    原文地址:http://blog.sina.com.cn/s/blog_6306f2c60102f5ub.html 完美:adobe premiere cs6破解版下载,含序列号.汉化包.注册机.破解 ...

  6. 简单计算器(Android)

    aaarticlea/jpeg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKC

  7. 打开首页老是加载themes.googleusercontent.com_Wopus问答

    打开首页老是加载themes.googleusercontent.com_Wopus问答 打开首页老是加载themes.googleusercontent.com

  8. 由mysql数据库基础上的php程序实现单词的查询、删除、更改和查询

    我做了一个php程序,将表单数据添加到数据库,借用mysql扩展库函数实现对mysql数据库的操作,能够实现添加单词.删除单词.更新和查询单词.运行环境是普通的mysql数据库和php.Apache服 ...

  9. JMeter简单的性能测试实例

    JMeter基础之——一个简单的性能测试 我们了解了jmeter的一此主要元件,那么这些元件如何使用到性能测试中呢.这一节创建一个简单的测试计划来使用这些元件.该计划对应的测试需求. 1)测试目标网站 ...

  10. [RxJS] Handling a Complete Stream with Reduce

    When a stream has completed, you often need to evaluate everything that has happened while the strea ...