Android视频直播流(二)Android摄像头YUV数据的获取

这里涉及到了摄像头Camera的使用,和对YUV数据的获取。

这里有一些东西需要格外注意,就是编码格式的选择,以及对宽高的设置

我这里自定义了一个CmeraView 因为摄像头的使用有点复杂,我索性就封装起来,这里一定要注意,宽(prewWidth )高(prewHeight )的设置,很有用,很有用。

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
    }
}

其实关键代码就是这句:

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

这里拿到的数据,就是刚才设置的 宽*高以及对 UV 数据的排列格式的数据。

猜你喜欢

转载自my.oschina.net/xiaolei123/blog/1786787
今日推荐