在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 以前使用的API1(android.hardware.camera)就被标为 Deprecated 了. Camera API2相较于API1有很大不一样, 而且API2是为了配合HAL3进行使用的, API2有不少API1不支持的特性, 好比:android
上面列举的只是一部分, 而且实际功能支持状况要看HAL层是否完成了相关功能, 也就是说API有不少功能来知足拍照/录像需求, 但实际是否能用和具体设备有关.git
在API架构方面, Camera2和以前的Camera有很大区别, APP和底层Camera以前能够想象成用管道方式链接, 以下图:github
如上图所示, Camera APP 经过CameraCaptureSession发送CaptureRequest, CameraDevices收到请求后返回对应数据到对应的Surface,预览数据通常都是到TextureView, 拍照数据则在ImageReader中, 总体来讲就是一个请求--响应过程, 请求完成后, 能够在回调中查询到相应的请求参数和CameraDevice当前状态, 总的来讲, Camera2中预览/拍照/录像数据统一由Surface来接收, CaptureRequest表明请求控制的Camera参数, CameraMetadata(CaptureResult)则表示当前返回帧中Camera使用的参数以及当前状态.架构
API使用流程大致以下:ui
context.getSystemService(Context.CAMERA_SERVICE)
获取CameraManager
.CameraManager .open()
方法在回调中获得CameraDevice
.CameraDevice.createCaptureSession()
在回调中获取CameraCaptureSession
.CaptureRequest
, 有三种模式可选 预览/拍照/录像.CameraCaptureSession
发送CaptureRequest
, capture表示只发一次请求, setRepeatingRequest表示不断发送请求.ImageReader.OnImageAvailableListener
回调中获取, CaptureCallback
中则可获取拍照实际的参数和Camera当前状态.上面说过, 不是因此手机都支持完整的Camera2功能, 如今都2018了, Camera2出来都有4年左右了, 但估计还有些中低端手机使用的HAL1, 使用HAL1就会致使Camera2一些高级功能都无法使用了, 下面讲一下如何查询设备对应Camera2的支持状况.
INFO_SUPPORTED_HARDWARE_LEVEL
硬件层面支持的Camera2功能等级, 主要分为5个等级:google
LEVEL_LEGACY: 向后兼容模式, 若是是此等级, 基本没有额外功能, HAL层大几率就是HAL1(我遇到过的都是)
LEVEL_LIMITED: 有最基本的功能, 还支持一些额外的高级功能, 这些高级功能是LEVEL_FULL的子集
LEVEL_FULL: 支持对每一帧数据进行控制,还支持高速率的图片拍摄
LEVEL_3: 支持YUV后处理和Raw格式图片拍摄, 还支持额外的输出流配置
LEVEL_EXTERNAL: API28中加入的, 应该是外接的摄像头, 功能和LIMITED相似spa
各个等级从支持的功能多少排序为: LEGACY < LIMITED < FULL < LEVEL_3
获取代码以下:code
// CameraCharacteristics 可经过 CameraManager.getCameraCharacteristics() 获取 private int isHardwareSupported(CameraCharacteristics characteristics) { Integer deviceLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); if (deviceLevel == null) { Log.e(TAG, "can not get INFO_SUPPORTED_HARDWARE_LEVEL"); return -1; } switch (deviceLevel) { case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL: Log.w(TAG, "hardware supported level:LEVEL_FULL"); break; case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY: Log.w(TAG, "hardware supported level:LEVEL_LEGACY"); break; case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3: Log.w(TAG, "hardware supported level:LEVEL_3"); break; case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED: Log.w(TAG, "hardware supported level:LEVEL_LIMITED"); break; } return deviceLevel; }
REQUEST_AVAILABLE_CAPABILITIES
上面讲的几个等级是从总体上说明Camera2支持状况, 咱们还能够查询更多细节功能,REQUEST_AVAILABLE_CAPABILITIES 则能够知道具体支持哪些实际功能, 具体有以下功能:排序
BACKWARD_COMPATIBLE | READ_SENSOR_SETTINGS |
---|---|
MANUAL_SENSOR | BURST_CAPTURE |
MANUAL_POST_PROCESSING | YUV_REPROCESSING |
RAW | DEPTH_OUTPUT |
PRIVATE_REPROCESSING | CONSTRAINED_HIGH_SPEED_VIDEO |
各个功能具体含义请参考官网的解释 :官网 的解释, 我没有深刻研究.
大多数支持等级为 LEGACY 的设备, 只支持 BACKWARD_COMPATIBLE , 也就是说前面提到的Camera2新功能都不支持......图片
在使用Camera2 API过程当中, 设置测光和对焦区域这部分刚开始一直不知道该怎么写代码, 后面折腾了一番, 终于找到正确的方法了, 在此记录一下.
AE/AF 区域须要经过用户在屏幕上的点击位置来进行设置, 所以咱们要作的就是将屏幕点击点映射到底层对焦和测光位置点, 同时发送请求触发对焦这个动做. 首先要知道一点就是, 屏幕点击点坐标和Camera底层坐标不是对应的, 基本关系以下图:
AF_Coordinate.jpg
x,y坐标系表示屏幕坐标, x1,y1表示Camera对应坐标
可见屏幕点击和底层有个90度旋转关系, 实际设置AF/AE区域步骤以下:
1.首先获取SENSOR_INFO_ACTIVE_ARRAY_SIZE
, 即底层Camera坐标点的范围, API中经过一个Rect
表示
characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE)
获得``Rect```一般为支持的最大图片尺寸, 好比(0, 0, 4160, 3120)
2.坐标映射
咱们须要将屏幕点击点映射到 SENSOR_INFO_ACTIVE_ARRAY_SIZE
对应的Rect
中, 从图中能够很容易看出, 坐标转换关系为 : x1 = y , y1 = previewWidth - x
, 坐标转换完成后只需乘以 图片宽度/预览宽度
的比例便可获得转换后的坐标, 而后以坐标点为中心生成一个矩形, 这个矩形就是咱们要的结果, 实际代码片断以下:
private MeteringRectangle calcTapAreaForCamera2(CameraCharacteristics c, int areaSize, int weight) { // 获取Size Rect rect = c.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); Log.d(TAG, "active Rect:" + rect.toString()); Rect newRect; int leftPos, topPos; // 坐标转换 float newX = currentY; float newY = previewWidth - currentX; // 大小转换 leftPos = (int) ((newX / previewHeight) * rect.right); topPos = (int) ((newY / previewWidth) * rect.bottom); // 以坐标点为中心生成一个矩形, 须要防止上下左右的值溢出 int left = clamp(leftPos - areaSize, 0, rect.right); int top = clamp(topPos - areaSize, 0, rect.bottom); int right = clamp(leftPos + areaSize, leftPos, rect.right); int bottom = clamp(topPos + areaSize, topPos, rect.bottom); newRect = new Rect(left, top, right, bottom); Log.d(TAG, newRect.toString()); // 构造MeteringRectangle return new MeteringRectangle(newRect, weight); } private int clamp(int x, int min, int max) { if (x > max) { return max; } if (x < min) { return min; } return x; }
注: 此处坐标映射能够经过Matrix进行, 熟悉Matrix的同窗能够尝试下
3.设置AE/AF区域并触发对焦, 代码片断以下
public void startControlAFRequest(MeteringRectangle rect, CameraCaptureSession.CaptureCallback captureCallback) { MeteringRectangle[] rectangle = new MeteringRectangle[]{rect}; // 对焦模式必须设置为AUTO mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_AUTO); //AE mPreviewBuilder.set(CaptureRequest.CONTROL_AE_REGIONS,rectangle); //AF 此处AF和AE用的同一个rect, 实际AE矩形面积比AF稍大, 这样测光效果更好 mPreviewBuilder.set(CaptureRequest.CONTROL_AF_REGIONS,rectangle); try { // AE/AF区域设置经过setRepeatingRequest不断发请求 mSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } //触发对焦 mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CaptureRequest.CONTROL_AF_TRIGGER_START); try { //触发对焦经过capture发送请求, 由于用户点击屏幕后只需触发一次对焦 mSession.capture(mPreviewBuilder.build(), captureCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
Camera2 API和旧的Camera API区别很大, 刚开始用可能会很不习惯, 但Camera2有不少优点, 提供了很是多的参数供咱们控制, 后面API1可能会被移除, 因此能够尽早将项目用Camera2重写, 另外若是对API和HAL版本对应关系不清楚的, 能够参考我以前写的文章 Android Camera API和HAL版本对应关系.
基于Camera2 API, 我写了个Demo, 功能还算完善, 有兴趣的能够看下: Github: Camera2, 目前没有录像功能, 后续会加上.