Android Camera2 工具类封装:
疑问:
Camera2 网上的资料感觉不是很多,很奇怪,难道Camera2没多少人用?还是说我low。。。。
求解:
发现启用硬件加速设置后,启动Camera2很卡,有没有好的解决办法,求解答:个人理解:
Camera2启动规则:
(1)、获取CameraManager,准备三个东西:
CameraDevice.StateCallback理解为:负责监听设备打开或关闭的状态的一个监听器;
CameraCaptureSession.StateCallback理解为:负责监听捕获设备启动会话请求状态的
一个监听器;
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,但是测试后还能用,有带优化。
问题:
测试后发现存在启动较慢的情况,如果可以解决,请告知。。。。