Android Camera2

Android Camera2 工具类封装:

疑问:

        Camera2 网上的资料感觉不是很多,很奇怪,难道Camera2没多少人用?还是说我low。。。。

求解:

         发现启用硬件加速设置后,启动Camera2很卡,有没有好的解决办法,求解答:

个人理解:

      Camera2启动规则:

    (1)、获取CameraManager,准备三个东西:

         CameraDevice.StateCallback理解为:负责监听设备打开或关闭的状态的一个监听器;

         CameraCaptureSession.StateCallback理解为:负责监听捕获设备启动会话请求状态的

                                                    一个监听器;

扫描二维码关注公众号,回复: 732120 查看本文章

         CameraCaptureSession.CaptureCallback理解为:负责监听设备捕获每一帧完成的回调;


    (2)、根据cameraId打开相机:openCamera(...),传入CameraDevice.StateCallback;自己理解为此时该方法会调用设备本身的镜头,同时通知前台设备已经准备就绪。

  (3)、通过返回的CameraDevice,创建一个新的CaptureSession,调用方法:createCaptureSession(....),这里传入CameraCaptureSession.StateCallback,该监听器负责的作用就是通知前台CaptureSession事都已经准备完成。

    (4)、当CaptureSession创建成功,则会回调onConfigured(...)方法,通知前台,Seesion以创建成功,可以做相关的设置了。此时会返回一个已经创建好的Session,到这一步,就可以使用该Session进行拍照或预览了。

    拍照:capture(...);//捕获一帧

    预览:setRepeatingRequest(...);//重复的进行捕获请求,类似与帧动画

    注:要想获取拍照的图片信息,需加入ImageReader进行渲染

发句牢骚:

        很久没玩Android了,瞎摸一下午,各种资料到处查,自己封装了一个类;暂时还没做相应的优化。

代码分析:

整体看代码可能会比较乱,所以截取关键部分进行一一分析,注释很清楚,应该看得懂吧——————

1、初始化camera信息:

private void initCameraInfo() {
        try {
            //获取上下文
            Context context = this.getContext();
            //获取CameraManager
            mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
            //获取CameraDevice.StateCallback
            mProxyCamerDeviceStateCallback = new ProxyCamerDeviceStateCallback();
            //获取CameraCaptureSession.CaptureCallback
            mProxyCameraCaptureSessionCaptureCallback = new ProxyCameraCaptureSessionCaptureCallback();

            //获取camera 所有的id,即对应的前后镜头
            //String[] cameraIdList = cameraManager.getCameraIdList();
            //获取后置摄像头(也可以获取前置摄像头LENS_FACING_BACK)的相关配置信息,只可读取,不可设置
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(
                    String.valueOf(CameraCharacteristics.LENS_FACING_FRONT));
            //判断该设备是否支持硬件加速,这里没有做判断
            Integer integer = cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);

            //获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
            StreamConfigurationMap configurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            assert configurationMap != null;

            //根据TextureView的尺寸设置预览最佳尺寸
            mCameraPreviewOptimalSize = getCameraPreviewOptimalSize(
                    configurationMap.getOutputSizes(SurfaceTexture.class), getWidth(), getHeight());

            //获取相机支持的最大拍照尺寸
            Size captureSize = Collections.max(Arrays.asList(configurationMap.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth());
                }
            });

            //2代表ImageReader中最多可以获取两帧图像流
            mImageReader = ImageReader.newInstance(
                    captureSize.getWidth(), captureSize.getHeight(), ImageFormat.JPEG, 2);

            //注册图片完成时的监听回调
            mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    mTakePicturePreviewCallback.takePictureComplete(CameraProxyView.this.getBitmap());
                }
            }, mHandler);

            //默认开启后置摄像头
            openProxyCamera(LENS_FACING_FRONT);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
2、提出公共的方法进行设备的打开,方便多个摄像头的切换(便于外部的调用):
    /**
     * 打开摄像头
     * @param cameraId
     */
    private void openProxyCamera(int cameraId) {
        //打开后置摄像头
        try {
            //检查是否拥有Camera权限
            if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA)
                    != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            //打开指定的摄像头
            mCameraManager.openCamera(String.valueOf(cameraId), mProxyCamerDeviceStateCallback, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
3、内部类:实现CameraDevice.StateCallback抽象类
private class ProxyCamerDeviceStateCallback extends CameraDevice.StateCallback {
        private CameraDevice mCameraDevice;

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            try {
                mCameraDevice = cameraDevice;
                //获取捕获请求的构造器 CaptureRequest.Builder
                CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                //获取SurfaceTexture
                SurfaceTexture surfaceTexture = getSurfaceTexture();
                //设置预览大小
                surfaceTexture.setDefaultBufferSize(mCameraPreviewOptimalSize.getWidth(),mCameraPreviewOptimalSize.getHeight());
                //创建Surface
                Surface surface = new Surface(surfaceTexture);
                //向构造器中加入surface
                captureRequestBuilder.addTarget(surface);
                //设置自动对焦参数
                captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                //获取CameraCaptureSession.StateCallback
                mProxyCameraCaptureSessionStateCallback = new ProxyCameraCaptureSessionStateCallback(captureRequestBuilder);
                //创建新的捕获会话,并传入会话监听回调
                cameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mProxyCameraCaptureSessionStateCallback, mHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            cameraDevice.close();
            mCameraDevice = null;
        }

        /**
         * 调用该方法之后,所有调用处于激活状态的session和CameraDevice其方法的操作都将抛出一个IllegalStateException异常。
         * 当相机完全关闭后,CameraDevice.StateCallback.onClosed(CameraDevice)方法会被调用,
         * 此时相机就绪以等待下一次被打开。
         */
        public void onClosed() {
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
        }

        public CameraDevice getCameraDevice() {
            return mCameraDevice;
        }
    }

4、内部类:

private class ProxyCameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback implements
            TakePicturePreviewHelper {

        private CaptureRequest.Builder mPreviewBuilder;
        private ProxyCameraCaptureSessionCaptureCallback mProxySessionCaptureCallback;
        private CameraCaptureSession mCameraCaptureSession;

        public ProxyCameraCaptureSessionStateCallback(CaptureRequest.Builder previewBuilder) {
            mPreviewBuilder = previewBuilder;
            // 自动对焦
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            //创建CameraCaptureSession.CaptureCallback对象
            mProxySessionCaptureCallback = new ProxyCameraCaptureSessionCaptureCallback();
        }

        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            mCameraCaptureSession = cameraCaptureSession;
            this.takePreview();
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }

        /**
         * 拍照
         */
        @Override
        public void takePicture() {
            try {
                //设置捕获会话请求,即开启拍照模式
                mCameraCaptureSession.capture(mPreviewBuilder.build(), mProxySessionCaptureCallback, mHandler);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 预览
         */
        @Override
        public void takePreview() {
            try {
                //设置重复捕获会话请求,即开启预览模式(默认开启)
                mCameraCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

其中接口TakePicturePreviewHelper:

private interface TakePicturePreviewHelper {
        //拍照
        public void takePicture();

        //预览
        public void takePreview();
    }
5、内部类:CameraCaptureSession.CaptureCallback
private class ProxyCameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback {

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession cameraCaptureSession,
                                       @NonNull CaptureRequest captureRequest,
                                       @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(cameraCaptureSession, captureRequest, result);
            try {
                //获取静态的捕获请求构造
                CaptureRequest.Builder staticCaptureRequest = mProxyCamerDeviceStateCallback.getCameraDevice()
                        .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                //设置可生成图像的surface
                staticCaptureRequest.addTarget(mImageReader.getSurface());
                //staticCaptureRequest.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));

                //停止接收不会会话
                cameraCaptureSession.stopRepeating();
                cameraCaptureSession.capture(staticCaptureRequest.build(), null, mHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }

以上是关键的代码部分,注释的地方很多了,应该不难看懂。。。。

这里贴一下完整的代码:

/**
 * @describe: 相机代理类
 * @author: GR
 * @data: 2018-05-10 15:57
 */

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CameraProxyView<T> extends TextureView implements TextureView.SurfaceTextureListener {
    //loger util
    private Logger logger = LoggerFactory.create(CameraProxyView.class);

    //setup info
    private final int LENS_FACING_FRONT = 0;
    private final int LENS_FACING_BACK = 1;

    //register callback
    private ProxyCameraCaptureSessionCaptureCallback mProxyCameraCaptureSessionCaptureCallback;
    private ProxyCameraCaptureSessionStateCallback mProxyCameraCaptureSessionStateCallback;
    private ProxyCamerDeviceStateCallback mProxyCamerDeviceStateCallback;
    private TakePicturePreviewCallback mTakePicturePreviewCallback;

    private Handler mHandler;
    private CameraManager mCameraManager;
    private ImageReader mImageReader;
    private Size mCameraPreviewOptimalSize;

//    private static final SparseIntArray ORIENTATION = new SparseIntArray();
//
//    static {
//        ORIENTATION.append(Surface.ROTATION_0, 90);
//        ORIENTATION.append(Surface.ROTATION_90, 0);
//        ORIENTATION.append(Surface.ROTATION_180, 270);
//        ORIENTATION.append(Surface.ROTATION_270, 180);
//    }

    public CameraProxyView(Context context) {
        this(context, null);
    }

    public CameraProxyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.setSurfaceTextureListener(this);
        mHandler = new Handler(Looper.myLooper());
    }

    private void initCameraInfo() {
        try {
            //获取上下文
            Context context = this.getContext();
            //获取CameraManager
            mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
            //获取CameraDevice.StateCallback
            mProxyCamerDeviceStateCallback = new ProxyCamerDeviceStateCallback();
            //获取CameraCaptureSession.CaptureCallback
            mProxyCameraCaptureSessionCaptureCallback = new ProxyCameraCaptureSessionCaptureCallback();

            //获取camera 所有的id,即对应的前后镜头
            //String[] cameraIdList = cameraManager.getCameraIdList();
            //获取后置摄像头(也可以获取前置摄像头LENS_FACING_BACK)的相关配置信息,只可读取,不可设置
            CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(
                    String.valueOf(CameraCharacteristics.LENS_FACING_FRONT));
            //判断该设备是否支持硬件加速
            Integer integer = cameraCharacteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);

            //获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸
            StreamConfigurationMap configurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
            assert configurationMap != null;

            //根据TextureView的尺寸设置预览最佳尺寸
            mCameraPreviewOptimalSize = getCameraPreviewOptimalSize(
                    configurationMap.getOutputSizes(SurfaceTexture.class), getWidth(), getHeight());

            //获取相机支持的最大拍照尺寸
            Size captureSize = Collections.max(Arrays.asList(configurationMap.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth());
                }
            });

            //2代表ImageReader中最多可以获取两帧图像流
            mImageReader = ImageReader.newInstance(
                    captureSize.getWidth(), captureSize.getHeight(), ImageFormat.JPEG, 2);

            //注册图片完成时的监听回调
            mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    mTakePicturePreviewCallback.takePictureComplete(CameraProxyView.this.getBitmap());
                }
            }, mHandler);

            //默认开启后置摄像头
            openProxyCamera(LENS_FACING_FRONT);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 选择sizeMap中大于并且最接近width和height的size
     *
     * @param sizeMap
     * @param width
     * @param height
     * @return
     */
    private Size getCameraPreviewOptimalSize(Size[] sizeMap, int width, int height) {
        List<Size> sizeList = new ArrayList<>();
        for (Size option : sizeMap) {
            if (width > height) {
                if (option.getWidth() > width && option.getHeight() > height) {
                    sizeList.add(option);
                }
            } else {
                if (option.getWidth() > height && option.getHeight() > width) {
                    sizeList.add(option);
                }
            }
        }
        if (sizeList.size() > 0) {
            return Collections.min(sizeList, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        return sizeMap[0];
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        //初始化camera相关信息
        initCameraInfo();
    }

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

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

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    public void onRelease() {
        if (null != mImageReader) mImageReader = null;
        mProxyCamerDeviceStateCallback.onClosed();
    }

    private class ProxyCamerDeviceStateCallback extends CameraDevice.StateCallback {
        private CameraDevice mCameraDevice;

        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            try {
                mCameraDevice = cameraDevice;
                //获取捕获请求的构造器 CaptureRequest.Builder
                CaptureRequest.Builder captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                //获取SurfaceTexture
                SurfaceTexture surfaceTexture = getSurfaceTexture();
                //设置预览大小
                surfaceTexture.setDefaultBufferSize(mCameraPreviewOptimalSize.getWidth(),mCameraPreviewOptimalSize.getHeight());
                //创建Surface
                Surface surface = new Surface(surfaceTexture);
                //向构造器中加入surface
                captureRequestBuilder.addTarget(surface);
                //设置自动对焦参数
                captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                //获取CameraCaptureSession.StateCallback
                mProxyCameraCaptureSessionStateCallback = new ProxyCameraCaptureSessionStateCallback(captureRequestBuilder);
                //创建新的捕获会话,并传入会话监听回调
                cameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mProxyCameraCaptureSessionStateCallback, mHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            cameraDevice.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            cameraDevice.close();
            mCameraDevice = null;
        }

        /**
         * 调用该方法之后,所有调用处于激活状态的session和CameraDevice其方法的操作都将抛出一个IllegalStateException异常。
         * 当相机完全关闭后,CameraDevice.StateCallback.onClosed(CameraDevice)方法会被调用,
         * 此时相机就绪以等待下一次被打开。
         */
        public void onClosed() {
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
        }

        public CameraDevice getCameraDevice() {
            return mCameraDevice;
        }
    }

    private class ProxyCameraCaptureSessionStateCallback extends CameraCaptureSession.StateCallback implements
            TakePicturePreviewHelper {

        private CaptureRequest.Builder mPreviewBuilder;
        private ProxyCameraCaptureSessionCaptureCallback mProxySessionCaptureCallback;
        private CameraCaptureSession mCameraCaptureSession;

        public ProxyCameraCaptureSessionStateCallback(CaptureRequest.Builder previewBuilder) {
            mPreviewBuilder = previewBuilder;
            // 自动对焦
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            //创建CameraCaptureSession.CaptureCallback对象
            mProxySessionCaptureCallback = new ProxyCameraCaptureSessionCaptureCallback();
        }

        @Override
        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
            mCameraCaptureSession = cameraCaptureSession;
            this.takePreview();
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }

        /**
         * 拍照
         */
        @Override
        public void takePicture() {
            try {
                //设置捕获会话请求,即开启拍照模式
                mCameraCaptureSession.capture(mPreviewBuilder.build(), mProxySessionCaptureCallback, mHandler);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        /**
         * 预览
         */
        @Override
        public void takePreview() {
            try {
                //设置重复捕获会话请求,即开启预览模式(默认开启)
                mCameraCaptureSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private class ProxyCameraCaptureSessionCaptureCallback extends CameraCaptureSession.CaptureCallback {

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession cameraCaptureSession,
                                       @NonNull CaptureRequest captureRequest,
                                       @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(cameraCaptureSession, captureRequest, result);
            try {
                //获取静态的捕获请求构造
                CaptureRequest.Builder staticCaptureRequest = mProxyCamerDeviceStateCallback.getCameraDevice()
                        .createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                //设置可生成图像的surface
                staticCaptureRequest.addTarget(mImageReader.getSurface());
                //staticCaptureRequest.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation));

                //停止接收不会会话
                cameraCaptureSession.stopRepeating();
                cameraCaptureSession.capture(staticCaptureRequest.build(), null, mHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private interface TakePicturePreviewHelper {
        //拍照
        public void takePicture();

        //预览
        public void takePreview();
    }

    public interface TakePicturePreviewCallback {
        //完成
        public void takePictureComplete(Bitmap bitmap);
    }


    /**
     * ********************************************
     * Comon methed
     * ********************************************
     */

    /**
     * 拍照
     */
    public void takePicture() {
        if (null != mProxyCameraCaptureSessionStateCallback)
            mProxyCameraCaptureSessionStateCallback.takePicture();
    }

    /**
     * 预览
     */
    public void takePreview() {
        if (null != mProxyCameraCaptureSessionStateCallback)
            mProxyCameraCaptureSessionStateCallback.takePreview();
    }

    /**
     * 设置拍照回调接口
     *
     * @param takePicturePreviewCallback
     */
    public void setTakePicturePreviewCallback(TakePicturePreviewCallback takePicturePreviewCallback) {
        mTakePicturePreviewCallback = takePicturePreviewCallback;
    }

    /**
     * 打开摄像头
     * @param cameraId
     */
    private void openProxyCamera(int cameraId) {
        //打开后置摄像头
        try {
            //检查是否拥有Camera权限
            if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA)
                    != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            //打开指定的摄像头
            mCameraManager.openCamera(String.valueOf(cameraId), mProxyCamerDeviceStateCallback, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
}

到这里差不多了,虽然还有点low,但是测试后还能用,有带优化。

问题:

测试后发现存在启动较慢的情况,如果可以解决,请告知。。。。




猜你喜欢

转载自blog.csdn.net/qq_36220729/article/details/80285231
今日推荐