之前博文《Android學習筆記之——調用前後置相機的視頻流》已經實現了視頻流的捕獲與顯示了(通過 camera1 setPreviewCallback 類似功能實現)。本博文繼續學習一下Android中的camera相關的API
目錄
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使用流程大體如下:
步驟
- 通過
context.getSystemService(Context.CAMERA_SERVICE)
獲取CameraManager
. - 調用
CameraManager .open()
方法在回調中得到CameraDevice
. - 通過
CameraDevice.createCaptureSession()
在回調中獲取CameraCaptureSession
. - 構建
CaptureRequest
, 有三種模式可選 預覽/拍照/錄像. - 通過
CameraCaptureSession
發送CaptureRequest
, capture表示只發一次請求, setRepeatingRequest表示不斷髮送請求. - 拍照數據可以在
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的解決)