用crame1的api还是用camre2的api呢?用2的吧,随便了。
主要用到的5个类:
CameraManager:是通过getSystemService(Context.CAMERA_SERVICE);拿到所有相机的管理者。
CameraDevice: 通过CameraManager返回的一个可用摄像头,原过时的Carema(android.hardware.Camera)对应,但是API很不一样了。
每个CameraDevice自己会负责建立CameraCaptureSession以及建立CaptureRequest。
CameraCaptureSession:某个CameraDevice的拍照请求会话,很重要的一个类。
CaptureRequest:一个拍照等的请求对象。
CameraCharacteristics: 摄像头的特征,即这个类里封装着该手机摄像头支持的参数。
它们之间的关系大概就是:
首先通过getSystemService(Context.CAMERA_SERVICE)拿到CameraManger,
然后通过CameraManger拿到某个摄像头的CameraCharacteristics,得到支持的参数啊CameraCharacteristics以及预览尺寸等信息。
然后再通过CameraManger的openCamera拿到一个具体的摄像头对象CameraDevice。该方法有一个参数会要求传递一个回调,当摄像头打开时会该方法:onOpen,
然后再在该方法了开启摄像头预览,绑定Surface。
本篇说一下YUV的格式以及如何从Image中得到真正的YUV数据。
YUV的格式网上有很多了,这里就不从YUV的具体格式展开了。这里只分享Camera2设置YUV_420_888
时返回的数据格式。
如果了解YUV格式,我们知道其实YUV_420_888
可以包含很多格式,比如YUV420P(I420=YU12 YV12)
和YUV420SP(NV12,NV21)
都可以叫做YUV_420_888。
Surface本质上是一个Native Window, 并且保存着需要在屏幕上显示的数据(buffer), 它通常包含 triple-buffers 以防止Jank。
那么谁要创建Surface呢? 当然是App了,App需要将自己的内容显示在屏幕上,所以App负责发起Surface创建请求,创建好Surface后, 就可以直接可以在canvas上画图等,最终都会保存到Surface里的buffer里,最后由SurfaceFlinger合成并显示。
它这个预览与获取好像是两个分开的过程,没办法杂糅到一起,预览直接绑定的,数据流不在代码中体现。
Camera2引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送 Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。
Android普通窗口的视图绘制机制是一层一层的,任何一个子元素或者是局部的刷新都会导致整个视图结构全部重绘一次,因此效率相对较低。
视频或者opengl内容往往是显示在SurfaceView中的,SurfaceView的工作方式是:创建一个置于应用窗口之后的新窗口
。因为SurfaceView窗口刷新的时候不需要重绘应用程序的窗口,所以这种方式的效率非常高。
但是SurfaceView也有一些非常不便的限制,因为SurfaceView的内容不在应用窗口上,所以不能使用平移、缩放、旋转
等变换操作,也难以放在ListView或者ScrollView中,同样不能使用UI控件的一些特性,比如View.setAlpha()。
为了解决这个问题,Android 4.0 中引入了TextureView,与SurfaceView相比,TextureView并没有创建一个单独的 Surface 用来绘制,这使得它可以像一般的View一样执行一些变换操作,设置透明度等
。
TextureView的使用非常简单,你唯一要做的就是获取用于渲染内容的SurfaceTexture。
Textureview必须在硬件加速开启的窗口中。
surfaceflinger作用是接受多个来源的图形显示数据,将他们合成,然后发送到显示设备。比如打开应用,常见的有三层显示,顶部的statusbar底部或者侧面的导航栏以及应用的界面,每个层是单独更新和渲染,这些界面都是有surfaceflinger合成一个刷新到硬件显示。在显示过程中使用到了bufferqueue,surfaceflinger作为consumer方,比如windwomanager管理的surface作为生产方产生页面,交由surfaceflinger进行合成。
分析一下SurfaceTexture的原理,提到SurfaceTexture一般会提到与它相关的SurfaceView、GLSurfaceView、TextureView,这几个都可以将图形生产者的数据(比如Camera)送到SurfaceFlinger中显示,而SurfaceTexture可以看做Surface和Texture的组合,是将图形生产者的数据送到Texture,然后是由应用程序自己来处理。这里的Texture应该是属于opengl的概念,
应用程序会先创建一个SurfaceTexture,然后将SurfaceTexture传递给图形生产者对象(比如Camera,通过调用setPreviewTexture传递),图形生产者对象生产一帧数据后,会回调onFrameAvailable通知应用程序有新的图像数据可以使用,应用程序就可以调用updateTexImage将图像数据先送到Texture,之后就可以调用opengl接口做些具体的业务了。
TextureView是在4.0(API level 14)引入的,用于承载显示‘数据流’的View, 如本地Camera采集的预览数据流和视频通话模块从网络包里解出实时视频‘数据流’。
在TextureView中存在一个
private SurfaceTexture mSurface;
成员变量。
可以使用这个SurfaceTexture来构造一个Surface。然后在Surface上可以显示从camera中获取到的流。
处理预览帧简单来说就是对相机预览时的每一帧的数据进行处理,一般来说如果相机的采样速率是30fps的话,一秒钟就会有30个帧数据需要处理。帧数据具体是什么?如果你就是奔着处理帧数据来的话,想必你早已知道答案,其实就是一个byte类型的数组,包含的是YUV格式的帧数据。addPreferencesFromResource(R.xml.preferences);
是加载来自preferences.xml
文件中的菜单条目,对于PreferenceFragment
,其菜单条目都是存储在xml文件中,实例化时从xml文件加载的,别着急,我们马上创建preferences.xml
文件。
预览帧数据的处理通常会包含大量的计算,从而导致因为帧数据太多而处理效率低下,以及衍生出的预览画面卡顿等问题。onPreviewFrame()
在执行Camera.open()
时所在的线程运行。而目前Camera.open()
就是在UI线程中执行的(因为没有创建新进程),对应的解决方法也很简单了:让Camera.open()
在非UI线程执行。
如何确保不会有帧数据被丢弃,即保证每个帧数据都被处理。解决方法的中心思想很明确:让onPreviewFrame()
尽可能快地返回,不至于丢弃帧数据。
那我们看看现在的流程是怎么样的。
首先呢在camera2activity中,两个button分别接管着startPreview和stopPreview,其中stopPreview仅仅是移除了所有view。
而在startPreview中,新建了一个cameraPreview并把其添加进frame中,添加了一个settingFreagmet这个不用管,就是setting,这个cameraPreview是非常独立的。现在看看里面。它是怎么出现在画面上的?
Android+OpenCV 摄像头实时识别模板图像并跟踪,对,关键词是这个才对!终于让我找到曙光了