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();

    }

}

 

 

原文地址:https://www.cnblogs.com/shineyoung/p/11395180.html