音视频:使用 Camera API 获取 NV21 数据流

以前学习了图片和音频,此次咱们尝试使用 Android Camera API 获取到视频数据。git

简介

关于 Camera2 API

此次使用的 API 是 Camera2Camera2 是 Google 在 Android L 以后推出的全新的相机 API。Camera2 支持的功能要比 Camera 丰富不少,可是相应的,也增长了 API 的使用难度。github

流程图

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

NV21 是什么?

NV21YUV420p 的一种存储模式。存储顺序是先存 Y,再存 U,而后再 VU 交替存储。session

那么问题来了,YUV 是啥?ide

这里简要介绍下,后续能够专门一篇文章来介绍,固然你也能够在网上寻找其余资料来了解这个。学习

YUV 是啥

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

如何使用 Camera2?

此次咱们关注的是获取视频数据,因此对于相机相关的一些东西不会涉及。编码

主要的类

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

此次咱们要获取视频数据,还有一个类很重要:spa

  • ImageReader:能够从 Surface 直接接收渲染的数据。须要注意的是,它并非专为 Camera 设计的。

使用流程

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

  1. 初始化相机
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,
           2
       )
       mImageReader.setOnImageAvailableListener(ImageReaderAvailableListenerImp(), mBackgroundHandler)
       mCameraId = cameraId
       return true
   }复制代码

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

  1. 调用 openCamera()
  2. 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
       )
   }复制代码

  1. Session 建立完成后,在 onConfigured 回调中,发送请求。
override fun onConfigured(session: CameraCaptureSession) {
       Log.d(TAG, "onConfigured")
       session.setRepeatingRequest(
           mPreviewRequestBuilder.build(),
           object : CameraCaptureSession.CaptureCallback() {},
           mBackgroundHandler
       )
   }复制代码

  1. 最后,因为咱们使用了 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

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

相关文章
相关标签/搜索