深入理解Camera 三 (相機應(yīng)用層)

和你一起終身學(xué)習(xí),這里是程序員Android
經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識點:
一、概覽
二、Camera Api v2
三、Camera Framework
四、Camera App Demo
相機應(yīng)用層
一、概覽
相機應(yīng)用處于整個框架的上層,在現(xiàn)實生活中,為了滿足各式各樣的應(yīng)用場景,會加入很多業(yè)務(wù)處理邏輯,但是一旦當(dāng)我們撥開繁雜的業(yè)務(wù)邏輯,便會發(fā)現(xiàn)其核心部分依然是通過調(diào)用谷歌制訂的一系列Camera Api接口來完成的,而所有的相機行為都包含在該接口中。
起初,相機系統(tǒng)采用的是Camera Api v1接口,它通過一個Camera 類以及該類中的幾個標(biāo)準(zhǔn)方法來實現(xiàn)整個相機系統(tǒng)的預(yù)覽、拍照以及錄像功能,控制邏輯比較簡單,同時也比較容易理解,但也正是這種簡單,導(dǎo)致了它無法逐幀控制底層硬件,無法通過元數(shù)據(jù)進行修改進而增強幀的表達能力,再加之應(yīng)用場景的多樣化趨勢,該接口在新功能的實現(xiàn)上顯得些許力不從心。面對該接口難以進一步擴展相機功能這一局面,谷歌在Andorid 5.0(API Level 21)便重新對Camera進行了設(shè)計,摒棄了Camera Api v1的設(shè)計邏輯,提出了一個全新的API – camera2,引入了Session以及Request概念,將控制邏輯統(tǒng)一成一個視圖,因此在使用上更加復(fù)雜,同時也支持了更多特性,比如逐幀控制曝光、感光度以及支持Raw格式的輸出等。并且由于對控制邏輯的高度抽象化,使得該接口具有很高的靈活性,可以通過簡單的操作實現(xiàn)30fps的全高清連拍的功能,總得來說,該接口極大地提高了對于相機框架的控制能力,同時也進一步大幅度提升了其整體性能。
谷歌提出Camera Api v2接口的同時,將其具體實現(xiàn)放入了Camera Framework中來完成,F(xiàn)ramework內(nèi)部負(fù)責(zé)解析來自App的請求,并且通過AIDL跨進程接口下發(fā)到Camera Service中進行處理,并且等待結(jié)果的回傳。接下來我們首先以Camera Api v2接口為主簡單講解下其邏輯含義,然后詳細(xì)梳理下Camera Framework對于它的實現(xiàn),最后以一個簡單App Demo為例,來介紹下如何使用該接口來控制整個相機體系。

程序員Android轉(zhuǎn)于網(wǎng)絡(luò)
二、Camera Api v2
在介紹Camera Api v2之前,首先我們來回顧下Api v1接口的基本邏輯,該接口主要通過一個Camera.java類來定義了所有的控制行為,通過定義諸如open、startPreview、takePicture、AutoFocus等標(biāo)準(zhǔn)的接口來實現(xiàn)打開設(shè)備、預(yù)覽、拍照以及對焦操作的功能,同時通過定義Camera.Parameters來實現(xiàn)了參數(shù)的讀取與設(shè)置,其中包括了幀率、圖片格式的控制,另外,通過定義了Camera.CameraInfo來實現(xiàn)了圖像元數(shù)據(jù)的獲取。而為了更加細(xì)致化地控制相機系統(tǒng)的Camera Api v2接口,相對于Api v1接口而言,復(fù)雜了許多,通過不同的接口類以及接口方法定義了復(fù)雜的相機系統(tǒng)行為,接下來我們來逐一進行介紹:
CameraManager
谷歌將CameraManager定義為一個系統(tǒng)服務(wù),通過Context.getSystemService來獲取,主要用于檢測以及打開系統(tǒng)相機,其中打開操作通過openCamera方法來完成。除此之外,還定義了getCameraCharacteristics方法來獲取當(dāng)前Camera 設(shè)備支持的屬性信息,而該屬性信息通過CameraCharacteristics來表示,其中包括了圖像數(shù)據(jù)的大小以及幀率等信息。
CameraDevice
代表了一個被打開的系統(tǒng)相機,類似于Camera Api v1中的Camera類,用于創(chuàng)建CameraCaptureSession以及對于最后相機資源的釋放。
CameraDevice.StateCallback
該類定義了一系列的回調(diào)方法,其實現(xiàn)交由App來完成,主要用于返回創(chuàng)建Camera設(shè)備的結(jié)果,一旦創(chuàng)建成功相機框架會通過回調(diào)其onOpened方法將CameraDevice實例給到App,如果失敗,則調(diào)用onError返回錯誤信息。
CameraCaptureSession
該類代表了一個具體的相機會話,建立了與Camera設(shè)備的通道,而之后對于Camera 設(shè)備的控制都是通過該通道來完成的。當(dāng)需要進行預(yù)覽或者拍照時,首先通過該類創(chuàng)建一個Session,并且調(diào)用其startRepeatingRequest方法開啟預(yù)覽流程,或者調(diào)用capture方法開始一次拍照動作。
CameraCaptureSession.StateCallback
該接口類定義了一系列回調(diào)方法,其實現(xiàn)交由App完成,主要用于返回創(chuàng)建CameraCaptureSession的結(jié)果,成功則通過onConfigured方法返回一個CameraCaptureSession實例,如果失敗則通過onConfigureFailed返回錯誤信息。
CameraCaptureSession.CaptureCallback
該接口類定義了一系列回調(diào)方法,用于返回來自Camera Framework的數(shù)據(jù)和事件,其中onCaptureStarted方法在下發(fā)圖像需求之后立即被調(diào)用,告知App此次圖像需求已經(jīng)收到,onCaptureProgressed方法在產(chǎn)生partial meta data的時候回調(diào),onCaptureCompleted方法在圖像采集完成,上傳meta data數(shù)據(jù)時被調(diào)用。
CaptureRequest
該類用于表示一次圖像請求,在需要進行預(yù)覽或者拍照時,都需要創(chuàng)建一個CaptureRequest,并且將針對圖片的一系列諸如曝光/對焦設(shè)置參數(shù)都加入到該Request中,通過CameraCaptureSessin下發(fā)到相機系統(tǒng)中。
TotalCaptureResult
每當(dāng)通過CameraDevice完成了一次CaptureRequest之后會生成一個TotalCaptureResult對象,該對象包含了此次抓取動作所產(chǎn)生的所有信息,其中包括關(guān)于硬件模塊(包括Sensor/lens/flash)的配置信息以及相機設(shè)備的狀態(tài)信息等。
CaptureResult
該類代表了某次抓取動作最終生成的圖像信息,其中包括了此次關(guān)于硬件軟件的配置信息以及輸出的圖像數(shù)據(jù),以及顯示了當(dāng)前Camera設(shè)備的狀態(tài)的元數(shù)據(jù)(meta data),該類并不保證擁有所有的圖像信息。
三、Camera Framework
基于接口與實現(xiàn)相分離的基本設(shè)計原則,谷歌通過Camera Api 接口的定義,搭建起了App與相機系統(tǒng)的橋梁,而具體實現(xiàn)便是由Camera Framework來負(fù)責(zé)完成的。在采用Camera Api v1接口的時期,該部分是通過JNI層來進行java到C++的轉(zhuǎn)換,進而到達native層,而在native層會通過實現(xiàn)CameraClient建立與Camera Service的通訊 ,整個過程比較繁瑣,使得整體框架略顯繁雜,而隨著Camera Api v2的提出,在該層便大量使用AIDL機制,直接在Java層建立與Camera Service的通信,進一步簡化了整體框架。,接下來我們以幾個主要接口為主線,簡單梳理下其具體實現(xiàn)。

程序員Android 轉(zhuǎn)于網(wǎng)絡(luò)
CameraManager
實現(xiàn)主要在CameraManager.java中,通過CameraManager查詢、獲取以及打開一個Camera 設(shè)備。在該類中還實現(xiàn)了內(nèi)部類CameraManagerGlobal,該類繼承于ICameraServiceListener.Stub,在打開相機設(shè)備的時候,在內(nèi)部會獲取到ICameraService遠(yuǎn)程代理,并且調(diào)用ICameraService的addListener方法將自己注冊到Camera Service中,一旦Camera Service狀態(tài)有所變更便會通過其實現(xiàn)的回調(diào)方法通知到Camera Manager服務(wù),另外,該類還通過調(diào)用ICameraService.connectDevice()方法獲取到Camera Service中的CameraDevice遠(yuǎn)程代理,并且將該代理傳入CameraDeviceImpl中,進而與Camera Service建立了連接。
CameraDeviceImpl
該類定義在CameraDeviceImpl.java文件中,繼承并實現(xiàn)了CameraDevice接口,代表了一個相機設(shè)備,可以完成CameraCaptureSession的創(chuàng)建以及CaptureRequest創(chuàng)建等工作,內(nèi)部定義了CameraDeviceCallbacks類(該類繼承于ICameraDeviceCallbacks.Stub,對應(yīng)于Camera Service中的 ICameraDeviceCallbacks接口),用于接收來自Camera Service中的Camera Device的狀態(tài)回調(diào),并且內(nèi)部維護著一個Camera Service 的遠(yuǎn)程ICameraDevice代理,進而可以下發(fā)圖像請求到Camera Service中。
CameraCaptureSessionImpl
該類定義在CameraCaptureSessionImpl.java文件中,繼承并實現(xiàn)了CameraCaptureSession接口,每一個相機設(shè)備在一個時間段中,只能創(chuàng)建并存在一個CameraCaptureSession,其中該類包含了兩種Session,一種是普通的,適用于一般情況下的會話操作,另一種是用于Reprocess流程的會話操作,該流程主要用于對于現(xiàn)有的圖像數(shù)據(jù)進行再處理的操作。該類維護著來自實例化時傳入的Surface列表,這些Surface正是包含了每一個圖像請求的數(shù)據(jù)緩沖區(qū)。
除了以上這幾個接口,還有幾個接口是需要App部分進行實現(xiàn)的,用于返回App所需要的對象或者數(shù)據(jù):
CameraDevice.StateCallback
被App端進行繼承并實現(xiàn),用于在調(diào)用CameraManager的openCamera方法時,通過參數(shù)的形式傳入Framework,在Framework中,一旦CameraDeviceImpl創(chuàng)建成功便通過其中的onOpened方法將其返回給App,如果失敗,便會通過其他方法返回給App錯誤信息。
CameraCaptureSession.StateCallback
被App端進行繼承并實現(xiàn),用于在調(diào)用CameraDevice的createCaptureSession方法時作為參數(shù)傳入Framework中,一旦創(chuàng)建成功,F(xiàn)ramework便會通過調(diào)用該類的onConfigured接口返回一個CameraCaptureSessionImpl的對象,如果失敗,F(xiàn)ramework會調(diào)用其onConfigureFailed方法將錯誤信息返回至App。
CameraCaptureSession.CaptureCallback
被App端進行繼承并實現(xiàn),App通過調(diào)用CameraCaptureSessionImpl的setReaptingRequest或者capture方法是作為參數(shù)傳入Framework,一旦Framework接收到來自CameraService的數(shù)據(jù)時,便會通過調(diào)用這個回調(diào)類將數(shù)據(jù)發(fā)送至App中。

程序員Android轉(zhuǎn)于網(wǎng)絡(luò)
Camera Framework 中針對幾個接口的調(diào)用流程如上圖,接下來我們依次進行分析:
a) openCamera
當(dāng)用戶打開相機應(yīng)用時,會去調(diào)用該方法打開一個相機設(shè)備,其中該方法最終經(jīng)過層層調(diào)用會調(diào)用到Camera Framework中的openCameraDeviceUserAsync方法,在該方法中主要做了三件事:
首先是獲取ICameraService代理,調(diào)用其getCameraInfo方法獲取當(dāng)前設(shè)備的屬性。
其次是實例化了一個CameraDeviceImpl對象,并將來自App的CameraDevice.StateCallback接口存入該對象中,再將CameraDeviceImpl中的內(nèi)部類CameraDeviceCallback作為參數(shù)通過ICameraService的connectDevice方法傳入Camera Service去打開并獲取一個ICameraDeviceUser代理,并將該代理存入CameraDeviceImpl中進行管理。
最后通過App傳入的回調(diào)將CameraDeviceImpl返回給App使用,至此整個流程便完成了。
b) createCaptureSession
在打開相機設(shè)備之后便需要去創(chuàng)建一個相機會話,用于傳輸圖像請求,其最終實現(xiàn)是調(diào)用該方法來進行實現(xiàn)的,而該方法會去調(diào)用到Camera Framework中的createCaptureSessionInternal方法,該方法主要做了兩件事:
首先調(diào)用configureStreamsChecked方法來配置數(shù)據(jù)流。
其次實例化了一個CameraCaptureImpl對象,并通過傳入CameraCaptureSession.StateCallback回調(diào)類將該對象發(fā)送至至App中。
而在configureStreamsChecked方法中會去調(diào)用ICameraDeviceUser代理的一系列方法進行數(shù)據(jù)流配置,其中調(diào)用cancelRequest方法停掉當(dāng)前的的預(yù)覽流程,調(diào)用deleteStream方法刪除之前的數(shù)據(jù)流,調(diào)用createStream創(chuàng)建新的數(shù)據(jù)流,最后調(diào)用endConfigure來進行數(shù)據(jù)流的配置工作,針對性的配置便在最后這個endConfigure方法中。
c) createCaptureRequest
在創(chuàng)建并獲取相機會話之后,便可以開始下發(fā)圖像請求了,而在此之前,需要通過該方法來創(chuàng)建一個CaptureRequest,一旦調(diào)用該方法,最終會調(diào)用到Camera Service中ICameraDeviceUser的createDefaultRequest方法來創(chuàng)建一個默認(rèn)配置的CameraMetadataNative,其次實例化一個CaptureRequest.Builder對象,并將剛才獲取的CameraMetadataNative傳入其中,之后返回該CaptureRequest.Builder對象,在App中,直接通過調(diào)用該Buidler對象的build方法,獲取一個CaptureRequest對象。
CaptureRequest對象也創(chuàng)建成功了,接下來需要下發(fā)圖像請求了,一般常用請求分為兩種,一個是預(yù)覽一個是拍照。
d) setRepeatingRequest
App調(diào)用該方法開始預(yù)覽流程,通過層層調(diào)用最終會調(diào)用到Framework中的submitCaptureRequest方法,該方法主要做了兩件事:
首先調(diào)用CameraService層CameraDeviceUser的submitRequestList方法,將此次Request下發(fā)到CameraService中。
其次將App通過參數(shù)傳入的CameraCaptureSession.CaptureCallback對象存到CameraDeviceImpI對象中。
接下來看下拍照請求的處理流程:
e) capture
該方法最終也會調(diào)用到Framework中的submitCaptureRequest方法,接下來邊和預(yù)覽流程大致相同,會去調(diào)用Camera Service 中的ICameraDeviceUser的submitRequestList方法傳入請求,之后將App實現(xiàn)的回調(diào)對象存入CameraDeviceImpl對象中。
f) onCaptureProgressed
一旦Request下發(fā)到Camera Service之后,當(dāng)?shù)讓由闪薖artial Meta Data數(shù)據(jù),Camera Service會調(diào)用通過調(diào)用在打開相機設(shè)備時傳入的ICameraDeviceCallback代理,通過其onResultReceived方法將數(shù)據(jù)傳回Framework,之后調(diào)用App傳入的CameraCaptureSession.CaptureCallback中的onCaputreProgressed方法將結(jié)果回傳至App進行解析以及后處理。
g) onCaptureCompleted
一旦Request下發(fā)到Camera Service之后,當(dāng)?shù)讓由闪薓eta data數(shù)據(jù),Camera Service會調(diào)用通過調(diào)用在打開相機設(shè)備時傳入的ICameraDeviceCallback代理,通過其onResultReceived方法將數(shù)據(jù)傳回Framework,之后調(diào)用App傳入的CameraCaptureSession.CaptureCallback中的onCaputreCompleted方法將結(jié)果回傳至App進行解析以及后處理。
h) onImageAvailable
之前已經(jīng)通過兩個回調(diào)接口onCaptureProgressed以及onCaptureCompleted方法將meta data上傳到了App,一般情況下,圖像數(shù)據(jù)會在他們之后上傳,而且這個上傳過程并不經(jīng)過Camera Framework,而是通過BufferQueue來進行的,當(dāng)Camera Service接收到底層傳來的圖像數(shù)據(jù),便會立即調(diào)用processCaptureResult_3_4方法,該方法中會去調(diào)用BufferQueue中生產(chǎn)者角色的Surface的queueBuffer方法,將數(shù)據(jù)入隊并通知消費者去消費,而此時的消費者正是App端的ImageReader,并經(jīng)過一層層回調(diào),最終會通過調(diào)用ImageReader的onImageAvailable方法,通知ImageReader去將數(shù)據(jù)取出,并做后期操作。
從上面的梳理不難發(fā)現(xiàn),整個Camera Framework除了是對Camera Api v2的實現(xiàn)外,還承擔(dān)著與Camera Service跨進程通信的任務(wù),充當(dāng)了一個位于App與Service之間的中轉(zhuǎn)站的角色。
四、Camera App Demo
經(jīng)過上面的梳理總結(jié),我們已經(jīng)對整個Camera Api v2接口以及實現(xiàn)都有了一個較為深入的認(rèn)識,但是認(rèn)識暫時僅僅停留在代碼層面,為了更好理解其功能,接下來我們以一個簡單的相機應(yīng)用入手來加深下對接口的使用流程的理解:
該相機Demo比較簡單,界面有兩個元素,一個是用于預(yù)覽顯示的TextureView,以及一個用于拍照的按鈕,整個代碼就采用了一個MainActiviy,相機操作就在該類中進行,其主要代碼如下:
package com.bruce.camerademo1;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.ImageFormat;
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.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
TextureView mTextureView = null;
CameraManager mCamManager = null;
CameraDevice mCamDevice = null;
CameraCaptureSession mCamSession = null;
Button mClick = null;
//預(yù)覽數(shù)據(jù)大小 1080 × 720
private Size mPreviewSize = new Size(1080, 720);
//拍照數(shù)據(jù)圖像大小 1080 × 720
private Size mCaptureSize = new Size(1080, 720);
private Surface mPreviewSurface = null;
private CaptureRequest.Builder mCaptureRequestBuilder = null;
private CaptureRequest mCaptureRequest = null;
private ImageReader mImageReader = null;
static String LOG_TAG = "Camera";
private void log(String s) {
if (s.isEmpty()) {
Log.e(LOG_TAG, "s is empty");
return;
}
Log.i(LOG_TAG, "" + s);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
mTextureView = findViewById(R.id.textview);
mTextureView.setSurfaceTextureListener(textureListener);
mClick = findViewById(R.id.button);
mImageReader = ImageReader.newInstance(mCaptureSize.getWidth(), mCaptureSize.getHeight(), ImageFormat.JPEG, 2);
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireNextImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
//由緩沖區(qū)存入字節(jié)數(shù)組
buffer.get(bytes);
//字節(jié)數(shù)組轉(zhuǎn)換為jpeg格式圖片,并存儲在設(shè)備中
doByte2JpegFile(bytes);
image.close();
}
}, null /*mCameraHandler*/);
mClick.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//創(chuàng)建CaptureRequest.Builder,TEMPLATE_STILL_CAPTURE代表了此Request是用于拍照
CaptureRequest.Builder b = mCamDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
b.addTarget(mImageReader.getSurface());
mCamSession.capture(b.build(), new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
//返回result --> meta data
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
});
}
public TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, 1);
}
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
//獲取CameraManager服務(wù)
mCamManager = (CameraManager)getSystemService(Context.CAMERA_SERVICE);
try {
//打開主攝
mCamManager.openCamera("0", mStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
//App需要實現(xiàn)該接口,用于接收CameraDevice實例
public CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice camera) {
if (camera != null) {
log("camera is not null");
}
log("onOpened");
//返回CameraDevice,將其存入mCamDevice
mCamDevice = camera;
startPreview();
}
@Override
public void onDisconnected(CameraDevice camera) {
camera.close();
mCamDevice = null;
}
@Override
public void onError(CameraDevice camera, int error) {
camera.close();
mCamDevice = null;
}
};
public void startPreview() {
SurfaceTexture mSurfaceTexture = mTextureView.getSurfaceTexture();
// 設(shè)置TextureView 用于顯示的緩沖區(qū)大小
mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
//創(chuàng)建Surface,用于顯示預(yù)覽數(shù)據(jù)
mPreviewSurface = new Surface(mSurfaceTexture);
try {
//創(chuàng)建CameraCaptureSession,App需要實現(xiàn)CameraCaptureSession.StateCallback用于接收CameraCaptureSession實例
mCamDevice.createCaptureSession(Arrays.asList(mPreviewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession session) {
try {
//創(chuàng)建用于預(yù)覽的CaptureRequest.Builder,進而得到CaptureRequest
CaptureRequest.Builder b = mCamDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
b.addTarget(mPreviewSurface);
CaptureRequest r = b.build();
mCamSession = session;
//下發(fā)預(yù)覽需求
mCamSession.setRepeatingRequest(r, mPreviewCaptureCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
CameraCaptureSession.CaptureCallback mPreviewCaptureCallback = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
//返回result --> meta data
}
};
@Override
public void onReady(CameraCaptureSession session) {
super.onReady(session);
}
@Override
public void onConfigureFailed(CameraCaptureSession session) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
log("onSurfaceTextureSizeChanged Enter");
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
log("onSurfaceTextureDestroyed Enter");
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
log("onSurfaceTextureUpdated Enter");
}
};
private String doByte2JpegFile(byte[]... jpeg) {
File photo = new File(Environment.getExternalStorageDirectory(), "photo-test.jpg");
log("dir : " + Environment.getExternalStorageDirectory());
if (photo.exists()) {
photo.delete();
log("photo exists");
} else {
log("photo not exists");
}
try {
FileOutputStream fos = new FileOutputStream(photo.getPath());
log("photo path : " + photo.getPath());
fos.write(jpeg[0]);
fos.close();
}
catch (java.io.IOException e) {
Log.e(LOG_TAG, "Exception in photoCallback", e);
}
log("get jpeg files done");
return(null);
}
}
代碼比較簡單,其主要邏輯如下:
a) 初始化
隨著應(yīng)用的打開,首先在MainActivity的onResume方法中去初始化用于拍照的按鈕和接收數(shù)據(jù)的ImageReader,并且設(shè)置其各自回調(diào)方法。
b) 獲取CameraManager/打開Camera 設(shè)備
其次在TextureView.SurfaceTextureListener中的回調(diào)方法onSurfaceTextureAvailable會被調(diào)用,在該方法中會去獲取CameraManager服務(wù),并打調(diào)用其openCamera方法打開后主攝相機設(shè)備,并通過回調(diào)接口獲取該設(shè)備,緊接著調(diào)用startPreview方法。
c) 創(chuàng)建Camera Capture Session/下發(fā)預(yù)覽需求
在startPreview方法中會去下發(fā)預(yù)覽需求,主要工作有設(shè)置TextureView緩沖區(qū)大小,創(chuàng)建用于接收預(yù)覽數(shù)據(jù)的Surface,并調(diào)用CameraDevice的createCaptureSession方法創(chuàng)建CameraCaptureSession,在其回調(diào)接口onConfigured方法中,去創(chuàng)建CaptureRequest,并調(diào)用CameraCaptureSession的setRepeatingRequest方法下發(fā)Request到相機框架中。
d) 返回預(yù)覽Metadata/圖像數(shù)據(jù)
在完成了預(yù)覽需求的Request的下發(fā)工作后,相機框架便會不斷通過傳入的CaptureCallback中的onCaptureComplete方法上傳Meta Data以及通過BufferQueue框架上傳圖像數(shù)據(jù)進行預(yù)覽顯示。
e) 下發(fā)拍照需求
當(dāng)點擊應(yīng)用界面的拍照按鈕的時候,會觸發(fā)按鈕的View.OnClickListener監(jiān)聽類中的onClick方法,在該方法中,初始化了一個用于拍照的CaptureRequest,并且通過調(diào)用CameraCaptureSession的capture方法下發(fā)拍照需求.
f) 返回拍照Metadata/圖像數(shù)據(jù)
一旦拍照圖像數(shù)據(jù)生成,便會通過回調(diào)接口CaptureCallback中的onCaptureComplete方法上傳Meta Data以及通過BufferQueue框架上傳圖像數(shù)據(jù)到ImageReader中,觸發(fā)其onImageAvailable方法,在該方法中通過ImageReader的acquireNextImage獲取到拍照圖像數(shù)據(jù),并通過doByte2JpegFile將其轉(zhuǎn)存外JPEG格式的圖片保存在設(shè)備中。
Camera App作為整個框架體系的最上層,直接面向的主體是普通用戶,其關(guān)鍵性不言而喻,一點點的卡頓或者停滯都會降低用戶體驗,所以為了保證整個框架的穩(wěn)定性以及高效性,谷歌重新設(shè)計了Camera 接口Api v2,由于該接口將控制邏輯高度抽象成了一個控制視圖,因此可以逐幀控制硬件參數(shù),進而實現(xiàn)一系列強大的功能,比如可以直接在預(yù)覽過程中,手動控制曝光、感光度、對焦以及白平衡的參數(shù),動態(tài)地輸出不同效果的圖像,又比如可以利用該接口與HDR算法相配合,實現(xiàn)高動態(tài)成像效果。而其中,對于Api v2接口的實現(xiàn)是在Camera Framework中完成的,由上面分析可以看出,其內(nèi)部并沒有采用十分復(fù)雜的控制邏輯,整套代碼流程清晰明朗,而這樣的設(shè)計,進一步保證了該層的穩(wěn)定性以及高效性,為整個相機框架體系的穩(wěn)定奠定了堅實的基礎(chǔ)。
原文鏈接:?https://blog.csdn.net/u012596975/article/details/107137110
至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請聯(lián)系小編刪除,歡迎您的建議與指正。同時期待您的關(guān)注,感謝您的閱讀,謝謝!
友情推薦:
至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請聯(lián)系小編刪除,歡迎您的建議與指正。同時期待您的關(guān)注,感謝您的閱讀,謝謝!
點個在看,方便您使用時快速查找!
