由于最近需要使用相机拍照等功能,鉴于老旧的相机API问题多多,而且新的设备都是基于安卓5.0以上的,于是本人决定研究一下安卓5.0新引入的Camera2 API 来实现 Camera2API地址
首先我们来熟悉一下官方给的这几个图:
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送
Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。
其中 CameraManager 是那个站在高处统管所有摄像投设备(CameraDevice)的管理者,而每个 CameraDevice 自己会负责建立 CameraCaptureSession 以及建立 CaptureRequest。CameraCharacteristics 是 CameraDevice 的属性描述类,非要做个对比的话,那么它与原来的 CameraInfo 有相似性。 类图中有着三个重要的 callback,虽然这增加了阅读代码的难度,但是你必须要习惯,因为这是新包的风格。其中 CameraCaptureSession.CaptureCallback 将处理预览和拍照图片的工作,需要重点对待。这些类是如何相互配合的?下面是简单的流程图。
有了这三张图,那么接下来就好了理解了,我们按照拍照流程的指示,来吧整个过程走一遍
1首先定义一个SufaceView 用来实现预览照片用
surfaceView=(SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { initCamera(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } });
其中initCamera2()方法是用来初始化相机的方法
2获取Camera ID,该ID是用来打开相机的关键,一般后置摄像头是0,前置摄像头是1,这里我们选择后置摄像头做详解
mCameraID = "" + CameraCharacteristics.LENS_FACING_BACK;//后摄像头
3通过Camera ID 来打开摄像头,这里我们需要使用CamerManager,这是类是一个管理服务类,值得注意的是,打开摄像头是一个相当复杂的过程,不能直接在主线程中直接执行,其核心代码为:
HandlerThread handlerThread=new HandlerThread("Camera2"); handlerThread.start(); childHandler=new Handler(handlerThread.getLooper()); mainHandler=new Handler(getMainLooper());
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地 @Override public void onImageAvailable(ImageReader reader) { mCameraDevice.close(); surfaceView.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); // 拿到拍照照片数据 Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes);//由缓冲区存入字节数组 final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); if (bitmap != null) { iv_show.setImageBitmap(bitmap); } } }, mainHandler);
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } //打开摄像头 cameraManager.openCamera(mCameraID, stateCallback, mainHandler); } catch (CameraAccessException e) { e.printStackTrace(); }注意打开摄像头需要权限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
4 开启相机后有一个回调,stateCallback,该回调是用来返回相机是否正常打开的状态的
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) {//打开摄像头 mCameraDevice = camera; //开启预览 takePreview(); } @Override public void onDisconnected(CameraDevice camera) {//关闭摄像头 if (null != mCameraDevice) { mCameraDevice.close(); MainActivity.this.mCameraDevice = null; } } @Override public void onError(CameraDevice camera, int error) {//发生错误 Toast.makeText(MainActivity.this, "摄像头开启失败", Toast.LENGTH_SHORT).show(); } };
5 相机开启成功后,执行回调中的onOpen方法,在该方法中,我们实现让图像显示在界面上
/** * 开始预览 */ private void takePreview() { try { // 创建预览需要的CaptureRequest.Builder final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 将SurfaceView的surface作为CaptureRequest.Builder的目标 previewRequestBuilder.addTarget(surfaceHolder.getSurface()); // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求 mCameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③ { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { if (null == mCameraDevice) return; // 当摄像头已经准备好时,开始显示预览 mCameraCaptureSession = cameraCaptureSession; try { // 自动对焦 previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 打开闪光灯 previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 显示预览 CaptureRequest previewRequest = previewRequestBuilder.build(); mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Toast.makeText(MainActivity.this, "配置失败", Toast.LENGTH_SHORT).show(); } }, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
6,预览完成后,接下来就是拍照了,注意某些手机的摄像头会和正常手机的摄像头另类,成像会成180度的倒立像,比如nexus 5x,这时候只需要设置
rORIENTATIONS的值来调整角度就可以
/** * 拍照 */ private void takePicture() { if (mCameraDevice == null) return; // 创建拍照需要的CaptureRequest.Builder final CaptureRequest.Builder captureRequestBuilder; try { captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 将imageReader的surface作为CaptureRequest.Builder的目标 captureRequestBuilder.addTarget(mImageReader.getSurface()); // 自动对焦 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); // 获取手机方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根据设备方向计算设置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)+rORIENTATIONS); //拍照 CaptureRequest mCaptureRequest = captureRequestBuilder.build(); mCameraCaptureSession.capture(mCaptureRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
由于最近需要使用相机拍照等功能,鉴于老旧的相机API问题多多,而且新的设备都是基于安卓5.0以上的,于是本人决定研究一下安卓5.0新引入的Camera2 API 来实现 Camera2API地址
首先我们来熟悉一下官方给的这几个图:
这里引用了管道的概念将安卓设备和摄像头之间联通起来,系统向摄像头发送
Capture 请求,而摄像头会返回 CameraMetadata。这一切建立在一个叫作 CameraCaptureSession 的会话中。
其中 CameraManager 是那个站在高处统管所有摄像投设备(CameraDevice)的管理者,而每个 CameraDevice 自己会负责建立 CameraCaptureSession 以及建立 CaptureRequest。CameraCharacteristics 是 CameraDevice 的属性描述类,非要做个对比的话,那么它与原来的 CameraInfo 有相似性。 类图中有着三个重要的 callback,虽然这增加了阅读代码的难度,但是你必须要习惯,因为这是新包的风格。其中 CameraCaptureSession.CaptureCallback 将处理预览和拍照图片的工作,需要重点对待。这些类是如何相互配合的?下面是简单的流程图。
有了这三张图,那么接下来就好了理解了,我们按照拍照流程的指示,来吧整个过程走一遍
1首先定义一个SufaceView 用来实现预览照片用
surfaceView=(SurfaceView) findViewById(R.id.surfaceView);
surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { initCamera(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { } });
其中initCamera2()方法是用来初始化相机的方法
2获取Camera ID,该ID是用来打开相机的关键,一般后置摄像头是0,前置摄像头是1,这里我们选择后置摄像头做详解
mCameraID = "" + CameraCharacteristics.LENS_FACING_BACK;//后摄像头
3通过Camera ID 来打开摄像头,这里我们需要使用CamerManager,这是类是一个管理服务类,值得注意的是,打开摄像头是一个相当复杂的过程,不能直接在主线程中直接执行,其核心代码为:
HandlerThread handlerThread=new HandlerThread("Camera2"); handlerThread.start(); childHandler=new Handler(handlerThread.getLooper()); mainHandler=new Handler(getMainLooper());
mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { //可以在这里处理拍照得到的临时照片 例如,写入本地 @Override public void onImageAvailable(ImageReader reader) { mCameraDevice.close(); surfaceView.setVisibility(View.GONE); iv_show.setVisibility(View.VISIBLE); // 拿到拍照照片数据 Image image = reader.acquireNextImage(); ByteBuffer buffer = image.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes);//由缓冲区存入字节数组 final Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); if (bitmap != null) { iv_show.setImageBitmap(bitmap); } } }, mainHandler);
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } //打开摄像头 cameraManager.openCamera(mCameraID, stateCallback, mainHandler); } catch (CameraAccessException e) { e.printStackTrace(); }注意打开摄像头需要权限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
4 开启相机后有一个回调,stateCallback,该回调是用来返回相机是否正常打开的状态的
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) {//打开摄像头 mCameraDevice = camera; //开启预览 takePreview(); } @Override public void onDisconnected(CameraDevice camera) {//关闭摄像头 if (null != mCameraDevice) { mCameraDevice.close(); MainActivity.this.mCameraDevice = null; } } @Override public void onError(CameraDevice camera, int error) {//发生错误 Toast.makeText(MainActivity.this, "摄像头开启失败", Toast.LENGTH_SHORT).show(); } };
5 相机开启成功后,执行回调中的onOpen方法,在该方法中,我们实现让图像显示在界面上
/** * 开始预览 */ private void takePreview() { try { // 创建预览需要的CaptureRequest.Builder final CaptureRequest.Builder previewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // 将SurfaceView的surface作为CaptureRequest.Builder的目标 previewRequestBuilder.addTarget(surfaceHolder.getSurface()); // 创建CameraCaptureSession,该对象负责管理处理预览请求和拍照请求 mCameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(), mImageReader.getSurface()), new CameraCaptureSession.StateCallback() // ③ { @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { if (null == mCameraDevice) return; // 当摄像头已经准备好时,开始显示预览 mCameraCaptureSession = cameraCaptureSession; try { // 自动对焦 previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 打开闪光灯 previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); // 显示预览 CaptureRequest previewRequest = previewRequestBuilder.build(); mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { Toast.makeText(MainActivity.this, "配置失败", Toast.LENGTH_SHORT).show(); } }, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
6,预览完成后,接下来就是拍照了,注意某些手机的摄像头会和正常手机的摄像头另类,成像会成180度的倒立像,比如nexus 5x,这时候只需要设置
rORIENTATIONS的值来调整角度就可以
/** * 拍照 */ private void takePicture() { if (mCameraDevice == null) return; // 创建拍照需要的CaptureRequest.Builder final CaptureRequest.Builder captureRequestBuilder; try { captureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 将imageReader的surface作为CaptureRequest.Builder的目标 captureRequestBuilder.addTarget(mImageReader.getSurface()); // 自动对焦 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); // 获取手机方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根据设备方向计算设置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)+rORIENTATIONS); //拍照 CaptureRequest mCaptureRequest = captureRequestBuilder.build(); mCameraCaptureSession.capture(mCaptureRequest, null, childHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }