Android Camera模塊解析之拍照

最近學習Android的camera模塊,本文先介紹一下camera2的api,而後給出android camera拍照的例子,講解一下camera 拍照的緣由知識,與你們共勉。html

  • camera2 介紹
  • android camera拍照功能介紹

1、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設備上保持徹底幀率。java

  • TextureView可用於顯示內容流。這樣的內容流能夠例如是視頻或OpenGL場景。內容流能夠來自應用程序的進程以及遠程進程。
  • TextureView只能在硬件加速窗口中使用。在軟件中渲染時,TextureView將不會繪製任何內容。
  • TextureView不會建立單獨的窗口,但表現爲常規視圖。這一關鍵差別容許TextureView移動,轉換和使用動畫
  • 以後,應用程序須要構建CaptureRequest,在捕獲單個圖片的時候,這些request請求須要定義一些請求的參數。
  • 一旦設置了請求,就能夠將其傳遞到活動捕獲會話,以進行一次捕獲或無休止地重複使用。兩種方法還具備接受用做突發捕獲/重複突發的請求列表的變體。重複請求的優先級低於捕獲,所以在配置重複請求時經過capture()提交的請求將在當前重複(突發)捕獲的任何新實例開始捕獲以前捕獲。
  • 處理完請求後,攝像機設備將生成一個TotalCaptureResult
    對象,該對象包含有關捕獲時攝像機設備狀態的信息以及使用的最終設置。若是須要舍入或解決相互矛盾的參數,這些可能與請求有所不一樣。相機設備還將一幀圖像數據發送到請求中包括的每一個輸出表面。這些是相對於輸出CaptureResult異步生成的,有時基本上稍晚。

根據camera2的工做示意圖,畫出下面的camera類關係圖。下面使用camera功能的時候會詳細介紹一下camera2 的api的主要功能。android

2、android camera拍照功能介紹

2.1 設置camera preview預覽頁面

咱們打開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

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);方法的時候,傳入了三個參數: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);

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的原理。session

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/android-Camera2Basic/異步

相關文章
相關標籤/搜索