目前只是简单的实现的预览和拍照,照片没有处理,所以拍出来的不是正的。

android 获取相机预览数据 android camera preview_ci

 

权限先加好。

1.定义TextureView

要预览camera就要先定义一个用来显示的控件。布局很简单,不上代码了。

首先看TextureView是否可用,可用就直接去打开camera,否则设置一个监听,当TextureView可用的时候再去打开。

@Override
    protected void onResume() {
        startBackgroundThread();

        if (mTextureView.isAvailable()) {
            openCamera(mTextureView.getWidth(), mTextureView.getHeight());
        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }

        super.onResume();
    }
private final TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
            openCamera(width,height);
        }

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

        }

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

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {

        }
    };

这里还要开线程,就是camera跑的地方。当程序关闭记得关掉。

private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("camera");
        mBackgroundThread.start();
        mBackgroundHeandler = new Handler(mBackgroundThread.getLooper());
    }

    private void stopBackgroundThread() {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHeandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

 

2.设置预览的一些参数

private void setupCameraOutputs(int width, int height) {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

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

                //不使用前置摄像头
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
                    continue;

                //获取摄像头支持的配置属性
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (map == null)
                    continue;

                //获得摄像头支持的最大尺寸
                Size largest = Collections.max(
                        Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
                        new CompareSizesByArea());

                //preview最大不能超过1920x1080,不然camera会出错,不能预览。
                mPreviewSize = new Size(MAX_PREVIEW_WIDTH, MAX_PREVIEW_HEIGHT);
                mCameraId = cameraId;
                return;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

 

/**
     * Compares two {@code Size}s based on their areas.
     */
    static class CompareSizesByArea implements Comparator<Size> {

        @Override
        public int compare(Size lhs, Size rhs) {
            // We cast here to ensure the multiplications won't overflow
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
                    (long) rhs.getWidth() * rhs.getHeight());
        }

    }

 

3.打开camera

private void openCamera(int width, int height) {
        setupCameraOutputs(width, height);
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

        try {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHeandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

 mStateCallback是一个回调函数,查看camera的状态,如果camera已经打开了,就会走onOpened,然后我们就去显示预览画面。

private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
            createCaptureSession();
        }

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

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

 

4.显示预览

private void createCaptureSession() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());

            Surface mSurface = new Surface(texture);


            mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mCaptureRequestBuilder.addTarget(mSurface);

            //这里的list里面只有mSurface,如果要拍照还要加一个ImageReader
            mCameraDevice.createCaptureSession(Arrays.asList(mSurface), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                    mCameraCaptureSession = cameraCaptureSession;

                    mCaptureRequest = mCaptureRequestBuilder.build();
                    try {
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, mCaptureCallback, mBackgroundHeandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {

                }
            }, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }

    }

    private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
        }
    };

到这里,就能正常预览camera画面了

退出程序记得关闭camera和线程

@Override
    protected void onPause() {
        stopBackgroundThread();
        closeCamera();
        super.onPause();
    }
private void closeCamera() {
        if (mCameraCaptureSession != null) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }
        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
    }

 

拍照

这里没有考虑照片旋转,所以拍完的照片不是和preview的画面一样是正的。以后在加。

 

在设置参数的时候新建一个ImageReader,代码和前面一样,就多了2行,ImageReader的宽高不能超过(1920,1080)不然会有问题,为什么我就不知道了,我在google的camera2的Demo中看到了。

private void setupCameraOutputs(int width, int height) {
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

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

                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT)
                    continue;

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

                Size largest = Collections.max(
                        Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
                        new CompareSizesByArea());

                List<Size> list = new ArrayList<>();
                Collections.addAll(list, map.getOutputSizes(ImageFormat.JPEG));
                for (Size size : list) {

                    Log.i("aaron", "------- ");
                    Log.i("aaron", " " + size);
                }

                Log.i("aaron", "largest  " + largest.getWidth() + " " + largest.getHeight());
                Log.i("aaron", "texture  " + mTextureView.getWidth() + " " + mTextureView.getHeight());

                //选择camera最大支持的size来拍照
                mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 2);
                mImageReader.setOnImageAvailableListener(mOnImageAvailableListener, mBackgroundHeandler);

                //preview最大不能超过1920x1080,不然camera会出错,不能预览。
                mPreviewSize = new Size(width, height);
                mCameraId = cameraId;
                return;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

 

在创建预览的时候把ImageReader加进去。

mImageReader.getSurface()

mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                    mCameraCaptureSession = cameraCaptureSession;

                    mCaptureRequest = mCaptureRequestBuilder.build();
                    try {
                        //这里的CaptureCallback会一直回调,因为画面一直在preview
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, mCaptureCallback, mBackgroundHeandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }

 为按钮添加点击拍照功能。

这里的captureRequestBuilder是用来拍照的,然后addTarget把ImageReader加进去,拍照后就会有数据到ImageReader的回调函数了。

拍照的时候不能预览,所以要先关闭预览,拍完在重新开启预览。

private void takePicture() {
        try {
            CaptureRequest.Builder captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            //添加ImageReader,当拍照时就会走ImageReader的回调,之前设置的。
            captureRequestBuilder.addTarget(mImageReader.getSurface());

            CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
                    //拍照完成,重新preview
                    try {
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest, mCaptureCallback, mBackgroundHeandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                    }
                }
            };

            //停止preview,然后去拍照
            mCameraCaptureSession.stopRepeating();
            mCameraCaptureSession.abortCaptures();
            //这里的CaptureCallback在拍照的时候才会回调。和preview那里的不一样,preview那里会一直回调。
            mCameraCaptureSession.capture(captureRequestBuilder.build(), captureCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

拍到的照片在mOnImageAvailableListener回调中,然后去开线程保存。

private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
            = new ImageReader.OnImageAvailableListener() {

        @Override
        public void onImageAvailable(ImageReader reader) {
            //保存拍到的照片
            mBackgroundHeandler.post(new ImageSaver(reader.acquireNextImage(), mFile));
            Log.i("aaron", "onImageAvailable");
        }

    };
/**
     * Saves a JPEG {@link Image} into the specified {@link File}.
     */
    private static class ImageSaver implements Runnable {

        /**
         * The JPEG image
         */
        private final Image mImage;
        /**
         * The file we save the image into.
         */
        private final File mFile;

        ImageSaver(Image image, File file) {
            mImage = image;
            mFile = file;
        }

        @Override
        public void run() {
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] bytes = new byte[buffer.remaining()];
            buffer.get(bytes);
            FileOutputStream output = null;
            try {
                output = new FileOutputStream(mFile);
                output.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                mImage.close();
                if (null != output) {
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }


        }

    }

文件就随便在哪里创建下 

mFile = new File(Environment.getExternalStorageDirectory().toString() +
                "/Pictures/ab.jpg");

最后在程序退出关闭camera的时候把ImageReader一起关了

private void closeCamera() {
        if (mCameraCaptureSession != null) {
            mCameraCaptureSession.close();
            mCameraCaptureSession = null;
        }
        if (mCameraDevice != null) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        if (null != mImageReader) {
            mImageReader.close();
            mImageReader = null;
        }
    }