音視頻:使用 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

最新更新請關注微信公衆號

相關文章
相關標籤/搜索