设为首页 加入收藏

TOP

Android平台之不预览获取照相机预览数据帧及精确时间截(一)
2015-07-16 12:57:40 来源: 作者: 【 】 浏览:25
Tags: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中做耗时的工作,因为那么极可能会导致丢掉一些预览数据帧。


通过上面的方

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇Linux下获得线程ID syscall(224) 下一篇Android开发之打开闪光灯录制视频

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: