Android live video stream (2) Acquisition of Android camera YUV data

This involves the use of the camera and the acquisition of YUV data.

There are some things that need special attention here, that is, the choice of encoding format, and the setting of width and height

I have customized a CmeraView here because the use of the camera is a bit complicated, so I simply encapsulated it. It must be noted here that the settings of width (prewWidth) and height (prewHeight) are very useful and useful.

import android.app.Activity
import android.content.Context
import android.graphics.ImageFormat
import android.hardware.*
import android.util.AttributeSet
import android.view.SurfaceHolder
import android.view.SurfaceView
import android.view.Surface


/**
 * Created by xiaolei on 2018/3/26.
 */

class CameraView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : SurfaceView(context, attrs, defStyleAttr), SurfaceHolder.Callback
{
    private var cameraId = 0
    private val camera = Camera.open(cameraId)
    private var preViewBlock: ((ByteArray, Camera) -> Unit)? = null
    var prewWidth = 640
    var prewHeight = 480
    
    init
    {
        holder.addCallback(this)
        holder.setKeepScreenOn(true)
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS)
        camera.setPreviewCallback { data, camera ->
            preViewBlock?.invoke(data, camera)
        }
        val params = camera.parameters
        val supportSizeList = params.supportedPreviewSizes // 获取摄像头支持的分辨率宽高
        println(supportSizeList.size)
        supportSizeList.forEach { size ->
            if (size.width == prewWidth && size.height == prewHeight)
            {
                params.setPreviewSize(prewWidth, prewHeight)
            }
        }
        params.previewFormat = ImageFormat.NV21 // 设置摄像头输出的格式为 YUV420下 的 NV21
        camera.parameters = params
    }

    override fun surfaceCreated(holder: SurfaceHolder?)
    {
        try
        {
            camera.setPreviewDisplay(holder)
            camera.startPreview()
        } catch (e: Exception)
        {
            e.printStackTrace()
        }
    }

    override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int)
    {
        // If your preview can change or rotate, take care of those events here.
        // 如果您的预览可以更改或旋转,请在这里处理这些事件。
        // Make sure to stop the preview before resizing or reformatting it.
        // 在调整或重新格式化之前,务必停止预览。
        getHolder() ?: let {
            return
        }
        // stop preview before making changes
        // 更改前停止预览。
        try
        {
            camera.startPreview()
        } catch (e: Exception)
        {
            e.printStackTrace()
        }
        // set preview size and make any resize, rotate or reformatting changes here
        // 设置预览大小,并在这里进行任何调整、旋转或重新格式化。
        // start preview with new settings
        // 使用新的设置开始预览。
        try
        {
            camera.setPreviewDisplay(getHolder())
            camera.startPreview()
        } catch (e: Exception)
        {
            e.printStackTrace()
        }

    }

    override fun surfaceDestroyed(holder: SurfaceHolder?)
    {
        // empty. Take care of releasing the Camera preview in your activity.
        // 空的。注意在你的活动中发布相机预览。
        camera.setPreviewCallback(null)
        camera.stopPreview()
        camera.release()
    }

    /**
     * 设置自动旋转
     */
    fun setAutoRotation(activity: Activity)
    {
        val rotation = activity.windowManager.defaultDisplay.rotation
        val info = Camera.CameraInfo()
        Camera.getCameraInfo(cameraId, info)
        val degrees = when (rotation)
        {
            Surface.ROTATION_0 -> 0
            Surface.ROTATION_90 -> 90
            Surface.ROTATION_180 -> 180
            Surface.ROTATION_270 -> 270
            else -> 0
        }
        val result = if (info.facing === Camera.CameraInfo.CAMERA_FACING_FRONT)
        {
            val r = (info.orientation + degrees) % 360
            (360 - r) % 360
        } else
        {
            (info.orientation - degrees + 360) % 360
        }
        camera.setDisplayOrientation(result)
    }

    /**
     * 设置预览数据的回调
     */
    fun onPreviewCallback(block: (ByteArray, Camera) -> Unit)
    {
        this.preViewBlock = block
    }

    fun autoFocus()
    {
        camera.autoFocus { success, camera -> }
    }



    /**
     * 把 YUV 的界面旋转90度,使得预览正常,
     * 但是会把,宽 高 旋转
     */
    fun yuv_rotate90(src: ByteArray, width: Int, height: Int): ByteArray
    {
        val des = ByteArray(src.size)
        val wh = width * height
        //旋转Y
        var k = 0
        for (i in 0 until width)
        {
            for (j in height - 1 downTo 0)
            {
                des[k++] = src[j * width + i]
            }
        }
        //旋转UV
        val uvHeight = height shr 1
        val uvWidth = width shr 1
        val uvWH = uvHeight * uvWidth
        var i = 0
        while (i < width)
        {
            for (j in uvHeight - 1 downTo 0)
            {
                des[k] = src[wh + width * j + i]
                des[k + 1] = src[wh + width * j + i + 1]
                k += 2
            }
            i += 2
        }
        return des
    }
}

In fact, the key code is this sentence:

camera.setPreviewCallback { data, camera ->
            preViewBlock?.invoke(data, camera)
}

The data obtained here is the data of the width*height just set and the arrangement format of the UV data.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324444564&siteId=291194637