android Camera2 API使用詳解
阿新 • • 發佈:2019-02-16
由於最近需要使用相機拍照等功能,鑑於老舊的相機API問題多多,而且新的裝置都是基於安卓5.0以上的,於是本人決定研究一下安卓5.0新引入的Camera2 API 來實現 Camera2API地址
首先我們來熟悉一下官方給的這幾個圖:
這裡引用了管道的概念將安卓裝置和攝像頭之間聯通起來,系統向攝像頭髮送
Capture 請求,而攝像頭會返回 CameraMetadata。這一切建立在一個叫作 CameraCaptureSession 的會話中。
其中 CameraManager 是那個站在高處統管所有攝像投裝置(CameraDevice)的管理者,而每個 CameraDevice 自己會負責建立 CameraCaptureSession 以及建立 CaptureRequest。CameraCharacteristics 是 CameraDevice 的屬性描述類,非要做個對比的話,那麼它與原來的 CameraInfo 有相似性。 類圖中有著三個重要的 callback,雖然這增加了閱讀程式碼的難度,但是你必須要習慣,因為這是新包的風格。其中 CameraCaptureSession.CaptureCallback 將處理預覽和拍照圖片的工作,需要重點對待。這些類是如何相互配合的?下面是簡單的流程圖。
有了這三張圖,那麼接下來就好了理解了,我們按照拍照流程的指示,來吧整個過程走一遍
1首先定義一個SufaceView 用來實現預覽照片用
surfaceView=(SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { initCamera(); } @Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } });
其中initCamera2()方法是用來初始化相機的方法
2獲取Camera ID,該ID是用來開啟相機的關鍵,一般後置攝像頭是0,前置攝像頭是1,這裡我們選擇後置攝像頭做詳解
mCameraID = "" + CameraCharacteristics.LENS_FACING_BACK;//後攝像頭
3通過Camera ID 來開啟攝像頭,這裡我們需要使用CamerManager,這是類是一個管理服務類,值得注意的是,開啟攝像頭是一個相當複雜的過程,不能直接在主執行緒中直接執行,其核心程式碼為:
HandlerThread handlerThread=new HandlerThread("Camera2"); handlerThread.start(); childHandler=new Handler(handlerThread.getLooper()); mainHandler=new Handler(getMainLooper());
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在這裡處理拍照得到的臨時照片 例如,寫入本地 @Override public void onImageAvailable(ImageReader reader) { mCameraDevice.close(); surfaceView.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); // 拿到拍照照片資料 Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes);//由緩衝區存入位元組陣列 final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); if (bitmap != null) { iv_show.setImageBitmap(bitmap); } } }, mainHandler);
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } //開啟攝像頭 cameraManager.openCamera(mCameraID, stateCallback, mainHandler); } catch (CameraAccessException e) { e.printStackTrace(); }注意開啟攝像頭需要許可權:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
4 開啟相機後有一個回撥,stateCallback,該回調是用來返回相機是否正常開啟的狀態的
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) {//開啟攝像頭 mCameraDevice = camera; //開啟預覽 takePreview(); } @Override public void onDisconnected(CameraDevice camera) {//關閉攝像頭 if (null != mCameraDevice) { mCameraDevice.close(); MainActivity.this.mCameraDevice = null; } } @Override public void onError(CameraDevice camera, int error) {//發生錯誤 Toast.makeText(MainActivity.this, "攝像頭開啟失敗", Toast.LENGTH_SHORT).show(); } };
5 相機開啟成功後,執行回撥中的onOpen方法,在該方法中,我們實現讓影象顯示在介面上
/** * 開始預覽 */ private void takePreview() { try { // 建立預覽需要的CaptureRequest.Builder final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 將SurfaceView的surface作為CaptureRequest.Builder的目標 previewRequestBuilder.addTarget(surfaceHolder.getSurface()); // 建立CameraCaptureSession,該物件負責管理處理預覽請求和拍照請求 mCameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③ { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { if (null == mCameraDevice) return; // 當攝像頭已經準備好時,開始顯示預覽 mCameraCaptureSession = cameraCaptureSession; try { // 自動對焦 previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 開啟閃光燈 previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 顯示預覽 CaptureRequest previewRequest = previewRequestBuilder.build(); mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Toast.makeText(MainActivity.this, "配置失敗", Toast.LENGTH_SHORT).show(); } }, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
6,預覽完成後,接下來就是拍照了,注意某些手機的攝像頭會和正常手機的攝像頭另類,成像會成180度的倒立像,比如nexus 5x,這時候只需要設定
rORIENTATIONS的值來調整角度就可以
/** * 拍照 */ private void takePicture() { if (mCameraDevice == null) return; // 建立拍照需要的CaptureRequest.Builder final CaptureRequest.Builder captureRequestBuilder; try { captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 將imageReader的surface作為CaptureRequest.Builder的目標 captureRequestBuilder.addTarget(mImageReader.getSurface()); // 自動對焦 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 自動曝光 captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 獲取手機方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根據裝置方向計算設定照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)+rORIENTATIONS); //拍照 CaptureRequest mCaptureRequest = captureRequestBuilder.build(); mCameraCaptureSession.capture(mCaptureRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }