camera2 学习笔记

     在camera2中相机被被拆分为四个部分:相机管理器cameraManger,相机设备cameraDevice,相机拍照会话cameraCaptureSession,图片读取器ImageRead。

在Camera2中相机的调用流程较为复杂,下面用代码讲解来演示处理流程。

1、TextureView初始化完毕,触发SurfaceTextureListener接口的onSurfaceTextureAvailable(),他会接着调用cameraManager的cameraManager.openCamera(下一步骤)。

// 定义一个表面纹理变更监听器。TextureView准备就绪后,立即开启相机
private SurfaceTextureListener mSurfacetextlistener = new SurfaceTextureListener() {
  // 在纹理表面可用时触发
  public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
    ###############在这里触发下一步
    openCamera(); // 打开相机
  }
  // 在纹理表面销毁时触发
  public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
    closeCamera(); // 关闭相机
    return true;
  }
};

2、openCamera()方法获取CamreaManager对象,获取合适的预览尺寸(之后使用)。设置图像读取器,并绑定监听器,在接收到图象时保存图像。该接口的实现代码这里不再给出,它的功能是将图片保存为jpg格式。

调用了cameraManager.openCamera(cameraid, mDeviceStateCallback, mHandler)。他的第二个参数在相机打开后执行回调函数(下一步骤)。

// 打开相机
private void openCamera() {
  // 从系统服务中获取相机管理器
  CameraManager cm = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
  String cameraid = mCameraType + "";
  try {
    // 获取可用相机设备列表
    CameraCharacteristics cc = cm.getCameraCharacteristics(cameraid);
    StreamConfigurationMap map = cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    Size largest = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizeByArea());
    // 获取预览画面的尺寸
    mPreViewSize = map.getOutputSizes(SurfaceTexture.class)[0];
    // 创建一个JPEG格式的图像读取器
    mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), ImageFormat.JPEG, 10);
    // 设置图像读取器的图像可用监听器,一旦捕捉到图像数据就会触发监听器的onImageAvailable方法
    mImageReader.setOnImageAvailableListener(onImageAvaiableListener, mHandler);
    if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
      ///#######触发下一步
      // 开启摄像头
      cm.openCamera(cameraid, mDeviceStateCallback, mHandler);
    }
  } catch (CameraAccessException e) {
    e.printStackTrace();
  }
}

3、接下来我们看看回调函数,重点是他的onOpened()方法在相机打开后执行。在这里绑定变量CameraDevice,并继续调用函数createCameraPreviewSession()创建拍照会话(下一步骤)。

// 相机准备就绪后,开启捕捉影像的会话
private CameraDevice.StateCallback mDeviceStateCallback = new CameraDevice.StateCallback() {
  @Override
  public void onOpened(CameraDevice cameraDevice) {
    //绑定变量并创建会话
    mCameraDevice = cameraDevice;
    createCameraPreviewSession();
  }

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

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

4、下面是创建拍照会话的函数,这里创建了拍摄请求的创建器mPreviewBuilder(这个创建器之后用来连拍功能)。下面调用函数mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()), mSessionStateCallback, mHandler);这个拍摄会话决定了一组潜在的输出表面(surfaceTexture用来预览拍摄界面,mImageReader.getSurface()用于保存照片)  。mSessionStateCallback在会话构架完成后调用(下一步骤)。


    // 创建相机预览会话
    private void createCameraPreviewSession() {
        // 获取纹理视图的表面纹理
        SurfaceTexture texture = getSurfaceTexture();
        // 设置表面纹理的默认缓存尺寸
        texture.setDefaultBufferSize(mPreViewSize.getWidth(), mPreViewSize.getHeight());
        // 创建一个该表面纹理的表面对象
        Surface surface = new Surface(texture);
        try {
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            // 把纹理视图添加到预览目标
            mPreviewBuilder.addTarget(surface);
            // 设置自动对焦模式
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            // 设置自动曝光模式
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            // 开始对焦
            mPreviewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_START);
            // 设置照片的方向
            mPreviewBuilder.set(CaptureRequest.JPEG_ORIENTATION, (mCameraType == CameraCharacteristics.LENS_FACING_FRONT) ? 90 : 270);
            // ##从这里进入下一步,创建一个相片捕获会话。此时预览画面显示在纹理视图上
            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                    mSessionStateCallback, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

我在这里有一个疑惑之处,对于自动对焦功能。API有如下描述(可能是我理解有问题,请帮我解答)

Generally, applications should set this entry to START or CANCEL for only a single capture, and then return it to IDLE (or not set at all). Specifying START for multiple captures in a row means restarting the AF operation over and over again.我理解为不应该对连拍功能设置该属性,以防止每次拍照出现重复对焦。不过代码此处的mPreviewBuilder确实在之后调用了.setRepeatingRequest()实现连拍功能。

// 把图像读取器添加到预览目标
mPreviewBuilder.addTarget(mImageReader.getSurface());
// 设置连拍请求。此时预览画面会同时发给手机屏幕和图像读取器
mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);

5、这一步内启动连拍进行预览,将画面交给TextureSurface预览。

// 影像配置就绪后,将预览画面呈现到手机屏幕上
private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() {
  @Override
  public void onConfigured(CameraCaptureSession session) {
    try {
      Log.d(TAG, "onConfigured");
      mCameraSession = session;
      // 设置连拍请求。此时预览画面只会发给手机屏幕
      mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
    } catch (CameraAccessException e) {
      e.printStackTrace();
    }
  }

  @Override
  public void onConfigureFailed(CameraCaptureSession session) {}
};

6、所有的初始化工作完成,之后我们可以进行拍照。(本质上是构建目标为mImageReader.getSurface()的request请求。)之后mImageReader会处理接收到的图片(已经在第二步中设置的图像读取器),并进行保存。

拍摄单张代码

// 执行拍照动作
public void takePicture() {
  Log.d(TAG, "正在拍照");
  mTakeType = 0;
  try {
    //#####构建一个新的请求构建器,因此需要重新设置属性
    CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    ///######这一步设置目标为图片读取器
    // 把图像读取器添加到预览目标
    builder.addTarget(mImageReader.getSurface());
    // 设置自动对焦模式
    builder.set(CaptureRequest.CONTROL_AF_MODE,
                CaptureRequest.CONTROL_AF_MODE_AUTO);
    // 设置自动曝光模式
    builder.set(CaptureRequest.CONTROL_AF_MODE,
                CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
    // 开始对焦
    builder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                CameraMetadata.CONTROL_AF_TRIGGER_START);
    // 设置照片的方向
    builder.set(CaptureRequest.JPEG_ORIENTATION, (mCameraType == CameraCharacteristics.LENS_FACING_FRONT) ? 90 : 270);
    // 拍照会话开始捕获相片
    mCameraSession.capture(builder.build(), null, mHandler);
  } catch (CameraAccessException e) {
    e.printStackTrace();
  }
}

连拍功能,与拍单张的区别是,没有新建请求构造器,只是将图片读取器增加到Target中。并设置定时任务,一段时候后复原之前设置。

    // 开始连拍
    public void startShooting(int duration) {
        Log.d(TAG, "正在连拍");
        mTakeType = 1;
        mShootingArray = new ArrayList<String>();
        try {
            // 停止连拍
            mCameraSession.stopRepeating();
            // 把图像读取器添加到预览目标
            mPreviewBuilder.addTarget(mImageReader.getSurface());
            // 设置连拍请求。此时预览画面会同时发给手机屏幕和图像读取器
            mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
            // duration小等于0时,表示持续连拍,此时外部要调用stopShooting方法来结束连拍
            if (duration > 0) {
                // 延迟若干秒后启动拍摄停止任务
                mHandler.postDelayed(mStop, duration);
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    // 停止连拍
    public void stopShooting() {
        try {
            // 停止连拍
            mCameraSession.stopRepeating();
            // 移除图像读取器的预览目标
            mPreviewBuilder.removeTarget(mImageReader.getSurface());
            // 设置连拍请求。此时预览画面只会发给手机屏幕
            mCameraSession.setRepeatingRequest(mPreviewBuilder.build(), null, mHandler);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
        Toast.makeText(mContext, "已完成连拍,按返回键回到上页查看照片。", Toast.LENGTH_SHORT).show();
    }

    // 定义一个拍摄停止任务
    private Runnable mStop = new Runnable() {
        @Override
        public void run() {
            stopShooting();
        }
    };

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值