zoukankan      html  css  js  c++  java
  • Android开发 Camera2拍照功能实现(另一种思路的实现)

    前言

      在之前的博客https://www.cnblogs.com/guanxinjing/p/10940049.html 里,已经说明过如何使用Camera2实现拍照了。

      但是这个博客的讲解在Camera2控制上有点麻烦,因为这篇博客的思想是在页面进入后台后依然持有Camera与Camera会话,让下一次页面重新进入前台后使用原来的Camera会话重新打开预览与实现拍照。这样的方式优点是:

      1.响应快速,不会因为反复操作前后台导致多次初始化。

      2.在后台后Camera依然保持运行。

      但是这样的模式的缺点是:当App进入后台后,其他App调用了摄像头,你下次进入后就会无法获取摄像头了。并且还有一个延伸缺点是非常容易因为其他因素导致内存泄露,需要小心调试。

      所以,我们可以用另外一种简单直接的思想实现Camera2的功能,那就是在onStop后就清理摄像头持有,结束全部会话。在onStart后在重新获取持有摄像头,然后重新获得会话。这样的好处是:

      1.下次从后台进入前台能保证当前摄像头不会丢失,因为是重新获取的。

      2.Camera的释放会简单很多,直接在onStop里释放,不容易内存泄漏。

    代码部分

    import android.Manifest;
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.graphics.ImageFormat;
    import android.graphics.SurfaceTexture;
    import android.hardware.camera2.CameraAccessException;
    import android.hardware.camera2.CameraCaptureSession;
    import android.hardware.camera2.CameraCharacteristics;
    import android.hardware.camera2.CameraDevice;
    import android.hardware.camera2.CameraManager;
    import android.hardware.camera2.CaptureRequest;
    import android.hardware.camera2.params.StreamConfigurationMap;
    import android.media.Image;
    import android.media.ImageReader;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.HandlerThread;
    import android.util.DisplayMetrics;
    import android.util.Log;
    import android.util.Size;
    import android.view.LayoutInflater;
    import android.view.Surface;
    import android.view.TextureView;
    import android.view.View;
    import android.view.ViewGroup;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    import androidx.navigation.Navigation;
    
    import net.yt.lib.permission.OnRequestPermissionCallback;
    import net.yt.lib.permission.PermissionRequest;
    import net.yt.lib.sdk.utils.ButtonDelayUtil;
    import net.yt.lib.sdk.utils.ToastUtils;
    import net.yt.whale.owner.yzhome.R;
    import net.yt.whale.owner.yzhome.app.BaseFragment;
    import net.yt.whale.owner.yzhome.databinding.HomeFragmentFaceTakePicturesBinding;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    import java.util.Arrays;
    
    public class FaceTakePicturesFragment extends BaseFragment implements View.OnClickListener {
        private HomeFragmentFaceTakePicturesBinding mBinding;
        private CameraManager mCameraManager;
        private CameraDevice mCameraDevice;
        private CameraCaptureSession.StateCallback mSessionStateCallback;
        private CaptureRequest.Builder mCaptureRequest;
        private CameraDevice.StateCallback mCameraStateCallback;
        private CameraCaptureSession mCameraCaptureSession;
        private ImageReader mImageReader;
        private Surface mSurface;
        private SurfaceTexture mSurfaceTexture;
        private String mCurrentCameraId;
        private HandlerThread mHandlerThread;
        private Handler mChildHandler;
    
        @Override
        public void onAttach(@NonNull Context context) {
            super.onAttach(context);
        }
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
        }
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            mBinding = HomeFragmentFaceTakePicturesBinding.inflate(getLayoutInflater());
            return mBinding.getRoot();
        }
    
        @Override
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            mBinding.back.setOnClickListener(this);
            mBinding.takePictures.setOnClickListener(this);
            initListener();
    
        }
    
        @Override
        public void onStart() {
            super.onStart();
            initChildThread();
            if (mBinding.textureView.isAvailable()) {
                selectOpenCamera();
            }
        }
    
        @Override
        public void onStop() {
            super.onStop();
            if (mImageReader != null){
                mImageReader.close();
                mImageReader = null;
            }
            if (mCaptureRequest != null){
                mCaptureRequest.removeTarget(mSurface);
            }
            if (mCameraCaptureSession != null){
                try {
                    mCameraCaptureSession.stopRepeating();
                    mCameraCaptureSession.close();
                } catch (CameraAccessException e) {
                    e.printStackTrace();
                }
            }
            if (mCameraDevice != null){
                mCameraDevice.close();
                mCameraDevice = null;
            }
            if (mChildHandler != null){
                mChildHandler.removeCallbacksAndMessages(null);
                mChildHandler = null;
            }
            if (mHandlerThread != null){
                mHandlerThread.quitSafely();
                mHandlerThread = null;
            }
        }
    
        @Override
        public void onResume() {
            super.onResume();
    
        }
    
        @Override
        public void onPause() {
            super.onPause();
        }
    
        @Override
        public void onClick(View v) {
            int id = v.getId();
            if (id == R.id.back){
                Navigation.findNavController(getView()).navigateUp();
    
            }else if (id == R.id.takePictures){
                if (ButtonDelayUtil.isFastClick()){
                    takePicture();
                }
            }
    
        }
    
        private void initListener() {
            mBinding.textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
                @Override
                public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
                    mSurfaceTexture = surface;
                    selectOpenCamera();
                }
    
                @Override
                public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
                }
    
                @Override
                public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                    return false;
                }
    
                @Override
                public void onSurfaceTextureUpdated(SurfaceTexture surface) {
    
                }
            });
            //相机状态
            mCameraStateCallback = new CameraDevice.StateCallback() {
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                    mCameraDevice = camera;
                    try {
                        Size matchingSize = getMatchingSize();
                        initImageReader(matchingSize.getWidth(), matchingSize.getHeight());
                        mSurfaceTexture = mBinding.textureView.getSurfaceTexture();
                        mSurfaceTexture.setDefaultBufferSize(matchingSize.getWidth(), matchingSize.getHeight());
                        mSurface = new Surface(mSurfaceTexture);
                        mCaptureRequest = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
                        mCaptureRequest.addTarget(mSurface);
                        mCaptureRequest.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
                        mCameraDevice.createCaptureSession(Arrays.asList(mSurface, mImageReader.getSurface()), mSessionStateCallback, mChildHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        Navigation.findNavController(getView()).navigateUp();
                        ToastUtils.showShortToast("打开相机失败");
                    }
                }
    
                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {
    
                }
    
                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                    ToastUtils.showShortToast("打开相机失败:" + error);
                    Navigation.findNavController(getView()).navigateUp();
    
                }
            };
            //会话状态
            mSessionStateCallback = new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    mCameraCaptureSession = session;
                    try {
                        mCameraCaptureSession.setRepeatingRequest(mCaptureRequest.build(), null, mChildHandler);
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        ToastUtils.showShortToast("相机预览失败:" + e.getMessage());
                        Navigation.findNavController(getView()).navigateUp();
    
                    }
                }
    
                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                    //配置失败
                    try {
                        session.stopRepeating();
                    } catch (CameraAccessException e) {
                        e.printStackTrace();
                        Navigation.findNavController(getView()).navigateUp();
                    }
                    Navigation.findNavController(getView()).navigateUp();
    
                }
            };
        }
    
        /**
         * 初始化子线程
         */
        private void initChildThread() {
            mHandlerThread = new HandlerThread("camera2");
            mHandlerThread.start();
            mChildHandler = new Handler(mHandlerThread.getLooper());
        }
    
        public void selectOpenCamera() {
            PermissionRequest request = new PermissionRequest.Builder(this)//权限
                    .addPermission(Manifest.permission.CAMERA)
                    .setCallback(new OnRequestPermissionCallback() {
                        @SuppressLint("MissingPermission")
                        @Override
                        public void onRequest(boolean granted) {
                            if (granted) {
                                mCameraManager = (CameraManager) getContext().getApplicationContext().getSystemService(Context.CAMERA_SERVICE);
                                try {
                                    String[] cameraIdList = mCameraManager.getCameraIdList();
                                    if (cameraIdList.length == 0) {
                                        ToastUtils.showShortToast("此设备没有摄像头");
                                        Navigation.findNavController(getView()).navigateUp();
                                        return;
                                    }
                                    for (String cameraId : cameraIdList) {
                                        CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(cameraId);
                                        Integer facing = cameraCharacteristics.get(CameraCharacteristics.LENS_FACING);//获取这个摄像头的面向
                                        if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
                                            mCurrentCameraId = cameraId;
                                        }
                                    }
                                    mCameraManager.openCamera(mCurrentCameraId, mCameraStateCallback, mChildHandler);
                                } catch (CameraAccessException e) {
                                    e.printStackTrace();
                                    ToastUtils.showShortToast("打开相机失败:" + e.getMessage());
                                    Navigation.findNavController(getView()).navigateUp();
                                }
                            } else {
                                ToastUtils.showShortToast("未获取相机权限");
                                Navigation.findNavController(getView()).navigateUp();
                            }
                        }
                    }).build();
            request.requestPermissions();
        }
    
        private Size getMatchingSize() {
            Size selectSize = null;
            try {
                CameraCharacteristics cameraCharacteristics = mCameraManager.getCameraCharacteristics(mCurrentCameraId);
                StreamConfigurationMap streamConfigurationMap = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                Size[] sizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG);
                DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
                int deviceWidth = displayMetrics.widthPixels;
                int deviceHeigh = displayMetrics.heightPixels;
                for (int j = 1; j < 41; j++) {
                    for (int i = 0; i < sizes.length; i++) {
                        Size itemSize = sizes[i];
                        if (itemSize.getHeight() < (deviceWidth + j * 5) && itemSize.getHeight() > (deviceWidth - j * 5)) {
                            if (selectSize != null) {
                                if (Math.abs(deviceHeigh - itemSize.getWidth()) < Math.abs(deviceHeigh - selectSize.getWidth())) { //求绝对值算出最接近设备高度的尺寸
                                    selectSize = itemSize;
                                    continue;
                                }
                            } else {
                                selectSize = itemSize;
                            }
                        }
                    }
                    if (selectSize != null) {
                        return selectSize;
                    }
                }
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
            return selectSize;
        }
    
        /**
         * 初始化图片读取器
         */
        private void initImageReader(int width, int height) {
            //创建图片读取器,参数为分辨率宽度和高度/图片格式/需要缓存几张图片,我这里写的2意思是获取2张照片
            mImageReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 2);
            mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
                @Override
                public void onImageAvailable(ImageReader reader) {
                    Image image = reader.acquireNextImage();
                    File path = new File(getContext().getExternalCacheDir().getPath());
                    File file = new File(path, "demo.jpg");//TODO 后续修改路径
                    if (!path.exists()) {
                        path.mkdirs();
                    }
                    try (FileOutputStream fileOutputStream = new FileOutputStream(file)) {
                        ByteBuffer byteBuffer = image.getPlanes()[0].getBuffer();
                        byte[] bytes = new byte[byteBuffer.remaining()];
                        byteBuffer.get(bytes);
                        fileOutputStream.write(bytes);
                        fileOutputStream.flush();
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        image.close();
                    }
                }
            }, mChildHandler);
        }
    
        private void takePicture() {
            CaptureRequest.Builder captureRequestBuilder = null;
            try {
                captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
                captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);//自动对焦
                captureRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);//自动爆光
                captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, 270);
                Surface surface = mImageReader.getSurface();
                captureRequestBuilder.addTarget(surface);
                CaptureRequest request = captureRequestBuilder.build();
                mCameraCaptureSession.capture(request, null, mChildHandler);
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        }
    }
  • 相关阅读:
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
    第 6 章 Cinder
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/13426618.html
Copyright © 2011-2022 走看看