android camera2 API流程分析

Android camera2 API流程分析
Android5.0以後,新推出來了一個類,android.hardware.camera2,與原來的camera的類實現照相和拍視頻的流程有所不一樣,原來的camera的類並無深刻分析。在作項目的時候,因爲須要涉及到這方面的知識,本身學了一下。因爲本人英文也不是很優秀,看着看着還要看前人的總結。這個是在半年前就簡單總結了一下,如今po上來。若有錯誤,敬請指教!android

1、整體分析session


Camera2流程示意圖:ide


CameraManager:管理全部的攝像頭(CameraDevice)設備的管理者,打開攝像頭等功能。函數

 

CameraDevice:一個手機設備通常有兩個攝像頭(CameraDevice),前置和後置。該類經過CameraCharacteristics對象提供攝像頭的硬件信息,配置信息和輸出參數等。ui

 

CameraCaptureSession:經過CameraDevice 中createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)建立一個CaptureSession會話,全部的CaptureRequest和返回的data都在這個會話中進行。其中,該類中的capture (CaptureRequest request, CameraCaptureSession.CaptureCallback listener, Handler handler)的功能是捕獲一次(one-shot),通常用於照相setRepeatingRequest (CaptureRequest request, CameraCaptureSession.CaptureCallbacklistener, Handler handler)是不停的發出capture的請求,也就是一直在捕獲畫面,通常用於捕獲畫面輸出至預覽界面或者錄製視頻。Capture()比setRepeatingRequest ()優先級高,當在setRepeatingRequest 時進行Capture,會先處理Capture,而後繼續setRepeatingRequest 。(PS:能夠根據平時使用相機時,首先咱們看到的預覽界面是setRepeatingRequest 顯示出來的,當點擊拍照時執行Capture,而後又出現預覽界面繼續實行setRepeatingRequest )。.net

 

CameraRequest:request中定義了照相效果的一些參數,而且必須使用addTarget()函數爲這個request添加一個target surface,在最後CameraDevice返回的數據送到這個target surface中。在android camera2的API文檔中,這個target surface能夠是Surface View,Surface Texture,將返回的數據傳遞到預覽界面中;還能夠是MediaRecorder或mageReader,將返回的數據傳給這兩個類,進行進一步處理,造成視頻文件或者圖片。線程

 

TotalCaptureResult:繼承CaptureResult類,CaptureResult繼承CameraMetadata類。包含camera device的狀態信息。code


2、照相流程分析(參考Camera2Basic)orm


            Camera2Basic在顯示預覽界面和拍照時建立了一個session,兩個request,mPreviewRequest和captureBuilder.build()分別將數據返回給預覽界面和Image。視頻


顯示preview的代碼:


private void openCamera(int width, int height) {
        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);//根據mCameraId打開前置或者後置攝像頭
                                                                            //mBackgroundHandler是處理打開攝像頭的線程
                                                                            //mStateCallback打開攝像頭後,進入這個回調函數
        } catch (CameraAccessException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
        }
}
 
 
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
 
        @Override
        public void onOpened(CameraDevice cameraDevice) {//若成功打開,進入這個函數
            // This method is called when the camera is opened.  We start camera preview here.
            mCameraOpenCloseLock.release();
            mCameraDevice = cameraDevice;
            createCameraPreviewSession();//建立顯示預覽界面的函數
        }
            //………
}
 
 
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);//target爲surface,就是手機的界面
 
            // Here, we create a CameraCaptureSession for camera preview.
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),//拍照的session,注意這裏有兩個surface
                                                                                                //一個是手機的界面,一個是圖片。
                                                                                                //也就是說,這個session造成的數據流,
                                                                                                //能夠一個傳向手機界面,一個造成圖片
                                                                                                //具體看Caputre()或者SetRepeatingRequest函數裏面的
                                                                                                //參數request的addtarget()裏面的值
                                                                                                
                    new CameraCaptureSession.StateCallback() {
 
                        @Override
                        public void onConfigured(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.
                                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                                        CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
 
                                // Finally, we start displaying the camera preview.
                                mPreviewRequest = mPreviewRequestBuilder.build();
                                mCaptureSession.setRepeatingRequest(mPreviewRequest,//mPreviewRequest的target是手機界面的surface,就是造成預覽
                                                                                    //所以須要setRepeatingRequest,持續捕獲幀造成視頻
                                        mCaptureCallback, mBackgroundHandler);
                            } catch (CameraAccessException e) {
                                e.printStackTrace();
                            }
                        }
 
                      //……….
    }

CameraDevice.StateCallback:

當CameraDevice狀態改變(打開或關閉)後調用該函數,通常在該函數執行以下功能:

建立CameraDevice.TEMPLATE_PREVIEW類型的previewRequest,設置addTarget()爲preview的surface。

建立session,這個session有兩個request,所以要把request的target surface都放到List中,createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)中的List爲preview和ImageReader的surface。CameraCaptureSession.StateCallback裏面進行setRepeatingCapture(),將捕獲的畫面顯示在preview上。mCaptureCallback說是捕獲完成後的回調函數,暫不分析。

拍照(捕獲靜態圖像)的代碼:

private void captureStillPicture() {
        try {
            final Activity activity = getActivity();
            if (null == activity || null == mCameraDevice) {
                return;
            }
            // This is the CaptureRequest.Builder that we use to take a picture.
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(mImageReader.getSurface()); //target是image
 
            // Use the same AE and AF modes as the preview.
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
 
            // Orientation
            int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
 
            CameraCaptureSession.CaptureCallback CaptureCallback
                    = new CameraCaptureSession.CaptureCallback() {
 
                @Override
                public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
                                               TotalCaptureResult result) {
                    showToast("Saved: " + mFile);
                    unlockFocus();
                }
            };
 
            mCaptureSession.stopRepeating();//停掉以前的setrepeating的持續不斷的捕獲
            mCaptureSession.capture(captureBuilder.build(), CaptureCallback, null);//capture的時候,數據流造成image
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

相似上述分析,不過此時request的類型cameraDevice.TEMPLATE_STILL_CAPTURE而且addTarget()的對象ImageReader,另外setRepeatingCapture換成了capture。即捕獲一個frame,返回至ImageReader中造成圖片。


3、錄像流程分析(參考Camera2Video)
private void openCamera(int width, int height) {
        final Activity activity = getActivity();
        if (null == activity || activity.isFinishing()) {
            return;
        }
        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.");
            }
            String cameraId = manager.getCameraIdList()[0];//後置攝像頭的id
 
            // Choose the sizes for camera preview and video recording
            CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
            StreamConfigurationMap map = characteristics
                    .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
            mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                    width, height, mVideoSize);
 
            int orientation = getResources().getConfiguration().orientation;
            if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
                mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            } else {
                mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
            }
            configureTransform(width, height);
            mMediaRecorder = new MediaRecorder();
            manager.openCamera(cameraId, mStateCallback, null);//打開相機,進入回調函數
           //……
}
 
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
 
        @Override
        public void onOpened(CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
            startPreview(); //進入這個函數
            mCameraOpenCloseLock.release();
            if (null != mTextureView) {
                configureTransform(mTextureView.getWidth(), mTextureView.getHeight());
            }
        }
        //……
}
 
    private void startPreview() {
        if (null == mCameraDevice || !mTextureView.isAvailable() || null == mPreviewSize) {
            return;
        }
        try {
            setUpMediaRecorder();//設置mediarecoder的參數,具體的介紹看android developer文檔
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            assert texture != null;
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);//此時request的參數是record
            List<Surface> surfaces = new ArrayList<Surface>();
 
            Surface previewSurface = new Surface(texture);
            surfaces.add(previewSurface);
            mPreviewBuilder.addTarget(previewSurface);//target是預覽界面
 
            Surface recorderSurface = mMediaRecorder.getSurface();
            surfaces.add(recorderSurface);
            mPreviewBuilder.addTarget(recorderSurface);//target是mediarecorder
 
            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
 
                @Override
                public void onConfigured(CameraCaptureSession cameraCaptureSession) {
                    mPreviewSession = cameraCaptureSession;
                    updatePreview();//進入這個函數
                }
 
//……….
            }, mBackgroundHandler);}
            
 
//此爲mediaRecoder的設置,具體見MediaRecorder
    private void setUpMediaRecorder() throws IOException {
        final Activity activity = getActivity();
        if (null == activity) {
            return;
        }
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mMediaRecorder.setOutputFile(getVideoFile(activity).getAbsolutePath());
        mMediaRecorder.setVideoEncodingBitRate(10000000);
        mMediaRecorder.setVideoFrameRate(30);
        mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int orientation = ORIENTATIONS.get(rotation);
        mMediaRecorder.setOrientationHint(orientation);
        mMediaRecorder.prepare();
    }
 
 
 
    private void updatePreview() {
        if (null == mCameraDevice) {
            return;
        }
        try {
            setUpCaptureRequestBuilder(mPreviewBuilder);
            mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, mBackgroundHandler);//session發送請求,持續捕獲幀
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
}
 
//進行上面設置以後,點擊button,執行startRecordingVideo()函數,mMediaRecorder.start();開始錄製視頻
 
    private void startRecordingVideo() {
        try {
            // UI
            mButtonVideo.setText(R.string.stop);
            mIsRecordingVideo = true;
 
            // Start recording
            mMediaRecorder.start();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }
//再次點擊button,中止錄製。源代碼會出現一些問題,應該先關閉capture以後再中止錄製,具體問題在stackoverflow裏面有寫。
 
  private void stopRecordingVideo() {
        // UI
        mIsRecordingVideo = false;
        mButtonVideo.setText(R.string.record);
    //modifying!!!!~~~~~android官方的demo裏面在中止拍攝的時候會卡,因此改了一點
    try {
        // Abort all pending captures.
        cameraCaptureSession.abortCaptures();
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }    
 
        // Stop recording
        mMediaRecorder.stop();
        mMediaRecorder.reset();
        Activity activity = getActivity();
        if (null != activity) {
            Toast.makeText(activity, "Video saved: " + getVideoFile(activity),
                    Toast.LENGTH_SHORT).show();
        }
        startPreview();//最後執行這個函數,從新開始預覽,準備錄製視頻
}
    }

在Camera2Video中:只建立了一個session,一個request有兩個target surface-MediaRecorder和Preview。SetRepeatingRequest()不停的捕獲畫面一方面顯示在preview上,另外一方面造成視頻流。當MediaRecorder.start()時,開始錄製,以前SetRepeatingRequest的幀(frame)拋棄掉,從start開始幀輸出到指定的文件中。  

相關文章
相關標籤/搜索