android camera1 2使用

    之前对于 camera 的了解只限于 camera1 的简单使用上,知道打开相机后要对相机参数进行设置,如最佳预览大小,对焦等。最近的工作涉及到了 camera 的使用上。所以对 android camera1 和 2 都做了一定的了解。

    首先需要知道的是在 Android 手机上,相机对于手机竖屏来说都是倾斜 90° 的,所以需要对相机的预览都是要旋转 90°(对于竖屏来说,如果手机横屏那就需要对旋转的角度再判断)。这里就要开始吐槽了,也不知道这么多年为什么一直不改。

    对于 camera1 的使用,大概步骤就是:

    1. 打开一个摄像头
    1. 对打开的摄像头进行参数的设置,这里主要设置的参数有界面预览大小,拍照的照片大小,对焦模式,输出数据的模式,预览界面的旋转角度,预览画面设置
    1. 设置预览的回调
    1. 真正开始预览
/**
 *                 Point point = new Point(surfaceView.getWidth(), surfaceView.getHeight());
 *                 WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
 *                 int a = windowManager.getDefaultDisplay().getOrientation();
 *                 Log.d("TAG", "onCreate is " + a);
 *                 Log.d("TAG", "onCreate is " + windowManager.getDefaultDisplay().getRotation());
 *                 Camera1Helper camera1Helper = new Camera1Helper.Builder()
 *                         .previewOn(surfaceView)
 *                         .previewViewSize(point)
 *                         .rotation(a)
 *                         .cameraListener(new CameraListener() {
 *                             @Override
 *                             public void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror) {
 *                                 Log.d("TAG", "onCameraOpened");
 *                             }
 *
 *                             @Override
 *                             public void onPreview(byte[] data, Camera camera) {
 *                                 Log.d("TAG", "onPreview");
 *                             }
 *
 *                             @Override
 *                             public void onCameraClosed() {
 *                                 Log.d("TAG", "onCameraClosed");
 *                             }
 *
 *                             @Override
 *                             public void onCameraError(Exception e) {
 *                                 Log.d("TAG", "onCameraError");
 *                             }
 *
 *                             @Override
 *                             public void onCameraConfigurationChanged(int cameraID, int displayOrientation) {
 *                                 Log.d("TAG", "onCameraConfigurationChanged");
 *                             }
 *                         }).build();
 *                 camera1Helper.start();
 */

public class Camera1Helper implements Camera.PreviewCallback {
    private static final String TAG = "Camera1Helper";
    private Camera mCamera;
    private int mCameraId;
    private Point previewViewSize;
    private View previewDisplayView;
    private Camera.Size previewSize;
    private Point specificPreviewSize;
    private int displayOrientation = 0;
    private int rotation;
    private int additionalRotation;
    private boolean isMirror = false;

    private Integer specificCameraId = null;
    private CameraListener cameraListener;

    private boolean isDisplay;
    private final SurfaceTexture surfaceTexture = new SurfaceTexture(0);

    private Camera1Helper(Camera1Helper.Builder builder) {
        specificCameraId = builder.specificCameraId;
        cameraListener = builder.cameraListener;

        if (builder.previewDisplayView == null) {
            isDisplay = false;
        } else {
            isDisplay = true;
            previewDisplayView = builder.previewDisplayView;

            rotation = builder.rotation;
            additionalRotation = builder.additionalRotation;
            previewViewSize = builder.previewViewSize;
            specificPreviewSize = builder.previewSize;

            if (builder.previewDisplayView instanceof TextureView) {
                isMirror = builder.isMirror;
            } else if (isMirror) {
                throw new RuntimeException("mirror is effective only when the preview is on a textureView");
            }
        }
        init();
    }

    public void init() {
        if (isDisplay) {
            if (previewDisplayView instanceof TextureView) {
                ((TextureView) this.previewDisplayView).setSurfaceTextureListener(textureListener);
            } else if (previewDisplayView instanceof SurfaceView) {
                ((SurfaceView) previewDisplayView).getHolder().addCallback(surfaceCallback);
            }

            if (isMirror) {
                previewDisplayView.setScaleX(-1);
            }
        }
    }

    public void start() {
        synchronized (this) {
            //相机数量为2则打开1,1则打开0,相机ID 1为前置,0为后置
            mCameraId = Camera.getNumberOfCameras() - 1;
            //若指定了相机ID且该相机存在,则打开指定的相机
            if (specificCameraId != null && specificCameraId <= mCameraId) {
                mCameraId = specificCameraId;
            }

            //没有相机
            if (mCameraId == -1) {
                if (cameraListener != null) {
                    cameraListener.onCameraError(new Exception("camera not found"));
                }
                return;
            }
            if (mCamera == null) {
                mCamera = Camera.open(mCameraId);
            }
            if (isDisplay) {
                displayOrientation = getCameraOri(rotation);
                mCamera.setDisplayOrientation(displayOrientation);
            }
            try {
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setPreviewFormat(ImageFormat.NV21);

                if (isDisplay) {
                    //预览大小设置
                    previewSize = parameters.getPreviewSize();
                    List<Camera.Size> supportedPreviewSizes = parameters.getSupportedPreviewSizes();
                    if (supportedPreviewSizes != null && supportedPreviewSizes.size() > 0) {
                        previewSize = getBestSupportedSize(supportedPreviewSizes, previewViewSize);
                    }
                    parameters.setPreviewSize(previewSize.width, previewSize.height);
                }

                //对焦模式设置
                List<String> supportedFocusModes = parameters.getSupportedFocusModes();
                if (supportedFocusModes != null && supportedFocusModes.size() > 0) {
                    if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
                        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                    } else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
                        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
                    } else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
                        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
                    }
                }
                mCamera.setParameters(parameters);
                if (isDisplay) {
                    if (previewDisplayView instanceof TextureView) {
                        mCamera.setPreviewTexture(((TextureView) previewDisplayView).getSurfaceTexture());
                    } else {
                        mCamera.setPreviewDisplay(((SurfaceView) previewDisplayView).getHolder());
                    }
                }else {
                    mCamera.setPreviewTexture(surfaceTexture);
                }
                mCamera.setPreviewCallback(this);
                mCamera.startPreview();
                if (cameraListener != null) {
                    cameraListener.onCameraOpened(mCamera, mCameraId, displayOrientation, isMirror);
                }
            } catch (Exception e) {
                if (cameraListener != null) {
                    cameraListener.onCameraError(e);
                }
            }
        }
    }

    private int getCameraOri(int rotation) {
        int degrees = rotation * 90;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
            default:
                break;
        }
        additionalRotation /= 90;
        additionalRotation *= 90;
        degrees += additionalRotation;
        int result;
        Camera.CameraInfo info = new Camera.CameraInfo();
        Camera.getCameraInfo(mCameraId, info);
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;
        } else {
            result = (info.orientation - degrees + 360) % 360;
        }
        return result;
    }

    private void stop() {
        synchronized (this) {
            if (mCamera == null) {
                return;
            }
            try {
                mCamera.setPreviewCallback(null);
                mCamera.setPreviewDisplay(null);
                mCamera.stopPreview();
                mCamera.release();
                mCamera = null;
                if (cameraListener != null) {
                    cameraListener.onCameraClosed();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public boolean isStopped() {
        synchronized (this) {
            return mCamera == null;
        }
    }

    public void release() {
        stop();
        previewDisplayView = null;
        specificCameraId = null;
        cameraListener = null;
        previewViewSize = null;
        specificPreviewSize = null;
        previewSize = null;
    }

    private Camera.Size getBestSupportedSize(List<Camera.Size> sizes, Point previewViewSize) {
        if (sizes == null || sizes.size() == 0 || previewViewSize == null) {
            return mCamera.getParameters().getPreviewSize();
        }
        Camera.Size bestSize = sizes.get(0);
        float previewViewRatio = (float) previewViewSize.x / (float) previewViewSize.y;

        if (previewViewRatio > 1) {
            previewViewRatio = 1 / previewViewRatio;
        }
        boolean isNormalRotate = (additionalRotation % 180 == 0);

        for (Camera.Size s : sizes) {
            if (specificPreviewSize != null && specificPreviewSize.x == s.width && specificPreviewSize.y == s.height) {
                return s;
            }
            if (isNormalRotate) {
                if (Math.abs((s.height / (float) s.width) - previewViewRatio) < Math.abs(bestSize.height / (float) bestSize.width - previewViewRatio)) {
                    bestSize = s;
                }
            } else {
                if (Math.abs((s.width / (float) s.height) - previewViewRatio) < Math.abs(bestSize.width / (float) bestSize.height - previewViewRatio)) {
                    bestSize = s;
                }
            }
        }
        return bestSize;
    }

    public List<Camera.Size> getSupportedPreviewSizes() {
        if (mCamera == null) {
            return null;
        }
        return mCamera.getParameters().getSupportedPreviewSizes();
    }

    public List<Camera.Size> getSupportedPictureSizes() {
        if (mCamera == null) {
            return null;
        }
        return mCamera.getParameters().getSupportedPictureSizes();
    }


    @Override
    public void onPreviewFrame(byte[] nv21, Camera camera) {
        if (cameraListener != null) {
            cameraListener.onPreview(nv21, camera);
        }
    }

    private TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
            start();
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
            Log.i(TAG, "onSurfaceTextureSizeChanged: " + width + "  " + height);
        }

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

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

        }
    };
    private SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback() {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            start();
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
            stop();
        }
    };

    public void changeDisplayOrientation(int rotation) {
        if (mCamera != null) {
            this.rotation = rotation;
            displayOrientation = getCameraOri(rotation);
            mCamera.setDisplayOrientation(displayOrientation);
            if (cameraListener != null) {
                cameraListener.onCameraConfigurationChanged(mCameraId, displayOrientation);
            }
        }
    }


    public interface CameraListener {
        /**
         * 当打开时执行
         * @param camera 相机实例
         * @param cameraId 相机ID
         * @param displayOrientation 相机预览旋转角度
         * @param isMirror 是否镜像显示
         */
        void onCameraOpened(Camera camera, int cameraId, int displayOrientation, boolean isMirror);

        /**
         * 预览数据回调
         * @param data 预览数据
         * @param camera 相机实例
         */
        void onPreview(byte[] data, Camera camera);

        /**
         * 当相机关闭时执行
         */
        void onCameraClosed();

        /**
         * 当出现异常时执行
         * @param e 相机相关异常
         */
        void onCameraError(Exception e);

        /**
         * 属性变化时调用
         * @param cameraID  相机ID
         * @param displayOrientation    相机旋转方向
         */
        void onCameraConfigurationChanged(int cameraID, int displayOrientation);
    }

    public static final class Builder {

        /**
         * 预览显示的view,目前仅支持surfaceView和textureView
         */
        private View previewDisplayView;

        /**
         * 是否镜像显示,只支持textureView
         */
        private boolean isMirror;
        /**
         * 指定的相机ID
         */
        private Integer specificCameraId;
        /**
         * 事件回调
         */
        private CameraListener cameraListener;
        /**
         * 屏幕的长宽,在选择最佳相机比例时用到
         */
        private Point previewViewSize;
        /**
         * 传入getWindowManager().getDefaultDisplay().getRotation()的值即可
         */
        private int rotation;
        /**
         * 指定的预览宽高,若系统支持则会以这个预览宽高进行预览
         */
        private Point previewSize;

        /**
         * 额外的旋转角度(用于适配一些定制设备)
         */
        private int additionalRotation;

        public Builder() {
        }


        public Builder previewOn(View val) {
            if (val instanceof SurfaceView || val instanceof TextureView) {
                previewDisplayView = val;
                return this;
            } else {
                throw new RuntimeException("you must preview on a textureView or a surfaceView");
            }
        }


        public Builder isMirror(boolean val) {
            isMirror = val;
            return this;
        }

        public Builder previewSize(Point val) {
            previewSize = val;
            return this;
        }

        public Builder previewViewSize(Point val) {
            previewViewSize = val;
            return this;
        }

        public Builder rotation(int val) {
            rotation = val;
            return this;
        }

        public Builder additionalRotation(int val) {
            additionalRotation = val;
            return this;
        }

        public Builder specificCameraId(Integer val) {
            specificCameraId = val;
            return this;
        }

        public Builder cameraListener(CameraListener val) {
            cameraListener = val;
            return this;
        }

        public Camera1Helper build() {
            if (previewViewSize == null) {
                Log.e(TAG, "previewViewSize is null, now use default previewSize");
            }
            if (cameraListener == null) {
                Log.e(TAG, "cameraListener is null, callback will not be called");
            }
            if (previewDisplayView == null) {
                Log.e(TAG, "previewDisplayView is null ,you can preview on a textureView or a surfaceView");
            }
            return new Camera1Helper(this);
        }
    }

}

    因为我要使用到的是无界面拿到预览数据,和平常的还有点不一样

    相比较于 camera1 ,camera2 的使用和 1 没有半毛钱关系,而且巨多回调,一眼看去几乎都是回调

    1. 打开相机服务
    1. 设置摄像头的配置信息,预览大小设置等,设置相机设备的可用状态发生变化回调,设置相机设备的闪光灯的 Torch 模式可用状态发生变化回调等等
    1. 打开摄像头
    1. 摄像头打开回调成功后创建预览的请求,预览设置可以设置预览显示、图像格式、图像分辨率、传感器控制、闪光灯控制、3A(自动对焦-AF、自动曝光-AE和自动白平衡-AWB)控制等
    1. 预览的请求成功后设置重复请求就基本上差不多了
/**
 *         Point point = new Point(surfaceView.getWidth(), surfaceView.getHeight());
 *         Camera2Helper camera2Helper = new Camera2Helper.Builder()
 *                 .previewOn(surfaceView)
 *                 .previewViewSize(point)
 *                 .cameraListener(new Camera2Helper.CameraListener() {
 *                     @Override
 *                     public void onPreview(byte[] y, byte[] u, byte[] v, Size previewSize, int stride) {
 *                         Log.d("TAG", "onPreview");
 *                     }
 *
 *                     @Override
 *                     public void onCameraClosed() {
 *                         Log.d("TAG", "onCameraClosed");
 *                     }
 *
 *                     @Override
 *                     public void onCameraError(Exception e) {
 *                         Log.d("TAG", "onCameraError");
 *                     }
 *
 *                     @Override
 *                     public void onCameraOpen() {
 *                         Log.d("TAG", "onCameraOpen");
 *                     }
 *                 }).build();
 *         camera2Helper.start(MainActivity.this);
 */

public class Camera2Helper {

    private static final String TAG = "Camera2Helper";
    private int mCameraId;
    private Point previewViewSize;
    private View previewDisplayView;
    private Size previewSize;
    private Point specificPreviewSize;
    private ImageReader mImageReader;
    private Handler mBackgroundHandler;
    private HandlerThread mBackgroundThread;
    private CameraDevice mCameraDevice;
    private CaptureRequest.Builder mPreviewRequestBuilder;
    private CameraCaptureSession mCaptureSession;

    private Integer specificCameraId = null;
    private CameraListener cameraListener;
    private CameraManager mCameraManager;

    private boolean isDisplay;

    //当一个相机设备的可用状态发生变化时回调
    private CameraManager.AvailabilityCallback mAvailabilityCallback = new CameraManager.AvailabilityCallback() {
        @Override
        public void onCameraAvailable(@NonNull String cameraId) {
            super.onCameraAvailable(cameraId);
        }

        @Override
        public void onCameraUnavailable(@NonNull String cameraId) {
            super.onCameraUnavailable(cameraId);
        }
    };

    //当一个相机设备的闪光灯的 Torch 模式可用状态发生变化时回调
    //通过 setTorchMode(String cameraId, boolean enabled) 方法设置 Torch 模式。
    private CameraManager.TorchCallback mTorchCallback = new CameraManager.TorchCallback() {
        @Override
        public void onTorchModeUnavailable(@NonNull String cameraId) {
            super.onTorchModeUnavailable(cameraId);
        }

        @Override
        public void onTorchModeChanged(@NonNull String cameraId, boolean enabled) {
            super.onTorchModeChanged(cameraId, enabled);
        }
    };

    private Camera2Helper(Camera2Helper.Builder builder) {

        specificCameraId = builder.specificCameraId;
        cameraListener = builder.cameraListener;

        if (builder.previewDisplayView == null) {
            isDisplay = false;
        } else {
            isDisplay = true;
            previewDisplayView = builder.previewDisplayView;
            previewViewSize = builder.previewViewSize;
            specificPreviewSize = builder.previewSize;
        }
    }


    public void start(Context context) {
        synchronized (this) {

            mCameraManager = (CameraManager)
                    context.getSystemService(Context.CAMERA_SERVICE);

//            mCameraManager.registerAvailabilityCallback(mAvailabilityCallback, mHandler);
//            mCameraManager.registerTorchCallback(mTorchCallback, mHandler);
            try {

                mCameraId = mCameraManager.getCameraIdList().length - 1;
                //若指定了相机ID且该相机存在,则打开指定的相机
                if (specificCameraId != null && specificCameraId <= mCameraId) {
                    mCameraId = specificCameraId;
                }

                //没有相机
                if (mCameraId == -1) {
                    if (cameraListener != null) {
                        cameraListener.onCameraError(new Exception("camera not found"));
                    }
                    return;
                }

//            这个摄像头的配置信息
                CameraCharacteristics characteristics =
                        mCameraManager.getCameraCharacteristics(String.valueOf(mCameraId));


                StreamConfigurationMap map = characteristics.get
                        (CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

                previewSize = getBestSupportedSize(previewViewSize, new ArrayList<Size>(Arrays.asList(map.getOutputSizes(SurfaceTexture.class))));

                mBackgroundThread = new HandlerThread("CameraBackground");
                mBackgroundThread.start();
                mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
                if (!isDisplay) {
                    mImageReader = ImageReader.newInstance(previewSize.getWidth(),
                            previewSize.getHeight(),
                            ImageFormat.YUV_420_888, 2
                    );
                    mImageReader.setOnImageAvailableListener(new OnImageAvailableListenerImpl(), mBackgroundHandler);
                }


                if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                    return;
                }
                mCameraManager.openCamera(String.valueOf(mCameraId), mDeviceStateCallback, mBackgroundHandler);
            } catch (Exception e) {
                e.printStackTrace();
            }


        }
    }

    private void stop() {
        try {
            if (null != mCaptureSession) {
                mCaptureSession.stopRepeating();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void release() {
        cameraListener.onCameraClosed();
        stop();
        try {
            if (null != mCaptureSession) {
                mCaptureSession.close();
                mCaptureSession = null;
            }
            if (null != mCameraDevice) {
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (null != mImageReader) {
                mImageReader.close();
                mImageReader = null;
            }
            if (null != cameraListener) {
                cameraListener = null;
            }
            //当回调不再需要时一定要注销,否则将带来内存泄漏的问题。
//            mCameraManager.unregisterAvailabilityCallback(mAvailabilityCallback);
//            mCameraManager.unregisterTorchCallback(mTorchCallback);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void selectCeameraId() {
        try {
            //获取可用相机
            String[] cameraIdList = mCameraManager.getCameraIdList();
            for (String cameraId : cameraIdList) {
                //获取摄像头参数
                CameraCharacteristics mCameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private CameraDevice.StateCallback mDeviceStateCallback = new CameraDevice.StateCallback() {


        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {

            cameraListener.onCameraOpen();
            mCameraDevice = cameraDevice;

            createCameraPreviewSession();
        }

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

        @Override
        public void onError(@NonNull CameraDevice camera, int error) {
            camera.close();
            cameraListener.onCameraError(new Exception("CameraDevice.StateCallback error is " + error));
            mCameraDevice = null;
        }
    };

    private void createCameraPreviewSession() {
        try {
            /**
             * TEMPLATE_PREVIEW:预览模式
             * TEMPLATE_STILL_CAPTURE:拍照模式
             * TEMPLATE_RECORD:视频录制模式
             * TEMPLATE_VIDEO_SNAPSHOT:视频截图模式
             * TEMPLATE_MANUAL:手动配置参数模式
             *
             */
            mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);

            //可以设置图像格式、图像分辨率、传感器控制、闪光灯控制、3A(自动对焦-AF、自动曝光-AE和自动白平衡-AWB)控制等
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            if (isDisplay) {
                Surface surface;
                if (previewDisplayView instanceof SurfaceView) {

                    surface = ((SurfaceView) previewDisplayView).getHolder().getSurface();

                    mPreviewRequestBuilder.addTarget(surface);
                } else {
                    SurfaceTexture texture = ((TextureView) previewDisplayView).getSurfaceTexture();

                    texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());

                    surface = new Surface(texture);

                    mPreviewRequestBuilder.addTarget(surface);
                }
                mCameraDevice.createCaptureSession(Arrays.asList(surface), mCaptureStateCallback, mBackgroundHandler);
            } else {
                mPreviewRequestBuilder.addTarget(mImageReader.getSurface());
                mCameraDevice.createCaptureSession(Arrays.asList(mImageReader.getSurface()), mCaptureStateCallback, mBackgroundHandler);
            }


        } catch (CameraAccessException e) {
            e.printStackTrace();
            cameraListener.onCameraError(e);
        }


    }

    private CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() {


        @Override
        public void onConfigured(@NonNull CameraCaptureSession session) {

            if (null == mCameraDevice) {
                return;
            }
            mCaptureSession = session;
            try {
                mCaptureSession.setRepeatingRequest(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onConfigureFailed(@NonNull CameraCaptureSession session) {
        }
    };

    private CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureResult partialResult) {

        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {

        }
    };


    private Size getBestSupportedSize(Point previewViewSize, ArrayList<Size> sizes) {
        Size bestSize = sizes.get(0);
        if (previewViewSize == null) {
            return bestSize;
        }

        float previewViewRatio = (float) previewViewSize.x / (float) previewViewSize.y;

        if (previewViewRatio > 1) {
            previewViewRatio = 1 / previewViewRatio;
        }

        for (Size s : sizes) {
            if (specificPreviewSize != null && specificPreviewSize.x == s.getWidth() && specificPreviewSize.y == s.getHeight()) {
                return s;
            }
            if (Math.abs((s.getHeight() / (float) s.getWidth()) - previewViewRatio) < Math.abs(bestSize.getHeight() / (float) bestSize.getWidth() - previewViewRatio)) {
                bestSize = s;
            }
        }
        return bestSize;
    }


    private class OnImageAvailableListenerImpl implements ImageReader.OnImageAvailableListener {
        private byte[] y;
        private byte[] u;
        private byte[] v;
        
        @Override
        public void onImageAvailable(ImageReader reader) {
            Image image = reader.acquireNextImage();
            Image.Plane[] planes = image.getPlanes();
            if (y == null) {
                y = new byte[planes[0].getBuffer().limit() - planes[0].getBuffer().position()];
                u = new byte[planes[1].getBuffer().limit() - planes[1].getBuffer().position()];
                v = new byte[planes[2].getBuffer().limit() - planes[2].getBuffer().position()];
            }
            if (image.getPlanes()[0].getBuffer().remaining() == y.length) {
//                分别填到 yuv

                planes[0].getBuffer().get(y);
                planes[1].getBuffer().get(u);
                planes[2].getBuffer().get(v);
            }
            if (cameraListener != null) {
                cameraListener.onPreview(y, u, v, previewSize, planes[0].getRowStride());
            }
            image.close();
        }
    }

    private void yuvToNv21(byte[] y, byte[] u, byte[] v, byte[] nv21, int stride, int height) {
        System.arraycopy(y, 0, nv21, 0, y.length);
        // 注意,若length值为 y.length * 3 / 2 会有数组越界的风险,需使用真实数据长度计算
        int length = y.length + u.length / 2 + v.length / 2;
        int uIndex = 0, vIndex = 0;
        for (int i = stride * height; i < length; i += 2) {
            nv21[i] = v[vIndex];
            nv21[i + 1] = u[uIndex];
            vIndex += 2;
            uIndex += 2;
        }
    }


    public interface CameraListener {
        /**
         * 预览数据回调
         *
         * @param y           预览数据,Y分量
         * @param u           预览数据,U分量
         * @param v           预览数据,V分量
         * @param previewSize 预览尺寸
         * @param stride      步长
         */
        void onPreview(byte[] y, byte[] u, byte[] v, Size previewSize, int stride);

        /**
         * 当相机关闭时执行
         */
        void onCameraClosed();

        /**
         * 当出现异常时执行
         *
         * @param e 相机相关异常
         */
        void onCameraError(Exception e);


        void onCameraOpen();
    }

    public static final class Builder {

        /**
         * 预览显示的view,目前仅支持surfaceView和textureView
         */
        private View previewDisplayView;
        /**
         * 指定的相机ID
         */
        private Integer specificCameraId;
        /**
         * 事件回调
         */
        private CameraListener cameraListener;
        /**
         * 屏幕的长宽,在选择最佳相机比例时用到
         */
        private Point previewViewSize;
        /**
         * 指定的预览宽高,若系统支持则会以这个预览宽高进行预览
         */
        private Point previewSize;

        public Builder() {
        }


        public Camera2Helper.Builder previewOn(View val) {
            if (val instanceof SurfaceView || val instanceof TextureView) {
                previewDisplayView = val;
                return this;
            } else {
                throw new RuntimeException("you must preview on a textureView or a surfaceView");
            }
        }


        public Camera2Helper.Builder previewSize(Point val) {
            previewSize = val;
            return this;
        }

        public Camera2Helper.Builder previewViewSize(Point val) {
            previewViewSize = val;
            return this;
        }


        public Camera2Helper.Builder specificCameraId(Integer val) {
            specificCameraId = val;
            return this;
        }

        public Camera2Helper.Builder cameraListener(CameraListener val) {
            cameraListener = val;
            return this;
        }

        public Camera2Helper build() {
            if (previewViewSize == null) {
                Log.e(TAG, "previewViewSize is null, now use default previewSize");
            }
            if (cameraListener == null) {
                Log.e(TAG, "cameraListener is null, callback will not be called");
            }
            if (previewDisplayView == null) {
                Log.e(TAG, "previewDisplayView is null ,you can preview on a textureView or a surfaceView");
            }
            return new Camera2Helper(this);
        }
    }


}

    可能是太难用了,google 自己都看不下去了,现在又搞了一个 camerax 来简化调用相机的难度,但是 camerax 没有具体了解过,这里不做介绍。但是从代码量上来说 100 行不到就可以实现预览了,对于 camera2 来说,简直不要太友好。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容