Android APP Camera2应用(02)预览流程

49 篇文章 15 订阅

说明:camera子系统 系列文章针对Android10.0系统,主要针对 camera API2 + HAL3 框架进行解读。

1 预览流程简要解读

@1 由 CameraManager.openCamera 方法,打开摄像头。该方法传入三个参数:

  • String CameraId:传入要打开的摄像头的 Id 
  • CameraDevice.stateCallback:即 CameraDevice.stateCallback 的实例 
  • Handler:代表执行callback的handler,如果程序希望直接在当前线程中执行callback,则可以将handler参数设为null

关键代码如下所示:

//camera process,step1 打开camera
mCameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);

@2 实例化CameraDevice.statCallback,并重写 onOpened 方法,该方法在摄像 头完成打开动作后调用,以下动作均在 onOpened 方法中完成,onOpened方法,会返回当前使用的CameraDevice。关键代码如下所示:

//camera process,step2 mStateCallback 实例化
    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            //重写onOpened方法,最为关键
            mCameraOpenCloseLock.release();
            mCameraDevice = camera;
            startCaptureSession();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            //...
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            //...
        }
    };

@3 调用 CameraDevice.CreateCapture(int templateType),创建一个 CaptureRequest.Builder,templateType来区分是拍照还是预览,当预览时,我们传入CameraDevice.TEMPLATE_PREVIEW参数,该方法回返回一个对于返回的对象,我们声明一个 CaptureRequest.Builder类型的mCaptureRequestBuilder变量来接收。关键代码如下所示:

//camera process,step3 创建一个 CaptureRequest.Builder,templateType来区分是拍照还是预览
mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

@4 调用mCaptureRequestBuilder.addTarget()方法,将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中。关键代码如下所示:

//camera process,step4 将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中
mPreviewRequestBuilder.addTarget(mImageReader.getSurface());

@5 调用 CameraDevice.CreateCaptureSession()方法,该方法传入三个参数:

  • List<Surface>:可用于捕获目标图像数据的 Surface 集合,此处传入用于显示预览图像的 Surface 即可,即 Arrays.asList(surface) 
  • CameraCaptureSession.stateCallback:该回调的实例
  • Handler:一个句柄,代表执行 callback 的 handler,如果程序希望在当前线程执行callback,可以将 Handler 参数设为 null。
//camera process,step5 将显示预览用的surface的实例传入,即将显示预览用的surface的实例,作为一个显示层添加到该请求的目标列表中
surfaceList = Arrays.asList(surface, mImageReader.getSurface());

@6 调用 CameraDevice.CreateCaptureSession()方法。该方法传入三个参数:

  • List<Surface>:可用于捕获目标图像数据的Surface集合,此处传入用于显示预览图像的 Surface 即可,即 Arrays.asList(surface) CameraCaptureSession.
  • stateCallback:该回调的实例
  • Handler:代表执行callback的handler,如果程序希望在当前线程执行callback,可以将 Handler 参数设为 null

关键代码整合在8中,因为 6 7 8 代码紧密相关。

@7 实例化 CameraCaptureSession.stateCallback,并重写 onConfigured 方法,该方法在摄像头设备完成自身配置的时候调用,该方法会从底层传回一个 CameraCaptureSession,该 session 可以开始处理捕获请求。

关键代码整合在8中,因为 6 7 8 代码紧密相关。

@8 调用 CameraCaptureSession.setRepeatingRequest()方法创建预览,此方法 接收三个参数:

  • CaptureReqeust:无限重复的请求,此处传入上一步中中创建的captureRequest对象,即mCaptureRequest
  • CameraCaptureSession.CaptureCallback:callback的回调,不过预览中该回调中不用进行任何处理
  • Handler:代表执行callback的handler,如果程序希望直接在 当前线程中执行callback,则可以将handler参数设为 null

关键代码如下所示:

//camera process,step6 & 7
// 6 执行createCaptureSession方法
// 7 参数中实例化 CameraCaptureSession.stateCallback,并重写 onConfigured 方法
mCameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {
    @Override
    public void onConfigured(CameraCaptureSession session) {
        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
        mCaptureSession = session;
		//camera process,step8 用 CameraCaptureSession.setRepeatingRequest()方法创建预览
        mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);
    }

    @Override
    public void onConfigureFailed(CameraCaptureSession session) {
        //...
    }

    @Override
    public void onClosed(CameraCaptureSession session) {
        //...
    }
}, mCameraHandler);

2 camera预览流程代码完整解读

2.1 java源码部分(草稿)

Camera流程相关代码如下所示:

class CameraCoreManager {
    private static final String TAG = "CameraDemo";

    private Context mContext;
    private CameraManager mCameraManager;
    private String mCameraId;
    private HandlerThread mCameraThread;
    private Handler mCameraHandler;
    private Handler mImageHandler;
    private ImageReader mImageReader;
    private CameraDevice mCameraDevice;
    private CameraCharacteristics mCameraCharacteristics;

    //Max preview width&height that is guaranteed by Camera2 API
    private static final int MAX_PREVIEW_WIDTH = 1920;
    private static final int MAX_PREVIEW_HEIGHT = 1080;

    //A Semaphore to prevent the app from exiting before closing the camera.
    private Semaphore mCameraOpenCloseLock = new Semaphore(1);
    private Size mPreviewSize = new Size(1920, 1080);
    private CaptureRequest.Builder mPreviewRequestBuilder;
    private CameraCaptureSession mCaptureSession;
    private int mFacing = CameraCharacteristics.LENS_FACING_BACK;
    private Choreographer.FrameCallback mFrameCallback;
    private SurfaceTexture mSurfaceTexture;

    private ImageReader.OnImageAvailableListener mImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
            Image image = reader.acquireNextImage();
            Log.d(TAG, "##### onFrame: " + image.getPlanes());
            image.close();
        }
    };

    //camera process,step2 mStateCallback 实例化
    private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(CameraDevice camera) {
            //重写onOpened方法,最为关键
            mCameraOpenCloseLock.release();
            mCameraDevice = camera;
            startCaptureSession();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            mCameraOpenCloseLock.release();
            camera.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            Log.e("DEBUG", "onError: " + error);
            mCameraOpenCloseLock.release();
            camera.close();
            mCameraDevice = null;
            Log.e("DEBUG", "onError:  restart camera");
            stopPreview();
            startPreview();
        }
    };

    public CameraCoreManager(Context context) {
        mContext = context;
        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
    }

    public void startPreview() {
        if (!chooseCameraIdByFacing()) {
            Log.e(TAG, "Choose camera failed.");
            return;
        }

        mCameraThread = new HandlerThread("CameraThread");
        mCameraThread.start();
        mCameraHandler = new Handler(mCameraThread.getLooper());
        prepareImageReader();
        openCamera();
    }

    public void stopPreview() {
        closeCamera();
        if (mCameraThread != null) {
            mCameraThread.quitSafely();
            mCameraThread = null;
        }
        mCameraHandler = null;
    }

    private void prepareImageReader() {

        if (mImageReader != null) {
            mImageReader.close();
        }
        mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.YUV_420_888, 1);
        mImageReader.setOnImageAvailableListener(mImageAvailableListener, mCameraHandler);
    }

    private boolean chooseCameraIdByFacing() {
        try {
            String ids[] = mCameraManager.getCameraIdList();
            if (ids.length == 0) {
                Log.e(TAG, "No available camera.");
                return false;
            }

            for (String cameraId : mCameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);

                StreamConfigurationMap map = characteristics.get(
                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map == null) {
                    continue;
                }

                Integer level = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                    continue;
                }

                Integer internal = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (internal == null) {
                    continue;
                }
                if (internal == mFacing) {
                    mCameraId = cameraId;
                    mCameraCharacteristics = characteristics;
                    return true;
                }
            }
            mCameraId = ids[1];
            mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
            Integer level = mCameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
            if (level == null || level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
                return false;
            }

            Integer internal = mCameraCharacteristics.get(CameraCharacteristics.LENS_FACING);
            if (internal == null) {
                return false;
            }
            mFacing = CameraCharacteristics.LENS_FACING_BACK;
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        return true;
    }

    @SuppressLint("MissingPermission")
    public void openCamera() {
        if (TextUtils.isEmpty(mCameraId)) {
            Log.e(TAG, "Open camera failed. No camera available");
            return;
        }

        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                throw new RuntimeException("Time out waiting to lock camera opening.");
            }
            //camera process,step1 打开camera
            mCameraManager.openCamera(mCameraId, mStateCallback, mCameraHandler);
        } catch (InterruptedException | CameraAccessException e) {
            Log.e(TAG, e.getMessage());
        }
    }

    private void closeCamera() {
        try {
            mCameraOpenCloseLock.acquire();
            if (mCaptureSession != null) {
                mCaptureSession.close();
                mCaptureSession = null;
            }
            if (mCameraDevice != null) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (mImageReader != null) {
                mImageReader.close();
                mImageReader = null;
            }
        } catch (InterruptedException e) {
            throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
        } finally {
            mCameraOpenCloseLock.release();
        }
    }

    private void startCaptureSession() {
        if (mCameraDevice == null) {
            return;
        }

        if ((mImageReader != null || mSurfaceTexture != null)) {
            try {
                //camera process,step3 创建一个 CaptureRequest.Builder,templateType来区分是拍照还是预览
                mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                //camera process,step4 将显示预览用的surface的实例传入,即将显示预览用的 surface 的实例,作为一个显示层添加到该 请求的目标列表中
                mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
                List<Surface> surfaceList = Arrays.asList(mImageReader.getSurface());
                if (mSurfaceTexture != null) {
                    mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
                    Surface surface = new Surface(mSurfaceTexture);
                    mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
                    mPreviewRequestBuilder.addTarget(surface);
                    //camera process,step5 将显示预览用的surface的实例传入,即将显示预览用的surface的实例,作为一个显示层添加到该请求的目标列表中
                    surfaceList = Arrays.asList(surface, mImageReader.getSurface());
                }

                Range<Integer>[] fpsRanges = mCameraCharacteristics.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
                Log.d("DEBUG", "##### fpsRange: " + Arrays.toString(fpsRanges));
                //camera process,step6 & 7
                // 6 执行createCaptureSession方法
                // 7 参数中实例化 CameraCaptureSession.stateCallback,并重写 onConfigured 方法
                mCameraDevice.createCaptureSession(surfaceList, new CameraCaptureSession.StateCallback() {
                    @Override
                    public void onConfigured(CameraCaptureSession session) {

                        if (mCameraDevice == null)
                            return;
                        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);

                        mCaptureSession = session;
                        try {
                            if (mCaptureSession != null)
                                //camera process,step8 用 CameraCaptureSession.setRepeatingRequest()方法创建预览
                                mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), null, null);
                        } catch (CameraAccessException | IllegalArgumentException | IllegalStateException | NullPointerException e) {
                            e.printStackTrace();
                        }
                    }

                    @Override
                    public void onConfigureFailed(CameraCaptureSession session) {
                        Log.e(TAG, "Failed to configure capture session");
                    }

                    @Override
                    public void onClosed(CameraCaptureSession session) {
                        if (mCaptureSession != null && mCaptureSession.equals(session)) {
                            mCaptureSession = null;
                        }
                    }
                }, mCameraHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage());
            } catch (IllegalStateException e) {
                stopPreview();
                startPreview();
            } catch (UnsupportedOperationException e) {
                e.printStackTrace();
                Log.e(TAG, e.getMessage());
            }
        }

    }

    public interface FrameCallback {
        void onFrame(Image data);
    }

    public Size getPreviewSize() {
        return mPreviewSize;
    }

    public void setPreviewSize(Size previewSize) {
        mPreviewSize = previewSize;
    }

    public FrameCallback getFrameCallback() {
        return (FrameCallback) mFrameCallback;
    }

    public void setFrameCallback(FrameCallback frameCallback) {
        mFrameCallback = (Choreographer.FrameCallback) frameCallback;
    }

    public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
        mSurfaceTexture = surfaceTexture;
    }
}

Activity UI相关代码如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private ImageButton mTakePictureBtn;
    private CameraCoreManager manager;
    private TextureView mTextureView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera);
        manager = new CameraCoreManager(this);
        mTakePictureBtn = findViewById(R.id.camera_take_picture);
        mTakePictureBtn.setOnClickListener(this);
        mTextureView = findViewById(R.id.texture_view);
    }

    @Override
    public void onResume() {
        super.onResume();
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @Override
            public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                manager.setSurfaceTexture(surface);
                manager.startPreview();
            }

            @Override
            public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

            }

            @Override
            public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                return false;
            }

            @Override
            public void onSurfaceTextureUpdated(SurfaceTexture surface) {
            }
        });
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        manager.stopPreview();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.camera_take_picture:
                //take picture
                break;
        }
    }
}

2.2 layout文件

这里涉及布局文件主要为activity_camera.xml,xml文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/gray"
    android:gravity="center"
    android:orientation="horizontal"
    android:padding="20dp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toRightOf="parent">

    <ImageButton
        android:id="@+id/camera_take_picture"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:background="@drawable/ic_camera" />
</LinearLayout>

<TextureView
    android:id="@+id/texture_view"
    android:layout_width="320dp"
    android:layout_height="180dp"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

camera图标的矢量xml文件,这里特殊记录下,文件内容为:

<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
    android:fillColor="#FFFFFFFF"
    android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0"/>
<path
    android:fillColor="#FFFFFFFF"
    android:pathData="M9,2L7.17,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2L9,2zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z"/>
</vector>

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值