运用Camera2 和 SurfaceView 实现实时视频帧处理

因为工作需要需要用到相机API,就去瞅了瞅。Camera基本上已经都被弃用,写完满屏幕的灰色横线。就琢磨着用Camera2来实现该有的功能。

这是官方的camera2 demo 基本上该有的功能,都在demo中实现

https://github.com/googlearchive/android-Camera2Basic

首先开发过自定义相机的都知道,如果不特殊处理,会出现拉伸的情况,这是因为SurfaceView的宽高比,和相机输出的图像宽高比不同,所以会出现图像拉伸的情况。
根据官方给出的处理方案,就是自定义一个SurfaceVIew根据相机输出的图像宽高比,来制定自身的宽高,代码如下:

	private var aspectRatio = 0f

    fun setAspectRatio(width: Int, height: Int) {
    
    
        require(width > 0 && height > 0) {
    
     "Size cannot be negative" }
        aspectRatio = width.toFloat() / height.toFloat()
        holder.setFixedSize(width, height)
        requestLayout()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val width = MeasureSpec.getSize(widthMeasureSpec)
        val height = MeasureSpec.getSize(heightMeasureSpec)
        if (aspectRatio == 0f) {
    
    
            setMeasuredDimension(width, height)
        } else {
    
    
            val newWidth: Int
            val newHeight: Int
            val actualRatio = if (width > height) aspectRatio else 1f / aspectRatio
            if (width < height * actualRatio) {
    
    
                newHeight = height
                newWidth = (height * actualRatio).roundToInt()
            } else {
    
    
                newWidth = width
                newHeight = (width / actualRatio).roundToInt()
            }
            setMeasuredDimension(newWidth, newHeight)
        }
    }

在surface监听中,拿到相机的previewSize,调用setAspectRatio方法即可
代码如下

surfaceView.holder.addCallback(object : SurfaceHolder.Callback {
    
    
            override fun surfaceDestroyed(holder: SurfaceHolder) = Unit

            override fun surfaceChanged(
                holder: SurfaceHolder,
                format: Int,
                width: Int,
                height: Int
            ) = Unit

            override fun surfaceCreated(holder: SurfaceHolder) {
    
    
                val previewSize = getPreviewOutputSize(
                    surfaceView.display, characteristics, SurfaceHolder::class.java
                )
                surfaceView.setAspectRatio(previewSize.width, previewSize.height)

                view.post {
    
     initializeCamera() }
            }
        })

因为Camera2中没有像Camera中一样有一个setPreviewCallback方法,可以对每一帧进行处理。但是Camera2中有ImageReader对象,可以进行我们需要的处理
在上述代码中 initializeCamera中如下代码

 camera = openCamera(cameraManager, FONT_CAMERA, cameraHandler)
  val size = characteristics.get(
           CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP
        )!!
            .getOutputSizes(ImageFormat.YUV_420_888).maxBy {
    
     it.height * it.width }!!
        currentSize = size
        imageReader = ImageReader.newInstance(
            size.width, size.height, ImageFormat.YUV_420_888, IMAGE_BUFFER_SIZE
        )    imageReader.setOnImageAvailableListener(mOnImageAvailableListener, null)
        val targets = listOf(surfaeView.holder.surface, imageReader.surface)
        session = createCaptureSession(camera, targets, cameraHandler)

        val captureRequest = camera.createCaptureRequest(
            CameraDevice.TEMPLATE_PREVIEW
        ).apply {
    
    
            addTarget(viewFinder.holder.surface)
            addTarget(imageReader.surface)
        }

        session.setRepeatingRequest(captureRequest.build(), null, cameraHandler)

其中具体openCamera或characteristics如何获取等基本方法信息,很简单就不放出来了。

然后关键是Imagereader设置的mOnImageAvailableListener
咱们可以自定义一个监听 代码如下

private val mOnImageAvailableListener: OnImageAvailableListener =
        object : OnImageAvailableListener {
    
    
            override fun onImageAvailable(reader: ImageReader) {
    
    
          
                val image = reader.acquireLatestImage() ?: return
				//子线程中进行处理
                imageReaderHandler.post {
    
    
					//todo  此处可以对每一帧的image进行处理

                	//不要忘记关闭close
                 	image.close()
                }

            }
        }

其实不怎么难,一开始不太愿意看官方的demo。但是看了其实一目了然。Camera2跟Camera还是差了很多的。难吧,其实也还好。但是也不容易。仔细看看demo就能理解了。
希望有帮助吧

猜你喜欢

转载自blog.csdn.net/adminyuqiao/article/details/108643066