最近學習Android的camera模塊,本文先介紹一下camera2的api,而後給出android camera拍照的例子,講解一下camera 拍照的緣由知識,與你們共勉。html
- camera2 介紹
- android camera拍照功能介紹
- 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設備上保持徹底幀率。java
- 若是要操做相機設備,須要獲取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的主要功能。android
咱們打開android camera通常會出現一個預覽頁面,經過這個預覽頁面,我麼調用攝像頭,將前方的景放在這個預覽界面中,而後移動camera,預覽界面會隨之出現變化,實現這個預覽界面的view,通常有兩種選擇,SurfaceView或者TextureView,上面咱們也介紹了兩種view之間的區別,本文咱們選擇TextureView,由於TextureView設置動畫比較方便,咱們移動或者旋轉手機的時候,TextureView也應用相應的旋轉,這樣符合用戶的體驗。
能夠經過設置TextureView.SurfaceTextureListener來對TextureView表明的surface監聽。git
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大小正在調整。github
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實例打開相機
在執行manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);方法的時候,傳入了三個參數:api
- 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);
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移動的回調,一旦移動,會觸發這個回調,而後在回調中做出相應的改變。
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的原理。session
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/android-Camera2Basic/異步