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的解決)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章