利用Android Camera2 的照相机api 实现 实时的图像采集与预览

https://blog.csdn.net/DavidWillo/article/details/63688319

  最近想要做一个客户端往服务器推送实时画面的功能,首先可以考虑到两种思路,一种是在客户端进行视频流的推送,主要利用RTSP等流媒体协议进行传输,而另外一种是通过摄像头获取当前画面,将每一帧作为对象单独传输。

   项目想要实现的功能最终目的是对实时画面的每一帧进行处理,可以考虑客户端推流到服务器,再在服务器进行帧解析的操作,但由于目前很多的流媒体推送框架在推流端或者服务端都或多或少存在限制,很少有完全开源的项目,再加上传送画面的同时需要附带部分的数据,仍然需要另外建立连接进行传输,所以暂时搁置这一方案。选择第二种思路,获取每一帧的画面,单独传输。

   要想获取实时画面,我们必须通过对安卓设备上的摄像头进行调用。

   从API21开始,安卓引入了android.hardware.camera2这个包,来替代原有的camera类,原有的camera类已经不再建议使用了。camera2中最重要的变化是,摄像头的调用不再是简单地进行实例化,而是用一种类似服务申请的方式来进行调用。通过CameraManager来管理摄像服务,需要通过建立CameraCaptureSession来建立一个调用摄像设备CameraDevices的会话,来实现对摄像头的调用。而CaptureRequest.Builder类用于建立实际的调用请求,具体的参数设置也可以通过这个类来实现(而不是对camera设备进行直接设置),这样做的目的是把对摄像头的控制与摄像头本身分离开来,用户可以通过不同的session根据不同的配置来使用摄像头。

   我们可以结合具体的代码来分析新api中摄像头调用的过程。

   首先我们想要对摄像设备进行操作,需要获得CameraManager的实例

[java]  view plain  copy
  1.     CameraManager cameraManager;  
  2.     cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);  

   我们可以调用openCamera函数打开摄像头设备

[java]  view plain  copy
  1.     cameraManager.openCamera(cameraId, cameraCallback, mainHandler);  

   这里需要传入三个参数,cameraId是设备编号,cameraCallback控制摄像服务的回调,最后一个参数指定HandlerThread对象 

[java]  view plain  copy
  1.      cameraId = Integer.toString(CameraCharacteristics.LENS_FACING_FRONT);  
  2.        
  3.      private CameraDevice.StateCallback cameraCallback = new CameraDevice.StateCallback() {  
  4.         @Override  
  5.         public void onOpened(CameraDevice camera) {  
  6.             Log.d("CameraCallback""Camera Opened");  
  7.             cameraDevice = camera;  
  8.             takePreview();  
  9.         }  
  10.   
  11.         @Override  
  12.         public void onDisconnected(CameraDevice cameraDevice) {  
  13.             Log.d("CameraCallback""Camera Disconnected");  
  14.             closeCameraDevice();  
  15.         }  
  16.   
  17.         @Override  
  18.         public void onError(CameraDevice cameraDevice, int i) {  
  19.             Log.d("CameraCallback""Camera Error");  
  20.             Toast.makeText(PusherSurface.this"摄像头开启失败", Toast.LENGTH_SHORT).show();  
  21.         }  
  22.     };  

 回调函数用于指定连接摄像头设备时不同状态的操作。在这里,我们在摄像头成功连接的时候调用  takePreview()函数开启摄像头画面的预览。

[java]  view plain  copy
  1. private void takePreview() {  
  2.     try {  
  3.         final CaptureRequest.Builder previewRequestBuilder  
  4.                 = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);  
  5.         previewRequestBuilder.addTarget(surfaceHolder.getSurface());  
  6.         previewRequestBuilder.addTarget(previewReader.getSurface());  
  7.         cameraDevice.createCaptureSession(Arrays.asList(surfaceHolder.getSurface(),  
  8.                 previewReader.getSurface(),  
  9.                 imageReader.getSurface()), new CameraCaptureSession.StateCallback() {  
  10.             @Override  
  11.             public void onConfigured(CameraCaptureSession cameraCaptureSession) {  
  12.                 if (cameraDevice == nullreturn;  
  13.                 mCameraCaptureSession = cameraCaptureSession;  
  14.   
  15.                 try {  
  16.                     previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,  
  17.                             CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);  
  18.                     previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE,  
  19.                             CaptureRequest.CONTROL_AE_MODE_OFF);  
  20.   
  21.                     CaptureRequest previewRequest = previewRequestBuilder.build();  
  22.                     mCameraCaptureSession.setRepeatingRequest(previewRequest, null, childHandler);  
  23.                 } catch (CameraAccessException e) {  
  24.                     e.printStackTrace();  
  25.                 }  
  26.             }  
  27.   
  28.             @Override  
  29.             public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {  
  30.                 Toast.makeText(PusherSurface.this"配置失败", Toast.LENGTH_SHORT).show();  
  31.             }  
  32.         }, childHandler);  
  33.     } catch (CameraAccessException e) {  
  34.         e.printStackTrace();  
  35.     }  
  36. }  

要从摄像设备中获取图像,我们首先需要建立一个camera capture session。函数

createCaptureSession(List, CameraCaptureSession.StateCallback, Handler)的第一个参数传入了我们想要绘制的视图列表,第二个参数传入的是建立摄像会话的状态回调函数,第三个参数传入相应的handler处理器。然后,我们需要利用capturerequest来定义摄像头捕获图像时候的具体参数,比如是否开启摄像头,是否自动对焦等。最后通过CamraCaptureSession.setRepeatingRequest来开启请求。这样我们就可以从capturesession传入的list中的surface列表获得连续的图像。留意到

[java]  view plain  copy
  1. previewRequestBuilder.addTarget(surfaceHolder.getSurface());  
  2. previewRequestBuilder.addTarget(previewReader.getSurface());  

这里除了传入xml界面布局中的surfaceHolder的surface外,还传入了一个previewReader的surface。

previewReader是一个自定义的ImageReader对象。

[java]  view plain  copy
  1. previewReader = ImageReader.newInstance(10801920, ImageFormat.YUV_420_888, 2);  
  2.         previewReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {  
  3.             @Override  
  4.             public void onImageAvailable(ImageReader imageReader) {  
  5.                 Image image = null;  
  6.                 try {  
  7.                     image = imageReader.acquireLatestImage();  
  8.                     Log.d("PreviewListener""GetPreviewImage");  
  9.                     if (image == null) {  
  10.                         return;  
  11.                     }  
  12.                     byte[] bytes = ImageUtil.imageToByteArray(image);  
  13.                     if (pushFlag == false)  
  14.                         uploadImg(bytes);  
  15.                 } finally {  
  16.                     if (image != null) {  
  17.                         image.close();  
  18.                     }  
  19.                 }  
  20.   
  21.             }  
  22.         }, mainHandler);  

ImageReader是一个可以让我们对绘制到surface的图像进行直接操作的类。在这里我们从摄像设备中传入了连续的预览图片,也就是我们在屏幕上看到的画面,它们的格式都是未经压缩的YUV_420_888类型的(同样的如果要操作拍摄后的图片,就要设置成jpeg格式)。我们调用imageReader.acquireLatestImage或者acquireNextImage来获取图像队列中的图片。并进行操作。在这里我利用一个函数将图像压缩后转化成byte[]格式,并调用uploadImg函数上传至服务器。这样,整个摄像头的调用到预览图像的处理也就完成了。想要实现拍照功能也是大同小异,在这里我就不一一贴出了。

  欢迎更多安卓开发者一同交流。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Android Camera2 APIAndroid系统提供的一种新的相机框架,用于实现相机预览采集功能。Camera2 API可以提供更强大、更灵活的相机控制和图像处理能力。 相比于传统的Camera APICamera2 API具有以下优势: 1. 能够同时管理多个相机设备:Camera2 API支持同时管理多个相机设备,包括前置摄像头、后置摄像头、深度摄像头等,可以方便地进行相机切换和管理。 2. 提供更灵活的相机控制:Camera2 API提供了更丰富的相机控制选项,可以精确地设置曝光时间、ISO值、焦距等参数,以便于用户根据不同的拍摄场景进行调整。 3. 支持原生的相机预览采集Camera2 API提供了对原生的预览采集数据的支持,可以更高效地获取图像数据,用户可以利用这些数据进行实时图像处理或者保存。 4. 支持原生的相机回调:Camera2 API提供了原生的相机回调接口,用户可以通过设置回调函数来实时获取相机的状态和图像数据,从而实现一些特殊的相机功能,如实时人脸检测、连拍等。 总之,Android Camera2 API提供了更强大、更灵活的相机功能,可以满足开发者对相机预览采集的各种需求。无论是在智能手机、平板电脑还是其他移动设备上,都可以通过Camera2 API实现高质量的相机应用。 ### 回答2: Android Camera 2是Android系统用于处理相机功能的全新API。相较于之前的Camera APICamera 2更加强大和灵活,提供了更多的功能和优化。 在Android Camera 2预览采集是通过创建一个CameraDevice实例来实现的。首先,需要获取相机的相关信息,例如相机的ID、参数等。然后,通过调用CameraManager的openCamera方法来打开指定ID的相机。 在相机打开后,可以通过创建一个CaptureRequest.Builder对象来配置相机的预览设置。可以设置预览的目标Surface、预览图像的尺寸、率等。然后,通过调用createCaptureSession方法,传入预览的目标Surface和一个CameraCaptureSession.StateCallback来创建一个预览会话。 在预览会话创建后,可以调用setRepeatingRequest方法来开始预览Camera2会自动将预览数据传输到指定的Surface上,并在Surface上更新预览图像。同时,可以通过设置回调函数来处理预览数据,例如实时显示预览图像、进行进一步的图像处理等。 在预览过程,还可以根据需要调整相机的参数,例如调整曝光度、焦距、白平衡等。通过创建新的CaptureRequest.Builder对象并设置相应参数,然后调用Session的setRepeatingRequest方法,可以实现动态调整相机参数。 总而言之,Android Camera 2提供了更强大和灵活的方式来实现相机预览采集。通过合理地使用Camera2的API,我们可以轻松地实现各种功能,例如实时预览图像处理、动态调整参数等。 ### 回答3: Android Camera2预览采集是通过使用Android操作系统的Camera2 API实现的一种方式。Camera2 APIAndroid 5.0(Lollipop)版本引入的相机框架,它提供了更多的功能和灵活性,用于控制和管理Android设备上的相机。 使用Camera2 API进行预览采集是一种相对较新和高级的方法,相比传统的Camera APICamera2 API提供了更多的控制选项和功能,以及更好的性能和稳定性。 通过Camera2 API,我们可以实时地从Android设备的摄像头获取图像,并将图像传输到屏幕上进行实时预览预览采集可以在应用程序使用,例如用于视频通话、拍照、视频录制等场景。 Camera2 API预览采集过程主要涉及以下步骤: 1. 获取相机设备:通过CameraManager类获取设备的摄像头列表,并选择要使用的摄像头设备。 2. 创建相机会话:使用CameraDevice类进行连接和建立与相机设备的会话。 3. 创建预览请求:使用CaptureRequest.Builder类创建一个预览请求,并设置相应的参数,例如预览尺寸、率等。 4. 创建预览会话:使用CameraDevice类创建预览会话,并将预览请求设置为预览会话的目标。 5. 开启预览:将预览会话设置为活动状态,相机将开始实时地捕获图像,并通过指定的Surface进行预览。 通过这些步骤,我们可以在Android设备上实现相机预览采集功能,并根据需要进行自定义设置和扩展。预览采集可以进一步应用于更多的相机应用场景,例如人脸识别、图像处理等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值