Camera 2 最简单的预览拍照保存小白指南

1.Camera 2 的架构图:

   

                                                                camera2架构图

 Camera2引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送Capture请求,而摄像头会返回CameraMetada。这一切都建立在CameraCaptureSession的会话中。

2.camera2拍照流程图

                                                 camera2拍照流程图

3.具体实现步骤

3.1 申请权限 动态申请

        Amdroidmanifest.xml文件中也需要输入以下代码

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

         MainActivity.java 中

 private static final String TAG = "预览";
    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);
    }
    private String mCameraId;         // 摄像头Id
    private Size mPreviewSize;      //获取分辨率
    private ImageReader mImageReader;  //图片阅读器
    private CameraDevice mCameraDevice;   //摄像头设备
    private CameraCaptureSession mCaptureSession;   //获取会话
    private CaptureRequest mPreviewRequest;      //获取请求
    private CaptureRequest.Builder mPreviewRequestBuilder;   //创建获取请求
    private TextureView textureView;      //预览视图
    private Surface mPreviewSurface;      //
    private String[] permissions = {Manifest.permission.CAMERA, 
        Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE };
    private List<String> permissionList = new ArrayList();
    // 第一步:获取权限
    /**
     * 获取拍照和读写权限
     */
    private void getPermission() {
        Log.i(TAG, " getPermission");
        if (permissionList != null) {
            permissionList.clear();
        }
        //版本判断 当手机系统大于23时,才有必要去判断权限是否获取
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            //权限是否已经 授权 GRANTED-授权  DINIED-拒绝
            for (String permission : permissions) {
                if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                    permissionList.add(permission);
                }
            }
            if (!permissionList.isEmpty()) {
                ActivityCompat.requestPermissions(this, permissionList.toArray(new String[permissionList.size()]), 1000);
            } else {
                //表示全都授权了
                textureView.setSurfaceTextureListener(textureListener);
            }
        }
    }
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] mPermissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == 1000) {
            //权限请求失败
            if (grantResults.length > 0) {
                //存放没授权的权限
                List<String> deniedPermissions = new ArrayList<>();
                for (int i = 0; i < grantResults.length; i++) {
                    int grantResult = grantResults[i];
                    String permission = permissions[i];
                    if (grantResult != PackageManager.PERMISSION_GRANTED) {
                        deniedPermissions.add(permission);
                    }
                }
                if (deniedPermissions.isEmpty()) {
                    //说明都授权了
                    openCamera();
                } else {
                    getPermission();
                }
            }
        }
    }

3.2 在xml布局文件中

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextureView
        android:id="@+id/textureView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

    <ImageButton
        android:id="@+id/takePicture"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/ic_baseline_camera_24"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="50dp"

        />

</RelativeLayout>

3.3 写SurfaceView回调以及摄像头状态回调

 // 1.  SurfaceView状态回调
    TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
            setupCamera(width,height);
            configureTransform(width,height);   //使配置改变
            openCamera();             //打开相机
        }

        @Override
        public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surfaceTexture, int width, int height) {
            configureTransform(width,height);
        }

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

        @Override
        public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surfaceTexture) {
        }
    };

    // 2.  摄像头状态回调
    private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
        //打开成功获取到camera设备
        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            mCameraDevice = cameraDevice;
            //开启预览
            startPreview();
        }
        //打开失败
        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            Toast.makeText(MainActivity.this, "摄像头设备连接失败", Toast.LENGTH_SHORT).show();
        }
        //打开错误
        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int i) {
            Toast.makeText(MainActivity.this, "摄像头设备连接出错", Toast.LENGTH_SHORT).show();
        }
    };

3.4 设置相机

//设置摄像机
    private void setupCamera(int width,int height){
        //获取摄像头的管理者CameraManager
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            //遍历所有摄像头
            for(String cameraId : manager.getCameraIdList()){
                CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); //获取摄像机的特征
                //默认打开后置  - 忽略前置 LENS(镜头)
                if(characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT)  //如果是前置跳过
                {
                    continue;
                }
                //获取StreamConfigurationMap,他是管理摄像头支持的所有输出格式
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mPreviewSize = getOptimalSize(map.getOutputSizes(SurfaceTexture.class),width,height); //获取最佳的预览大小
                int orientation = getResources().getConfiguration().orientation;
                if(orientation== Configuration.ORIENTATION_LANDSCAPE){
                    textureView.setSurfaceTextureListener(textureListener);
                }else {
                    textureView.setSurfaceTextureListener(textureListener);
                }
                mCameraId = cameraId;
                break;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

3.5 打开相机  关闭相机

private void openCamera(){
        //获取摄像头的管理者 CameraManager
        CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        //检查权限
        try{
            if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
                return;
            }else{
                manager.openCamera(mCameraId,stateCallback,null);
            }
        }catch (CameraAccessException e){
            e.printStackTrace();
        }
    }
    //  关闭相机
        private void closeCamera(){
        if(mCaptureSession != null){
            mCaptureSession.close();
            mCaptureSession = null;
        }
        if(mCameraDevice != null){
            mCameraDevice.close();
            mCameraDevice=null;
        }
    }

3.6 开启预览

 private void startPreview(){
        setupImageReader();
        SurfaceTexture mSurfaceTexture = textureView.getSurfaceTexture();
        //设置TextureView的缓冲区大小
        mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(),mPreviewSize.getHeight());
        //获取Surface显示预览数据
        mPreviewSurface = new Surface(mSurfaceTexture);
        try {
            getPreviewRequestBuilder();
            //创建相机捕捉会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当他创建好后会回调onCconfigured方法
            //第三个参数用来确定Callback在那个线程执行,null表示在当前线程执行
            mCameraDevice.createCaptureSession(Arrays.asList(mPreviewSurface, mImageReader.getSurface()), new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                    mCaptureSession = cameraCaptureSession;
                    repeatPreview();
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                }
            }, null);
        }catch (CameraAccessException e){
            e.printStackTrace();
        }
    }

3.7 实现previewcallback

   private CameraCaptureSession.CaptureCallback mPreviewCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        @Override
        // 一旦捕获完成
        public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
            super.onCaptureCompleted(session, request, result);
        }
    };
    private void repeatPreview(){
        mPreviewRequestBuilder.setTag(TAG);
        mPreviewRequest = mPreviewRequestBuilder.build();
        //设置反复捕获数据的请求,这样预览界面就会一直有数据显示
        try {
            mCaptureSession.setRepeatingRequest(mPreviewRequest,mPreviewCaptureCallback,null);
        }catch (CameraAccessException e)
        {
            e.printStackTrace();
        }
    }

3.8 设置图片阅读器

private void setupImageReader(){
        //前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据
        mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(),mPreviewSize.getHeight(), ImageFormat.JPEG,1);
        //监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable
        mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader imageReader) {
                Toast.makeText(MainActivity.this,"图片已保存",Toast.LENGTH_SHORT).show();
                Image image = imageReader.acquireNextImage();
                //开启线程一部保存图片
                new Thread(new ImageSaver(image)).start();
            }
        },null);
    }
    //选择sizeMap中大于并且接近width和height的size
    private Size getOptimalSize(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 size, Size t1) {
                    return Long.signum(size.getWidth()*size.getHeight()- t1.getWidth()*t1.getHeight());
                }
            });
        }
        return sizeMap[0];
    }
    //使配置转换
    private void configureTransform(int viewWidth,int viewHeight){
        if(textureView == null || mPreviewSize == null){
            return;
        }
        int rotation = getWindowManager().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(rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_90){
            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(rotation == Surface.ROTATION_180){
            matrix.postRotate(180,centerX,centerY);
        }
        textureView.setTransform(matrix);
    }

3.9 创建预览请求

  //创建预览请求的Builder (TEMPLATE_PREVIEW表示预览请求)
    private void getPreviewRequestBuilder(){
        try {
            mPreviewRequestBuilder =mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
        }catch (CameraAccessException e){
            e.printStackTrace();
        }
        //设置预览的显示图
        mPreviewRequestBuilder.addTarget(mPreviewSurface);
        MeteringRectangle[] meteringRectangles = mPreviewRequestBuilder.get(CaptureRequest.CONTROL_AE_REGIONS);
        if(meteringRectangles != null && meteringRectangles.length > 0){
            Toast.makeText(MainActivity.this,"PreviewRequestBuilder: AF_REGIONS=",Toast.LENGTH_SHORT).show();
        }
        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,CaptureRequest.CONTROL_MODE_AUTO);
        mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
    }

3.10 拍照

//  拍照
    private void takePhoto(){
        try {
            //首先创建拍照的请求 CaptureRequest
            final CaptureRequest.Builder mCaptureBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            //获取屏幕方向
            int rotation = getWindowManager().getDefaultDisplay().getRotation();
            mCaptureBuilder.addTarget(mPreviewSurface);
            mCaptureBuilder.addTarget(mImageReader.getSurface());
            //设置拍照方向
            mCaptureBuilder.set(CaptureRequest.JPEG_ORIENTATION,ORIENTATION.get(rotation));
            //停止预览
            mCaptureSession.stopRepeating();
            //开始拍照,然后回调上面的接口重启预览,因为mCaptureBuilder设置ImageReader作为target,所以会自动回调ImageReader
            // 的onImageAvailable()方法保存图片
            CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
                @Override
                public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request,
                                               @NonNull TotalCaptureResult result) {
                    super.onCaptureCompleted(session, request, result);
                    repeatPreview();
                }
            };
            mCaptureSession.capture(mCaptureBuilder.build(),captureCallback,null);
        }catch (CameraAccessException e){
            e.printStackTrace();
        }
    }

3.11 创建 子线程保存图片

 // 第九步 创建子线程保存图片
    public  static  class ImageSaver implements  Runnable{
        private Image mImage;

        public ImageSaver(Image image) {
            mImage = image;
        }
        @Override
        public void run(){
            ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
            byte[] data =new  byte[buffer.remaining()];
            buffer.get(data);
            File imageFile = new File(Environment.getExternalStorageDirectory()+"/DCIM/System.currentTimeMillis+myPicture.jpg");
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(imageFile);
                fos.write(data,0, data.length);
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                if(fos !=null){
                    try {
                        fos.close();
                    }catch (IOException e){
                        e.printStackTrace();
                    }
                }
                mImage.close(); // 必须关闭 不然拍第二章会报错
            }
        }
    }

3.12  活动的生命周期

 //活动的创建
    @Override
    protected void onCreate(Bundle saveInstanceState){
        super.onCreate(saveInstanceState);
        setContentView(R.layout.activity_main);
        textureView = findViewById(R.id.textureView);
        textureView.setSurfaceTextureListener(textureListener);
        getPermission();
        findViewById(R.id.takePicture).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                takePhoto();
            }
        });
    }
    //onPause
    @Override
    protected void onPause(){
      //closeCamera();
        super.onPause();
    }
    protected void onResume(){
        super.onResume();
       // textureView.setSurfaceTextureListener(textureListener);
        //openCamera();
    }
    protected void onDestroy(){
        super.onDestroy();
        closeCamera();
    }

这样就完成了 简单的预览拍照功能

Guess you like

Origin blog.csdn.net/I_am_a_loser/article/details/120249425
Recommended