Android Camera2 简介

Camera2简介

在Google 推出Android 5.0的时候, Android Camera API 版本升级到了API2(android.hardware.camera2), 之前使用的API1(android.hardware.camera)就被标为 Deprecated 了. Camera API2相较于API1有很大不同, 并且API2是为了配合HAL3进行使用的, API2有很多API1不支持的特性, 比如:

  1. 更先进的API架构
  2. 可以获取更多的帧(预览/拍照)信息以及手动控制每一帧的参数
  3. 对Camera的控制更加完全(比如支持调整focus distance, 剪裁预览/拍照图片)
  4. 支持更多图片格式(yuv/raw)以及高速连拍
    ......

上面列举的只是一部分, 并且实际功能支持情况要看HAL层是否完成了相关功能, 也就是说API有很多功能来满足拍照/录像需求, 但实际是否能用和具体设备有关.

基本架构

在API架构方面, Camera2和之前的Camera有很大区别, APP和底层Camera之前可以想象成用管道方式连接, 如下图:

38487b61-571f-4b9e-908f-acd99d69f4f1.png

如上图所示, Camera APP 通过CameraCaptureSession发送CaptureRequest, CameraDevices收到请求后返回对应数据到对应的Surface,预览数据一般都是到TextureView, 拍照数据则在ImageReader中, 整体来说就是一个请求--响应过程, 请求完成后, 可以在回调中查询到相应的请求参数和CameraDevice当前状态, 总的来说, Camera2中预览/拍照/录像数据统一由Surface来接收, CaptureRequest代表请求控制的Camera参数, CameraMetadata(CaptureResult)则表示当前返回帧中Camera使用的参数以及当前状态.

使用流程

API使用流程大体如下:

8c75b772-d214-4d2f-94ca-7b4ad877d17f.png
  1. 通过context.getSystemService(Context.CAMERA_SERVICE) 获取CameraManager.
  2. 调用CameraManager .open()方法在回调中得到CameraDevice.
  3. 通过CameraDevice.createCaptureSession() 在回调中获取CameraCaptureSession.
  4. 构建CaptureRequest, 有三种模式可选 预览/拍照/录像.
  5. 通过 CameraCaptureSession发送CaptureRequest, capture表示只发一次请求, setRepeatingRequest表示不断发送请求.
  6. 拍照数据可以在ImageReader.OnImageAvailableListener回调中获取, CaptureCallback中则可获取拍照实际的参数和Camera当前状态.

查询Camera2功能支持情况

上面说过, 不是所以手机都支持完整的Camera2功能, 现在都2018了, Camera2出来都有4年左右了, 但估计还有些中低端手机使用的HAL1, 使用HAL1就会导致Camera2一些高级功能都没法使用了, 下面讲一下如何查询设备对应Camera2的支持情况.
INFO_SUPPORTED_HARDWARE_LEVEL
硬件层面支持的Camera2功能等级, 主要分为5个等级:

  • INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY
  • INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED
  • INFO_SUPPORTED_HARDWARE_LEVEL_FULL
  • INFO_SUPPORTED_HARDWARE_LEVEL_3
  • INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL

LEVEL_LEGACY: 向后兼容模式, 如果是此等级, 基本没有额外功能, HAL层大概率就是HAL1(我遇到过的都是)
LEVEL_LIMITED: 有最基本的功能, 还支持一些额外的高级功能, 这些高级功能是LEVEL_FULL的子集
LEVEL_FULL: 支持对每一帧数据进行控制,还支持高速率的图片拍摄
LEVEL_3: 支持YUV后处理和Raw格式图片拍摄, 还支持额外的输出流配置
LEVEL_EXTERNAL: API28中加入的, 应该是外接的摄像头, 功能和LIMITED类似

各个等级从支持的功能多少排序为: LEGACY < LIMITED < FULL < LEVEL_3
获取代码如下:

// 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 AE/AF Region

在使用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, 目前没有录像功能, 后续会加上.

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容