Android学习笔记之——Camera2架构

之前博文《Android学习笔记之——调用前后置相机的视频流》已经实现了视频流的捕获与显示了(通过 camera1 setPreviewCallback 类似功能实现)。本博文继续学习一下Android中的camera相关的API

目录

Camera2

基本框架及流程

Camera2的各个部分详解

CaptureRequest

CaptureResult

CameraCaptureSession

CameraDevice

CameraManager

CameraCharacteristics 

Surface和CaptureRequest 

TextureView + Camera2

Test demo1

UI

mainactivity

权限申请

参考资料


Camera2

基本框架及流程

全新的android.hardware.Camera2 。Android 5.0对拍照API进行了全新的设计,新增了全新设计的Camera 2 API,这些API不仅大幅提高了Android系统拍照的功能,还能支持RAW照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快门等。

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

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

API使用流程大体如下:

步骤

  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的各个部分详解

叫出CameraManager ,打开 CameraDevice ,拿住CameraCaptureSession,发送CaptureRequest 。

CaptureRequest

(参考资料https://blog.csdn.net/afei__/article/details/86326991

(官方资料https://developer.android.com/reference/android/hardware/camera2/CaptureRequest

CaptureRequest 表示一个捕捉的请求。我们可以为不同的场景(预览、拍照)创建不同的捕捉请求,并可以配置不同的捕捉属性,如:预览分辨率,预览目标,对焦模式、曝光模式等等

Camera2下的摄像机称为CameraDevice,可以有很多个(一般是前后两个),每个CameraDevice都带有ameraCaptureSession的会话通道,只要我们捉住了一条会话通道,就可以通过这条通道传送CameraRequest请求预览,拍照,录像。所以我们只要向目标CameraDevice索取一条CameraCaptureSession会话通道,利用以下方法就可以实现控制摄像头预览,拍照或者录像:

CameraCaptureSession.setRepeatingRequest(CaptureRequest arg0, CaptureCallback arg1, Handler arg2)
//第一个参数,需要通过这个会话通道向CameraDevice传送一个CaptureRequest请求
//第二个参数是回调,回调不做处理,所以null
//第三个参数是给回调用的线程,回调是null,线程自然也是null

CameraCaptureSession由CameraDevice创建,CameraDevice由CameraManage创建。

通过 CameraDevice 对象的 createCaptureRequest() 方法得到一个 CaptureRequest.Builder 对象,基本配置都是通过该构造者来配置;最后通过 CaptureRequest.Builder 对象的 build() 方法便可得到 CaptureRequest 实例。

内部类

1.CaptureRequest.Builder

典型的建造者模式,是 CaptureRequest 的构建者。

CaptureRequest.Builder builder;//先拿到一个 CaptureRequest.Builder 对象
builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

获取:使用 CameraDevice.createCaptureRequest(int) 方法获取一个 CaptureRequest.Builder 对象。其中的 int 取值为:

  • TEMPLATE_PREVIEW : 用于创建一个相机预览请求。相机会优先保证高帧率而不是高画质。适用于所有相机设备。
  • TEMPLATE_STILL_CAPTURE : 用于创建一个拍照请求。相机会优先保证高画质而不是高帧率。适用于所有相机设备。
  • TEMPLATE_RECORD : 用于创建一个录像请求。相机会使用标准帧率,并设置录像级别的画质。适用于所有相机设备。
  • TEMPLATE_VIDEO_SNAPSHOT : 用于创建一个录像时拍照的请求。相机会尽可能的保证照片质量的同时不破坏正在录制的视频质量。适用于硬件支持级别高于 LEGACY 的相机设备。
  • TEMPLATE_ZERO_SHUTTER_LAG : 用于创建一个零延迟拍照的请求。相机会尽可能的保证照片质量的同时不损失预览图像的帧率,3A(自动曝光、自动聚焦、自动白平衡)都为 auto 模式。只适用于支持 PRIVATE_REPROCESSING 和 YUV_REPROCESSING 的相机设备。
  • TEMPLATE_MANUAL : 用于创建一个手动控制相机参数的请求。相机所有自动控制将被禁用,后期处理参数为预览质量,手动控制参数被设置为合适的默认值,需要用户自己根据需求来调整各参数。适用于支持 MANUAL_SENSOR 的相机设备。

常用方法

2. CaptureRequest.Key

上面 get/set 方法中的 Key 类就是这个内部类了,用作 CaptureRequest.Builder 的属性字段设置和查询使用。

注意:通过getAvailableCaptureRequestKeys()就是得到该相机设备可用的 CaptureRequest.Key 列表

CaptureResult

(参考资料https://blog.csdn.net/afei__/article/details/86326991

(官方文档链接:https://developer.android.com/reference/android/hardware/camera2/CaptureResult

CaptureResult 表示捕捉的结果,是从图像传感器捕获单个图像的结果的子集。包含捕获硬件(传感器、镜头、闪光灯)、处理管道、控制算法和输出缓冲区的最终配置的子集。

捕获结果由摄像机在对捕获请求进行处理后产生。还可以对捕获结果查询为捕获请求列出的所有属性,以确定捕获使用的最终值。结果还包括捕获过程中相机设备状态的附加元数据。

CaptureResult 对象也是不可变的。常使用的子类是 TotalCaptureResult

CameraCaptureSession

https://blog.csdn.net/afei__/article/details/86108482

CameraCaptureSession 是一个事务,用来向相机设备发送获取图像的请求。

主要有 setRepeatingRequest() 和 capture() 方法。setRepeatingRequest() 是重复请求获取图像数据,常用于预览或连拍,capture() 是获取一次,常用于单张拍照。

CameraCaptureSession 类是一个抽象类,其直接的实现类为 CameraConstrainedHighSpeedCaptureSession

下面通过CameraDevice类的 createCaptureSession() 方法创建创建CameraCaptureSession实例,创建出来的CameraCaptureSession就在参数二的回调类CameraCaptureSession.StateCallback()的onConfigured(CameraCaptureSession arg0)方法的参数给与,参数三是给回调用的线程,可以null即使用主线程,参数一是一个Surface的list集合,用以存放请求结果,可以把多个Surface打包成List送过去,实现你想同时多个显示或者其他利用如存储的需求。

CameraDevice.createCaptureSession(List<Surface> arg0, StateCallback arg1, Handler arg2)

参数说明:

  • outputs : 输出的 Surface 集合,每个 CaptureRequest 的输出 Surface 都应该是 outputs 的一个子元素。例如上例中使用了一个 mPreviewSurface 用于预览的输出,一个 mImageReader.getSurface() 用于拍照的输出。
  • callback : 创建会话的回调。成功时将调用 onConfigured(CameraCaptureSession session) 方法。
  • handler : 指定回调执行的线程,传 null 时默认使用当前线程的 Looper。

内部类

1. CameraCaptureSession.StateCallback

当相机捕捉事务的状态发生变化时,会回调这个类中的相应方法。其中只有 onConfigured 和 onConfigureFailed 两个方法是抽象的(必须重写),其余均有空实现。

CameraDevice

https://blog.csdn.net/afei__/article/details/85342597

(官方关于CameraDevice的介绍https://developer.android.google.cn/reference/android/hardware/camera2/CameraDevice

CameraDevice 是一个连接的相机设备代表,可以把它看作为相机设备在 java 代码中的表现。

通过 CameraManager 的 openCamera() 方法打开相机,在 CameraDevice.StateCallback 的 onOpened(CameraDevice camera) 方法中可获得 CameraDevice 的实例。

CameraDevice是由CameraManager的openCamera方法创建的,CameraDevice同样是在参数二回调类参数中的onOpened(CameraDevice arg0)方法的参数送回,参数一是指定打开的摄像头,“0”是后置,“1”是前置,详细不多说,参数三又是回调占用的线程,null吧。

CameraManager.openCamera(String cameraId, StateCallback callback, Handler handler) 

CameraManager

(参考资料https://blog.csdn.net/afei__/article/details/85342160)

(官方文档https://developer.android.google.cn/reference/android/hardware/camera2/CameraManager

CameraManager 是系统服务之一,专门用于检测和打开相机,以及获取相机设备特性。

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

// 方式一
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
// 方式二
CameraManager manager = (CameraManager) context.getSystemService(CameraManager.class);

String[] getCameraIdList()
获取当前连接的相机设备列表,这个 id 通常都是从 0 开始并依次递增的。对于一般的手机而言:

  • 后置摄像头一般为 “0”,常量值为 CameraCharacteristics.LENS_FACING_FRONT;
  • 前置摄像头一般为 “1”,常量值为 CameraCharacteristics.LENS_FACING_BACK。

CameraCharacteristics getCameraCharacteristics(String cameraId)
根据 cameraId 获取对应相机设备的特征。返回一个 CameraCharacteristics,类比于旧 API 中的 Camera.Parameter 类,里面封装了相机设备固有的所有属性功能

void openCamera(String cameraId, final CameraDevice.StateCallback callback, Handler handler)
打开指定的相机设备,该方法使用当前进程 uid 继续调用 openCameraForUid(cameraId, callback, handler, USE_CALLING_UID) 方法。

参数解释:

cameraId : 需要打开的相机 id。
callback : 回调类,常用如下几个回调方法。
onOpened(CameraDevice camera) 成功打开时的回调,此时 camera 就准备就绪,并且可以得到一个 CameraDevice 实例。
onDisconnected(CameraDevice camera) 当 camera 不再可用或打开失败时的回调,通常在该方法中进行资源释放的操作。
onError(CameraDevice camera, int error) 当 camera 打开失败时的回调,error 为具体错误原因,定义在 CameraDevice.StateCallback 类中。通常在该方法中也要进行资源释放的操作。
handler : 指定回调执行的线程。传 null 时默认使用当前线程的 Looper,我们通常创建一个后台线程来处理。

CameraCharacteristics 

(参考资料https://blog.csdn.net/afei__/article/details/85960343

(官方示例https://developer.android.google.cn/reference/android/hardware/camera2/CameraCharacteristics

CameraCharacteristics 是描述相机设备的属性类,其中的属性都是固定的,继承自CameraMetadata 类。类比于旧 API 中的 CameraInfo 类。

包括:曝光补偿(Exposure compensation)、自动曝光/自动对焦/自动白平衡模式(AE / AF / AWB mode)、自动曝光/自动白平衡锁(AE / AWB lock)、自动对焦触发器(AF trigger)、拍摄前自动曝光触发器(Precapture AE trigger)、测量区域(Metering regions)、闪光灯触发器(Flash trigger)、曝光时间(Exposure time)、感光度(ISO Sensitivity)、帧间隔(Frame duration)、镜头对焦距离(Lens focus distance)、色彩校正矩阵(Color correction matrix)、JPEG 元数据(JPEG metadata)、色调映射曲线(Tonemap curve)、裁剪区域(Crop region)、目标 FPS 范围(Target FPS range)、拍摄意图(Capture intent)、硬件视频防抖(Video stabilization)等。

Surface和CaptureRequest 

Surface可以来自控件TextureView,它用来显示摄像头的图像,在布局文件放一个,获取它的Surface可以通过它的监听事件

TextureView.setSurfaceTextureListener(SurfaceTextureListener listener)

之前博文《Android学习笔记之——调用前后置相机的视频流》就是用TextureView

new一个SurfaceTextureListener,在它的onSurfaceTextureAvailable(SurfaceTexture arg0, int arg1, int arg2)方法里的参数一提取SurfaceTexture arg0得到。

CaptureRequest.Builder builder;
builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
builder.addTarget(mPreviewSurface);
mCameraCaptureSession.setRepeatingRequest(builder.build(), null, null);

CaptureRequest则由CameraDevice.createCaptureRequest()创建的builder,绑定目标surface后再builder()出来。

TextureView + Camera2

https://blog.csdn.net/afei__/article/details/85269753

https://blog.csdn.net/afei__/article/details/86710164

之前博文《 Android学习笔记之——调用前后置相机的视频流》已经介绍过TextureView。由于 SurfaceView 是拥有一个独立的 Surface,不在 View hierachy 体系中,因此不支持动画和截图,而 TextureView 则没有该限制。TextureView 是 Android 4.0 之后引入的,它将内容流直接投影到 View 中,数据流可以来自 App 进程或远端进程。缺点是必须在硬件加速的窗口中使用,且内存和耗电比 SurfaceView 更高,绘制也可能存在 1~3 帧的延迟。

Test demo1

UI

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/take_photo_BackCamera"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="BackCamera"
        android:textAllCaps="false"
        />



    <RelativeLayout
        android:layout_width="200dp"
        android:layout_height="200dp">

        <TextureView
            android:id="@+id/texture_view_back"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_gravity="center_horizontal" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:text="TextureView(摄像头预览)"
            android:textColor="#f00"
            android:textSize="10sp" />
    </RelativeLayout>





</LinearLayout>

mainactivity

package com.example.camera2test;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.TextureView;

import java.util.Arrays;

public class MainActivity extends AppCompatActivity {

    private TextureView mTextureView;//显示数据流的UI控件
    private CameraCaptureSession mCameraCaptureSession;//是一个事务,用来向相机设备发送获取图像的请求。
    private CameraDevice mCameraDevice;//是一个连接的相机设备代表,你可以把它看作为相机设备在 java 代码中的表现
    private Surface mPreviewSurface;//Surface来自控件TextureView,它用来显示摄像头的图像,

    //权限
    private static String[] PERMISSIONS_STORAGE = {
//            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,//写权限
            Manifest.permission.CAMERA//照相权限
    };


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        如果提示【Fail to connect to camera service】很可能是没申请权限,或申请权限了但用户没有给你权限
        //华为手机摄像头权限申请
        //用于判断SDK版本是否大于23
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
            //检查权限
            int i = ContextCompat.checkSelfPermission(this,PERMISSIONS_STORAGE[0]);
            //如果权限申请失败,则重新申请权限
            if(i!= PackageManager.PERMISSION_GRANTED){
                //重新申请权限函数
                startRequestPermission();
                Log.e("这里","权限请求成功");
            }
        }



        //预览用的surface
        mTextureView = (TextureView) this.findViewById(R.id.texture_view_back);
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {

            //SurfaceTexture准备就绪后调用这个方法
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                // TODO 自动生成的方法存根
                mPreviewSurface = new Surface(surface);//定义一个surface

                //1、先通过通过context.getSystemService(Context.CAMERA_SERVICE) 方法来获取CameraManager
                CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);//系统服务,专门用于检测和打开相机,以及获取相机设备特性

                try {
                    if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                        // TODO: Consider calling
                        //    ActivityCompat#requestPermissions
                        // here to request the missing permissions, and then overriding
                        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                        //                                          int[] grantResults)
                        // to handle the case where the user grants the permission. See the documentation
                        // for ActivityCompat#requestPermissions for more details.
                        return;
                    }
                    //2、再通过调用CameraManager .open()方法在回调中得到CameraDevice。ID0为后置摄像头
                    manager.openCamera("0", new CameraDevice.StateCallback() {//打开指定的相机设备

                        //成功打开时的回调,此时 camera 就准备就绪,并且可以得到一个 CameraDevice 实例。
                        @Override
                        public void onOpened(@NonNull CameraDevice camera) {
                            mCameraDevice = camera;//所回调的相机设备赋予给mCameraDevice
                            try {
                                //CameraCaptureSession 是一个事务,用来向相机设备发送获取图像的请求
                                //3、通过CameraDevice.createCaptureSession() 在回调中获取CameraCaptureSession
                                mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface), new CameraCaptureSession.StateCallback() {
                                    //CameraCaptureSession.StateCallback是其内部类

                                    //相机设备完成配置,并开始处理捕捉请求时回调
                                    @Override
                                    public void onConfigured(@NonNull CameraCaptureSession session) {

                                        mCameraCaptureSession = session;//是一个事务,用来向相机设备发送获取图像的请求。
                                        try {
                                            //4、构建CaptureRequest, 有三种模式可选 预览/拍照/录像
                                            //通过下面方法获得一个CaptureRequest.Builder对象。
                                            //基本配置都是通过该构造者来配置
                                            //最后通过 CaptureRequest.Builder 对象的 build() 方法便可得到 CaptureRequest 实例(见setRepeatingRequest方法)
                                            CaptureRequest.Builder builder;//先拿到一个 CaptureRequest.Builder 对象
                                            builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                                            //TEMPLATE_PREVIEW : 用于创建一个相机预览请求。相机会优先保证高帧率而不是高画质。适用于所有相机设备
                                            //TEMPLATE_STILL_CAPTURE : 用于创建一个拍照请求。
                                            //TEMPLATE_RECORD : 用于创建一个录像请求。


                                            //通过 CaptureRequest.Builder 对象设置一些捕捉请求的配置
                                            //设置指定key的值
//                                            builder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

                                            builder.addTarget(mPreviewSurface);//绑定目标Surface

                                            //5、通过 CameraCaptureSession发送CaptureRequest, capture表示只发一次请求, setRepeatingRequest表示不断发送请求.
                                            //不断的重复请求捕捉画面,常用于预览或者连拍场景。
                                            mCameraCaptureSession.setRepeatingRequest(builder.build(), null, null);
                                        } catch (CameraAccessException e1) {
                                            e1.printStackTrace();
                                        }
                                    }

                                    //该会话无法按照相应的配置发起请求时回调
                                    @Override
                                    public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                                    }
                                }, null);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        //当 camera 不再可用或打开失败时的回调,通常在该方法中进行资源释放的操作。
                        @Override
                        public void onDisconnected(@NonNull CameraDevice camera) {

                        }

                        //当 camera 打开失败时的回调,error 为具体错误原因
                        // 定义在 CameraDevice.StateCallback 类中。通常在该方法中也要进行资源释放的操作。
                        @Override
                        public void onError(@NonNull CameraDevice camera, int error) {

                        }
                    }, null);
                }catch (CameraAccessException e){
                    e.printStackTrace();
                }
            }


            //在surface发生format或size变化时调用。
            //SurfaceTexture缓冲大小变化
            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }


            //SurfaceTexture即将被销毁
            //在此处回调做一些释放资源的操作
            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }


            //SurfaceTexture通过updateImage更新
            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }


    private void startRequestPermission(){
        //321为请求码
        ActivityCompat.requestPermissions(this,PERMISSIONS_STORAGE,321);
    }

}

权限申请

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.camera2test">

    <uses-permission android:name="android.permission.CAMERA" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

设置曝光时间

package com.example.camera2test;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.util.Range;
import android.view.Surface;
import android.view.TextureView;
import android.widget.Toast;

import java.util.Arrays;

import static android.hardware.camera2.CaptureRequest.SENSOR_EXPOSURE_TIME;

public class MainActivity extends AppCompatActivity {

    private TextureView mTextureView;//显示数据流的UI控件
    private CameraCaptureSession mCameraCaptureSession;//是一个事务,用来向相机设备发送获取图像的请求。
    private CameraDevice mCameraDevice;//是一个连接的相机设备代表,你可以把它看作为相机设备在 java 代码中的表现
    private Surface mPreviewSurface;//Surface来自控件TextureView,它用来显示摄像头的图像,

    private CameraCharacteristics mCameraCharacteristics;

    //权限
    private static String[] PERMISSIONS_STORAGE = {
//            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE,//写权限
            Manifest.permission.CAMERA//照相权限
    };


    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        如果提示【Fail to connect to camera service】很可能是没申请权限,或申请权限了但用户没有给你权限
        //华为手机摄像头权限申请
        //用于判断SDK版本是否大于23
        if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.M){
            //检查权限
            int i = ContextCompat.checkSelfPermission(this,PERMISSIONS_STORAGE[0]);
            //如果权限申请失败,则重新申请权限
            if(i!= PackageManager.PERMISSION_GRANTED){
                //重新申请权限函数
                startRequestPermission();
                Log.e("这里","权限请求成功");
            }
        }



        //预览用的surface
        mTextureView = (TextureView) this.findViewById(R.id.texture_view_back);
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {

            //SurfaceTexture准备就绪后调用这个方法
            @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                // TODO 自动生成的方法存根
                mPreviewSurface = new Surface(surface);//定义一个surface

                //1、先通过通过context.getSystemService(Context.CAMERA_SERVICE) 方法来获取CameraManager
                final CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);//系统服务,专门用于检测和打开相机,以及获取相机设备特性

                try {
                    if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                        // TODO: Consider calling
                        //    ActivityCompat#requestPermissions
                        // here to request the missing permissions, and then overriding
                        //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                        //                                          int[] grantResults)
                        // to handle the case where the user grants the permission. See the documentation
                        // for ActivityCompat#requestPermissions for more details.
                        return;
                    }
                    //2、再通过调用CameraManager .open()方法在回调中得到CameraDevice。ID0为后置摄像头
                    manager.openCamera("0", new CameraDevice.StateCallback() {//打开指定的相机设备

                        //成功打开时的回调,此时 camera 就准备就绪,并且可以得到一个 CameraDevice 实例。
                        @Override
                        public void onOpened(@NonNull CameraDevice camera) {
                            mCameraDevice = camera;//所回调的相机设备赋予给mCameraDevice
                            try {
                                //获取曝光时间这个属性。
                                mCameraCharacteristics = manager.getCameraCharacteristics(Integer.toString(0));
                                Range<Long> range=mCameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
                                long max=range.getUpper();
                                long min=range.getLower();
                                //通过Toast输出
                                Toast.makeText(MainActivity.this, "max:"+max+"min:"+min,Toast.LENGTH_LONG).show();




                                //CameraCaptureSession 是一个事务,用来向相机设备发送获取图像的请求
                                //3、通过CameraDevice.createCaptureSession() 在回调中获取CameraCaptureSession
                                mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface), new CameraCaptureSession.StateCallback() {
                                    //CameraCaptureSession.StateCallback是其内部类

                                    //相机设备完成配置,并开始处理捕捉请求时回调
                                    @Override
                                    public void onConfigured(@NonNull CameraCaptureSession session) {

                                        mCameraCaptureSession = session;//是一个事务,用来向相机设备发送获取图像的请求。
                                        try {
                                            //4、构建CaptureRequest, 有三种模式可选 预览/拍照/录像
                                            //通过下面方法获得一个CaptureRequest.Builder对象。
                                            //基本配置都是通过该构造者来配置
                                            //最后通过 CaptureRequest.Builder 对象的 build() 方法便可得到 CaptureRequest 实例(见setRepeatingRequest方法)
                                            CaptureRequest.Builder builder;//先拿到一个 CaptureRequest.Builder 对象
                                            builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                                            //TEMPLATE_PREVIEW : 用于创建一个相机预览请求。相机会优先保证高帧率而不是高画质。适用于所有相机设备
                                            //TEMPLATE_STILL_CAPTURE : 用于创建一个拍照请求。
                                            //TEMPLATE_RECORD : 用于创建一个录像请求。

                                            //设置曝光时间

                                            builder.set(CaptureRequest.BLACK_LEVEL_LOCK, false);//黑电平补偿是锁定
                                            builder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, (long) 10000);


                                            //通过 CaptureRequest.Builder 对象设置一些捕捉请求的配置
                                            //设置指定key的值
//                                            builder.set(CaptureRequest.CONTROL_AF_MODE,CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

                                            builder.addTarget(mPreviewSurface);//绑定目标Surface

                                            //5、通过 CameraCaptureSession发送CaptureRequest, capture表示只发一次请求, setRepeatingRequest表示不断发送请求.
                                            //不断的重复请求捕捉画面,常用于预览或者连拍场景。
                                            mCameraCaptureSession.setRepeatingRequest(builder.build(), null, null);
                                        } catch (CameraAccessException e1) {
                                            e1.printStackTrace();
                                        }
                                    }

                                    //该会话无法按照相应的配置发起请求时回调
                                    @Override
                                    public void onConfigureFailed(@NonNull CameraCaptureSession session) {

                                    }
                                }, null);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }

                        //当 camera 不再可用或打开失败时的回调,通常在该方法中进行资源释放的操作。
                        @Override
                        public void onDisconnected(@NonNull CameraDevice camera) {

                        }

                        //当 camera 打开失败时的回调,error 为具体错误原因
                        // 定义在 CameraDevice.StateCallback 类中。通常在该方法中也要进行资源释放的操作。
                        @Override
                        public void onError(@NonNull CameraDevice camera, int error) {

                        }
                    }, null);
                }catch (CameraAccessException e){
                    e.printStackTrace();
                }
            }


            //在surface发生format或size变化时调用。
            //SurfaceTexture缓冲大小变化
            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }


            //SurfaceTexture即将被销毁
            //在此处回调做一些释放资源的操作
            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }


            //SurfaceTexture通过updateImage更新
            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {

            }
        });
    }


    private void startRequestPermission(){
        //321为请求码
        ActivityCompat.requestPermissions(this,PERMISSIONS_STORAGE,321);
    }

}

但还是失败了。。。。设置了,但是显示的曝光时间没有降低

参考资料

https://www.jianshu.com/p/d83161e77e90

https://zhuanlan.zhihu.com/p/85271179(代码主要参考这个)

https://github.com/android/camera-samples(官方资料)

https://www.jianshu.com/p/23e8789fbc10

https://blog.csdn.net/afei__/article/details/51540188(一些bug的解决)

猜你喜欢

转载自blog.csdn.net/gwplovekimi/article/details/106076963