Camera2相機預覽流程


Android5之后,Camera的api有了很大變化,稱為Camera2

 

Camera2的功能更多更強大,同時也更加復雜

 

在camera2中管道pipeline(也可以叫做流),照片,視頻都是從流中獲取的。

 

分析這張圖,可以簡單得到預覽其實有三步

 

  1. 打開相機,創建與相機的會話(即建立管道)

 

  1. 發起CaptureRequest,獲取流內容

 

  1. 建立Surface,將內容輸出到Surface(即輸出到手機屏幕)

 

當然,這只是將相機預覽流程進行了抽象,實際相機預覽流程沒有那么簡單,下面是一個簡單的demo,實現相機預覽

 

 

 

 

 

入口MainActivity

 

package com.example.camera2test;

 

import androidx.appcompat.app.AppCompatActivity;

 

import android.os.Bundle;

import android.view.View;

import android.widget.FrameLayout;

 

public class MainActivity extends AppCompatActivity {

 

    public static final String TAG = "MainActivity";

    private Camera2Preview mCameraPreview;

    private FrameLayout parentView;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

 

        mCameraPreview = new Camera2Preview(this);

 

        parentView = (FrameLayout) findViewById(R.id.camera_preview);

 

        parentView.addView((View) mCameraPreview);

       

        findViewById(R.id.button_switch).setOnClickListener(new View.OnClickListener() {

            @Override

            public void onClick(View v) {

                mCameraPreview.switchCameraFacing();

            }

        });

    }

 

    @Override

    protected void onResume() {

        super.onResume();

        if (mCameraPreview != null)

            mCameraPreview.onResume();

    }

 

    @Override

    protected void onPause() {

        super.onPause();

        if (mCameraPreview != null)

            mCameraPreview.onPause();

    }

}

 

 

 

 

接口CameraView

/**

 * 功能描述

 *

 * @author y50003490

 * @since 2019-08-20

 */

public interface CameraView {

 

    /**

     * 與生命周期onResume調用

     */

    void onResume();

 

    /**

     * 與生命周期onPause調用

     */

    void onPause();

 

    /**

     * 切換相機攝像頭

     */

    void switchCameraFacing();

}

 

相機預覽Camera2Preview

 

package com.example.camera2test;

 

import androidx.annotation.NonNull;

import androidx.appcompat.app.AppCompatActivity;

import androidx.core.app.ActivityCompat;

 

import android.Manifest;

import android.content.Context;

import android.content.pm.PackageManager;

import android.graphics.Matrix;

import android.graphics.RectF;

import android.graphics.SurfaceTexture;

import android.hardware.camera2.CameraAccessException;

import android.hardware.camera2.CameraCaptureSession;

import android.hardware.camera2.CameraCharacteristics;

import android.hardware.camera2.CameraDevice;

import android.hardware.camera2.CameraManager;

import android.hardware.camera2.CaptureRequest;

import android.os.Bundle;

import android.os.Handler;

import android.os.HandlerThread;

import android.util.AttributeSet;

import android.util.Log;

import android.util.Size;

import android.view.Surface;

import android.view.TextureView;

import android.view.View;

import android.view.WindowManager;

import android.widget.FrameLayout;

 

import java.util.Arrays;

 

public class Camera2Preview extends TextureView implements CameraView {

 

    private static final String TAG = "Camera2Preview";

    private Context mContext;

 

    public Camera2Preview(Context context) {

        this(context, null);

    }

 

    public Camera2Preview(Context context, AttributeSet attrs) {

        this(context, attrs, 0);

    }

 

    public Camera2Preview(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        mContext = context;

    }

 

    /**

     * ID of the current {@link CameraDevice}.

     */

    private String mCameraId;

 

    /**

     * 相機預覽要用到的{@link CameraCaptureSession } .

     */

    private CameraCaptureSession mCaptureSession;

 

    /**

     * A reference to the opened {@link CameraDevice}.

     */

    private CameraDevice mCameraDevice;

 

    /**

     * 相機預覽的 {@link android.util.Size}

     */

    private Size mPreviewSize;

    /**

     * 用於運行不應該阻止UI的任務的附加線程。

     */

    private HandlerThread mBackgroundThread;

 

    /**

     * 在后台運行任務的Handler。

     */

    private Handler mBackgroundHandler;

 

    /**

     * {@link CaptureRequest.Builder} for the camera1 preview

     */

    private CaptureRequest.Builder mPreviewRequestBuilder;

 

    /**

     * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}

     */

    private CaptureRequest mPreviewRequest;

 

    /**

     * 當前相機(前置、后置)

     */

    private int mCurrentCameraFacing = CameraCharacteristics.LENS_FACING_BACK;

 

 

    /**

     * 同生命周期 onResume

     */

    @Override

    public void onResume() {

 

        startBackgroundThread();

        // 當關閉並打開屏幕時,SurfaceTexture已經可用,並且不會調用“onSurfaceTextureAvailable”。

        // 在這種情況下,我們可以打開相機並從這里開始預覽(否則,我們將等待,直到Surface在SurfaceTextureListener中准備好)。

        if (isAvailable()) {

            Log.d(TAG, "onResume: openCamera");

            openCamera();

        } else {

            setSurfaceTextureListener(mSurfaceTextureListener);

            Log.d(TAG, "onResume: is not Available");

        }

 

    }

 

    /**

     * 同生命周期 onPause

     */

    @Override

    public void onPause() {

        closeCamera();

        stopBackgroundThread();

        Log.d(TAG, "onPause: close Camera");

    }

 

    /**

     * 打開指定的相機(mCameraId)

     */

    private void openCamera() {

        Log.d(TAG, "openCamera: ");

        if (mCameraId == null) {

            mCameraId = String.valueOf(0);

        }

 

        CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);

        try {

            //檢查相機權限

 

            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {

                Log.d(TAG, "openCamera: no Camera Permission");

                return;

            }

 

            //打開對應mCameraId的攝像頭

            Log.d(TAG, "openCamera: 2");

            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);

        } catch (CameraAccessException e) {

            Log.d(TAG, "openCamera: ERROR");

            e.printStackTrace();

        } finally {

            Log.d(TAG, "finally: openCamera");

        }

    }

 

    /**

     * 關閉相機

     */

    private void closeCamera() {

        if (null != mCaptureSession) {

            mCaptureSession.close();

            mCaptureSession = null;

        }

        if (null != mCameraDevice) {

            mCameraDevice.close();

            mCameraDevice = null;

        }

    }

 

    /**

     * {@link CameraDevice.StateCallback} is called when {@link CameraDevice} changes its state.

     */

    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {

 

        @Override

        public void onOpened(@NonNull CameraDevice cameraDevice) {

            Log.d(TAG, "onOpened: ");

            // 當相機打開時調用此方法。我們在這里開始相機預覽。

            mCameraDevice = cameraDevice;

            //創建相機預覽會話

            createCameraPreviewSession();

        }

 

        @Override

        public void onDisconnected(@NonNull CameraDevice cameraDevice) {

            Log.d(TAG, "onDisconnected: ");

            cameraDevice.close();

            mCameraDevice = null;

        }

 

        @Override

        public void onError(@NonNull CameraDevice cameraDevice, int error) {

            Log.d(TAG, "onError: ");

            cameraDevice.close();

            mCameraDevice = null;

            Log.e(TAG, "CameraDevice.StateCallback onError errorCode= " + error);

        }

 

    };

 

    /**

     * 創建一個新的 {@link CameraCaptureSession} 用於相機預覽.

     */

    private void createCameraPreviewSession() {

        try {

            //獲得SurfaceTexture

            SurfaceTexture texture = getSurfaceTexture();

            if(texture == null){

                Log.d(TAG, "createCameraPreviewSession: texture is null");

            }

 

 

            // 我們需要開始預覽輸出Surface

            Surface surface = new Surface(texture);

 

            // 我們建立了一個具有輸出Surface的捕獲器。

            mPreviewRequestBuilder

                    = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            mPreviewRequestBuilder.addTarget(surface);

 

            // 這里,我們創建了一個用於相機預覽的CameraCaptureSession

            // test delete m mImageReader.getSurface()

 

            mCameraDevice.createCaptureSession(Arrays.asList(surface),

                    new CameraCaptureSession.StateCallback() {

 

                        @Override

                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {

                            Log.d(TAG, "onConfigured: ");

                            // 相機已經關閉

                            if (null == mCameraDevice) {

                                return;

                            }

 

                            // 當session准備好后,我們開始顯示預覽

                            mCaptureSession = cameraCaptureSession;

                            try {

                                // 相機預覽時應連續自動對焦

 

                                //mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

                                // 設置閃光燈在必要時自動打開

                                //setAutoFlash(mPreviewRequestBuilder);

 

                                // 最終,顯示相機預覽

                                mPreviewRequest = mPreviewRequestBuilder.build();

                                //test delte mCaptureCallback

                                mCaptureSession.setRepeatingRequest(mPreviewRequest,

                                        null, mBackgroundHandler);

                            } catch (CameraAccessException e) {

                                e.printStackTrace();

                            }

                        }

 

                        @Override

                        public void onConfigureFailed(

                                @NonNull CameraCaptureSession cameraCaptureSession) {

                            Log.e(TAG, "CameraCaptureSession.StateCallback onConfigureFailed");

                        }

                    }, null

            );

 

        } catch (CameraAccessException e) {

            e.printStackTrace();

        }

 

    }

 

    /**

     * 啟動后台線程和Handler.

     */

    private void startBackgroundThread() {

        mBackgroundThread = new HandlerThread("CameraBackground");

        mBackgroundThread.start();

        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());

    }

 

    /**

     * 停止后台線程和Handler.

     */

    private void stopBackgroundThread() {

        mBackgroundThread.quitSafely();

        try {

            mBackgroundThread.join();

            mBackgroundThread = null;

            mBackgroundHandler = null;

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

 

    /**

     * {@link TextureView.SurfaceTextureListener} handles several lifecycle events on a

     * {@link TextureView}.

     */

    private final TextureView.SurfaceTextureListener mSurfaceTextureListener

            = new TextureView.SurfaceTextureListener() {

 

        @Override

        public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {

            openCamera();

        }

 

        @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) {

        }

 

    };

 

    /**

     * 配置必要的 {@link android.graphics.Matrix} 轉換為 `mTextureView`.

     * <p>

     * 該方法應該在setUpCameraOutputs中確定相機預覽大小以及“mTextureView”的大小固定之后調用。

     *

     * @param viewWidth  The width of `mTextureView`

     * @param viewHeight The height of `mTextureView`

     */

    private void configureTransform(int viewWidth, int viewHeight) {

        if (null == mPreviewSize || null == mContext) {

            return;

        }

        WindowManager mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

        int rotation = mWindowManager.getDefaultDisplay().getRotation();

        Matrix matrix = new Matrix();

        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);

        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());

        float centerX = viewRect.centerX();

        float centerY = viewRect.centerY();

        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {

            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());

            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);

            float scale = Math.max(

                    (float) viewHeight / mPreviewSize.getHeight(),

                    (float) viewWidth / mPreviewSize.getWidth());

            matrix.postScale(scale, scale, centerX, centerY);

            matrix.postRotate(90 * (rotation - 2), centerX, centerY);

        } else if (Surface.ROTATION_180 == rotation) {

            matrix.postRotate(180, centerX, centerY);

        }

        setTransform(matrix);

    }

 

    @Override

    public void switchCameraFacing() {

        mCurrentCameraFacing = mCurrentCameraFacing == CameraCharacteristics.LENS_FACING_BACK ?

                CameraCharacteristics.LENS_FACING_FRONT : CameraCharacteristics.LENS_FACING_BACK;

 

        mCameraId = String.valueOf(mCurrentCameraFacing);

        if (mCameraId == null) {

            mCameraId = String.valueOf(1);

        }

        closeCamera();

        openCamera();

    }

}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM