Android多媒体功能开发(14)——Camera2框架

从Android5.0开始,引入了一套Camera2框架控制相机实现拍照和录像等功能,相关的类定义在android.hardware.camera2包中。原有的android.hardware包中的Camera类降级使用,因为其功能少,灵活性差,满足不了日益复杂的相机功能需求。

Camera2框架的相机模型被设计成一个管道,使用相机时需要先和相机设备建立一个会话,通过该会话向相机发送请求,相机将图像数据保存到配置好的Surface,Surface就是存放图像数据的缓冲区。请求分为单次请求、重复请求和多次请求三种。例如,实现预览功能需要发送一个重复请求,相当于不断向相机发送预览请求,相机就会不断把预览图像数据存入预览组件的图像缓冲区。而实现拍照功能可以发送一个单次请求,相机会将图像数据存入一个缓冲区,应用再将图像数据保存到图片文件即可。这些请求是被放在一个队列中顺序执行的,因此不会发生两个请求同时执行的冲突。

这种管道模型的请求和响应都是异步的,所以Camera2框架大量采用回调方法。这样使得代码的执行不够线性,刚开始学习时不容易理解掌握。下面我们就具体介绍Camera2框架的使用方法,以及用到的概念和类。

一、相机权限和特性
为了在安装应用前就确认设备上有相机,需要在配置文件Manifest.xml中配置相机特性。这样,如果设备上没有相机就无法安装应用。使用相机必须在配置文件Manifest.xml中注册相机权限android.permission.CAMERA,声明应用需要相机权限。代码是:

<uses-feature android:name="android.hardware.camera" android:required="true"/>
<uses-permission android:name="android.permission.CAMERA" />

Android6.0以上系统需要应用运行时进行动态权限申请,所以在使用相机前需要检查权限,如果没有许可就进行询问。主要代码是:

if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
      ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.CAMERA}, 101);
      return;
}

还可以配置更多特性要求,例如必须有支持自动对焦的相机等。

二、开关相机
Camera2框架使用流程的起点是CameraManager,这是一个负责管理相机的系统服务,通过它来获取相机设备和信息。获取CameraManager的代码如下:

CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

接着需要获取相机列表,每个相机用一个String类型的id表示,后面通过id可以获取对应的相机设备。所有相机id的列表保存在一个String数组中,设备上有几个相机就在数组中几个元素。代码如下:

String[] cameraIdList = cameraManager.getCameraIdList();

接下来可以根据相机id获取相机特性,相机特性保存在一个CameraCharacteristics对象中。一项属性表示为一个键值对,所以该对象是一个键值对的集合。下面的代码通过遍历所有相机的特性找到后置相机的id,再通过CameraCharacteristics对象获取相机支持的拍照分辨率大小:

String backCameraId = null;
for(String cameraId:cameraIdList){
      CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId); 
      if(cameraCharacteristics.get(CameraCharacteristics.LENS_FACING)==CameraCharacteristics.LENS_FACING_BACK) {
                mCameraId = cameraId;
                supportedSizes = cameraCharacteristics .get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(ImageReader.class);
                break;
       }
}

接下来就可以调用 CameraManager.openCamera() 方法开启相机,代码如下: 

cameraManager.openCamera(mCameraId, stateCallback, mBackgroundHandler);

openCamera()方法有三个参数,第一个参数是cameraId,这里使用上一步获取到的后置相机的id。第二个参数是处理结果的回调对象,和事件处理机制中的监听器一样。openCamera()方法是异步执行的,调用以后方法马上返回,继续执行后续代码,打开相机是否成功的结果要过一段时间才返回,返回后调用回调对象的对应方法。第三个参数是执行回调方法的线程,可以是主线程,也可以是后台线程。回调对象需要在调用openCamera()方法之前创建,示例代码如下:

CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
      @Override
      public void onOpened(@NonNull CameraDevice cameraDevice) {
          mCameraDevice = cameraDevice;
          //  回调函数的代码在子线程中执行,所以不能直接发出Toast消息,只能通过主线程发出
          runOnUiThread(new Runnable() {
              @Override
              public void run() {
                  Toast.makeText(getBaseContext(), "Camera opened", Toast.LENGTH_SHORT).show();
              }
          });
      }

      @Override
      public void onDisconnected(@NonNull CameraDevice cameraDevice) {
          mCameraDevice = null;
      }

      @Override
      public void onError(@NonNull CameraDevice cameraDevice, int i) {
          cameraDevice.close();
          mCameraDevice = null;
      }

};

后台线程也需要在调用openCamera()方法之前调用,启动和停止后台线程的代码如下:

private void startBackgroundThread() {
    mBackgroundThread = new HandlerThread("CameraBackground");
    mBackgroundThread.start();
    mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}

private void stopBackgroundThread() {
    mBackgroundThread.quitSafely();
    try {
        mBackgroundThread.join();
        mBackgroundThread = null;
        mBackgroundHandler = null;
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

相机不再使用以后需要关闭,否则会一直占用相机资源,使得其他使用相机的应用无法使用。关闭相机比较合适的时机是在Activity的onPause()方法中。关闭相机的代码如下:

mCameraDevice.close();


三、创建CaptureSession会话
打开相机设备以后,要控制相机还要建立CaptureSession会话。代码如下:

mCameraDevice.createCaptureSession(surfaceList, sessionStateCallback, mBackgroundHandler);

mCameraDevice是上一步打开相机的结果回调中传回的相机对象,创建CameraSession会话需要调用它的createCaptureSession()方法,方法有三个参数。第一个参数是Surface列表,即用于接收图像数据的内存缓冲区,比如用于预览的Surface,用于拍照的Surface,这些Surface必须在这里准备好,否则后面预览或拍照时无法使用。创建预览和拍照两个Surface的列表的代码如下:

//    用Texture组件预览,其Surface在监听器中获得
TextureView  previewTexture = new TextureView(this);
previewTexture.setSurfaceTextureListener(new 
  • 11
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nanoage

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值