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

Stella981
• 阅读 767

这里涉及到了摄像头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 数据的排列格式的数据。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Karen110 Karen110
3年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Android视频直播流(三) YUV 数据的存储,以及播放
上一章写了YUV数据的获取,这里写写怎么把YUV数据保存起来,以及播放吧。因为YUV数据,都是从camera的回调里拿到的,所以不可以对回调进行阻塞,所以最好的处理方式就是:定义一个队列,在camera的回调里只管往队列里塞数据,在外部,用一个线程,死循环,利用队列的特性:有
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这