Android Camera模組解析之拍照
最近學習Android的camera模組,本文先介紹一下camera2的api,然後給出android camera拍照的例子,講解一下camera 拍照的原因知識,與大家共勉。
- camera2 介紹
- android camera拍照功能介紹
一、camera2 介紹
- Camera api部分:
frameworks/base/core/java/android/hardware/camera2- Camera JNI部分:
frameworks/base/core/jni/android_hardware_Camera.cpp
編譯選項在目錄下的Android.bp
make libandroid_runtime -j1- Camera UI庫部分:
frameworks/av/camera/
編譯選項在目錄下的Android.bp
make libcamera_client -j1- Camera服務部分:
frameworks/av/services/camera/libcameraservice/
編譯選項在目錄下的Android.mk
make libcameraservice -j1- Camera HAL部分:
hardware/qcom/camera/
android.hardware.camera2開發包給開發者提供了一個操作相機的開發包,是api-21提供的,用於替代之前的Camera操作控類。該軟體包將攝像機裝置建模為管道,它接收捕獲單個幀的輸入請求,根據請求捕獲單個影象,然後輸出一個捕獲結果元資料包,以及一組請求的輸出影象緩衝區。請求按順序處理,多個請求可以立即進行。由於相機裝置是具有多個階段的管道,因此需要在移動中處理多個捕捉請求以便在大多數Android裝置上保持完全幀率。
- 如果要操作相機裝置,需要獲取CameraManager例項。CameraDevices提供了一系列靜態屬性集合來描述camera裝置,提供camera可供設定的屬性和裝置的輸出引數。描述這些屬性的是CameraCharacteristics例項,就是CameraManager的getCameraCharacteristics(String)方法。
- 為了捕捉或者流式話camera裝置捕捉到的圖片資訊,應用開發者必須建立一個CameraCaptureSession,這個camera session中包含了一系列相機裝置的輸出surface集合。目標的surface一般通過SurfaceView
, SurfaceTexture via Surface(SurfaceTexture), MediaCodec, MediaRecorder, Allocation, and ImageReader.- camera 預覽介面一般使用SurfaceView或者TextureView,,捕獲的圖片資料buffers可以通過ImageReader讀取。
- TextureView可用於顯示內容流。這樣的內容流可以例如是視訊或OpenGL場景。內容流可以來自應用程式的程序以及遠端程序。
- TextureView只能在硬體加速視窗中使用。在軟體中渲染時,TextureView將不會繪製任何內容。
- TextureView不會建立單獨的視窗,但表現為常規檢視。這一關鍵差異允許TextureView移動,轉換和使用動畫
- 之後,應用程式需要構建CaptureRequest,在捕獲單個圖片的時候,這些request請求需要定義一些請求的引數。
- 一旦設定了請求,就可以將其傳遞到活動捕獲會話,以進行一次捕獲或無休止地重複使用。兩種方法還具有接受用作突發捕獲/重複突發的請求列表的變體。重複請求的優先順序低於捕獲,因此在配置重複請求時通過capture()提交的請求將在當前重複(突發)捕獲的任何新例項開始捕獲之前捕獲。
- 處理完請求後,攝像機裝置將生成一個TotalCaptureResult
物件,該物件包含有關捕獲時攝像機裝置狀態的資訊以及使用的最終設定。如果需要舍入或解決相互矛盾的引數,這些可能與請求有所不同。相機裝置還將一幀影象資料傳送到請求中包括的每個輸出表面。這些是相對於輸出CaptureResult非同步生成的,有時基本上稍晚。
根據camera2的工作示意圖,畫出下面的camera類關係圖。下面使用camera功能的時候會詳細介紹一下camera2 的api的主要功能。
camera2 api關係圖.jpg
二、android camera拍照功能介紹
2.1 設定camera preview預覽頁面
我們開啟android camera一般會出現一個預覽頁面,通過這個預覽頁面,我麼呼叫攝像頭,將前方的景放在這個預覽介面中,然後移動camera,預覽介面會隨之出現變化,實現這個預覽介面的view,一般有兩種選擇,SurfaceView或者TextureView,上面我們也介紹了兩種view之間的區別,本文我們選擇TextureView,因為TextureView設定動畫比較方便,我們移動或者旋轉手機的時候,TextureView也應用相應的旋轉,這樣符合使用者的體驗。
可以通過設定TextureView.SurfaceTextureListener來對TextureView代表的surface監聽。
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
幾個監聽事件監聽當前surface的狀態。onSurfaceTextureAvailable表示當前的surface狀態可用,onSurfaceTextureSizeChanged表示當前的surface大小正在調整。
2.2 開啟相機
private void openCamera(int width, int height) {
if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
return;
}
setUpCameraOutputs(width, height);
configureTransform(width, height);
Activity activity = getActivity();
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
- 檢查當前camera許可權。
- 設定相機當前屬性和輸出圖片等等。
- 設定TextureView 選裝和移動的動畫屬性等等。
- 使用CameraManager例項開啟相機
2.3 相機開啟設定回撥
在執行manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);方法的時候,傳入了三個引數:
- mCameraId表示當前攝像頭的標識,我們手機中有好多個攝像頭,最新版的Mate20手機有3個後置攝像頭和1個前置攝像,可以通過manager.getCameraIdList()來獲取當前的cameraId集合。
- StateCallback是CameraDevice.StateCallback,這是表示相機裝置當前狀態的回撥。
下面StateCallback的眾多回調錶示當前相機的狀態,相機如果開啟的話應該執行什麼操作,相機如果斷開連線的話應該執行什麼操作等等。- 傳入的Handler處理camera當前的訊息。
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
Activity activity = getActivity();
if (null != activity) {
activity.finish();
}
}
};
mCameraOpenCloseLock是一個訊號量,一旦相機在openCamera到真正開啟這段時間,相機必須被獨佔,其他執行緒不能介入處理,不然會出現執行緒錯亂。一旦相機呈現出下一個狀態,就可以釋放這個訊號量了。
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
2.4 建立camera預覽session
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
mCaptureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Flash is automatically enabled when necessary.
setAutoFlash(mPreviewRequestBuilder);
// Finally, we start displaying the camera preview.
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest,
mCaptureCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
- 設定SurfaceTexture快取大小。
- 通過mCameraDevice.createCaptureRequest獲取CaptureResult.Builder物件,這個物件要和之前的Surface繫結,表示當前的capture請求是輸出到這個surface上的。
- mCameraDevice.createCaptureSession 建立capture session,在CameraCaptureSession.StateCallback的onConfigured回撥函式中,就是對當前camera移動的回撥,一旦移動,會觸發這個回撥,然後在回撥中作出相應的改變。
2.5 拍照
private void takePicture() {
lockFocus();
}
/**
* Lock the focus as the first step for a still image capture.
*/
private void lockFocus() {
try {
// This is how to tell the camera to lock focus.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
CameraMetadata.CONTROL_AF_TRIGGER_START);
// Tell #mCaptureCallback to wait for the lock.
mState = STATE_WAITING_LOCK;
mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Capture image的過程主要在 mCaptureSession.capture函式中,下面會從這個函式著手闡釋一下camera capture的原理。
2.6 關閉相機
private void closeCamera() {
try {
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
}
主要釋放camera相關的資源,手機中camera的資源具有獨佔性,如果在用完了不釋放的話,會造成別人使用的時候camera被佔用,session不關閉的,會造成嚴重的記憶體洩露。
大家也可以參考具體的原始碼https://github.com/googlesamples/andr