相关文章推荐
憨厚的枇杷  ·  C# ...·  1 年前    · 
苦闷的领带  ·  Users Unable to Log ...·  1 年前    · 
愤怒的消炎药  ·  office web ...·  1 年前    · 
曾经爱过的西装  ·  目标检测脚本 | ...·  1 年前    · 

关于 Camera2 API

这次使用的 API 是 Camera2 Camera2 是 Google 在 Android L 之后推出的全新的相机 API。 Camera2 支持的功能要比 Camera 丰富很多,但是相应的,也增加了 API 的使用难度。

这是使用 Camera2 打开相机获取预览数据的流程图:

NV21 是什么?

NV21 YUV420p 的一种存储模式。存储顺序是先存 Y ,再存 U ,然后再 VU 交替存储。

那么问题来了, YUV 是啥?

这里简要介绍下,后续可以专门一篇文章来介绍,当然你也可以在网上寻找其他资料来了解这个。

YUV 是啥

YUV 是一种颜色编码方法,主要应用于电视系统和模拟视频领域。其中 YUV 代表三个分量, Y 代表 明亮度 U V 表示的是 色度

如何使用 Camera2?

这次我们关注的是 获取视频数据 ,所以对于相机相关的一些东西不会涉及。

  • CameraManager :摄像头的管理类。
  • CameraCharacteristics :用于描述特定摄像头所支持的特性。
  • CameraDevice :代表摄像头。
  • CameraCaptureSession :相机实际的控制端,我们需要在相机上做什么操作,都是由这个类发出相应的指令。
  • CameraRequest :每次发起捕获请求的时候都需要传递这个对象,这个类代表了一次捕获请求,用于描述捕获的各种参数。

    这次我们要获取视频数据,还有一个类很重要:

  • ImageReader :可以从 Surface 直接接收渲染的数据。需要注意的是,它并不是专为 Camera 设计的。

    接下来用简洁的代码描述下如何使用 Camera2 API:

  • 初始化相机
       private fun configCamera(cameraManager: CameraManager, cameraId: String): Boolean {
           val cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId)
           val facing = cameraCharacteristics[LENS_FACING]
           if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
               // 不使用前置摄像头
               return false
           val streamConfigurationMap =
               (cameraCharacteristics[CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP]
                   ?: return false)
           mImageReader = ImageReader.newInstance(
               mSurfaceView.width,
               mSurfaceView.height,
               ImageFormat.YUV_420_888,
           mImageReader.setOnImageAvailableListener(ImageReaderAvailableListenerImp(), mBackgroundHandler)
           mCameraId = cameraId
           return true
    

    这里是获取目标相机的 ID,还有初始化 ImageReader

  • 调用 openCamera()
  • openCamera 之后在 onOpened 回调中初始化 Session
       private fun createCameraPreviewSession(camera: CameraDevice) {
           mPreviewRequestBuilder = camera.createCaptureRequest(TEMPLATE_PREVIEW)
           mPreviewRequestBuilder.addTarget(mSurfaceView.holder.surface)
           mPreviewRequestBuilder.addTarget(mImageReader.surface)
           camera.createCaptureSession(
               listOf(
                   mSurfaceView.holder.surface,
                   mImageReader.surface
               ), mCameraSessionStateCallback, mBackgroundHandler
    
  • Session 创建完成后,在 onConfigured 回调中,发送请求。
       override fun onConfigured(session: CameraCaptureSession) {
           Log.d(TAG, "onConfigured")
           session.setRepeatingRequest(
               mPreviewRequestBuilder.build(),
               object : CameraCaptureSession.CaptureCallback() {},
               mBackgroundHandler
    
  • 最后,由于我们使用了 ImageReader,所以会在 onImageAvailable 回调中收到图像的回传。
       override fun onImageAvailable(reader: ImageReader) {
           val image = reader.acquireNextImage()
           if (image.format == ImageFormat.YUV_420_888) {
               val planes = image.planes
               lock.lock()
               if (!::y.isInitialized) {
                   y = ByteArray(planes[0].buffer.limit() - planes[0].buffer.position())
                   u = ByteArray(planes[1].buffer.limit() - planes[1].buffer.position())
                   v = ByteArray(planes[2].buffer.limit() - planes[2].buffer.position())
               if (planes[0].buffer.remaining() == y.size) {
                   planes[0].buffer.get(y)
                   planes[1].buffer.get(u)
                   planes[2].buffer.get(v)
                   // 接下来通过转换,可以转换为 Bitmap 进行展示
               lock.unlock()
           image.close()
    

    这部分的完整代码可以在仓库Camera2Helper 类中看到。

    代码还是要有的,可以在 GitHub 仓库中找到:https://github.com/T-Oner/MediaPractice

    最新更新请关注微信公众号

    放牛崽 @自营小店
    粉丝
  •