使用SurfaceTexture作为Camera输出「第一章,Android音视频编码那点破事」

第一章,使用SurfaceTexture作为Camera输出


  在Android系统中,使用GPU对摄像头画面进行高效可控的渲染,几乎是必须的。说到GPU就不得不提OpenGL,一组GPU暴露给应用层使用的接口。

  • Tip:OpenGL是一组基于状态的系统,在这里没有对象,只有一系列的状态。包括申请的Texture、FBO和PBO都是以状态的形式存在的。当我们向系统申请一个Texture,系统不会直接给你返回一个Texture对象,而是一个编号,FBO和PBO也是一样的。

CameraPreviewPresenter.png

CameraPreviewPresenter.png


UML类图

UML类图

  正式开始之前我们先看一下这个模块的脑图和类图,以便对结构有个大概的了解。

  • CamerPreviewPresenter:序章介绍过了,一个全局控制器
  • Parameter:很重要的一个data类,包含了Camera、Encoder和Muxer初始化的一系列参数,再这里只用到Camera部分,也就是fps、previewWidth和previewHeight。
  • CameraWrapper:一个Camera的包装,对外暴露了SurfaceTexture.OnFrameAvailableListener接口,以及EGL,SurfaceTexture环境
  • CamerTextureWraper:EGL和SurfaceTexture的封装,其中textureId是OpenGL的纹理id,前面提到OpenGL是一个基于状态的系统,对SurfaceTexture的一系列处理都得通过textureId

  首先,CameraPreviewPresenter需要实现SurfaceTexture.OnFrameAvailableListener接口,并且持有一个Parameter和一个CameraWrapper。cameraWrapper 会在init中初始化,parameter则是在CameraPreviewPresenter的调用处传进来。

class CameraPreviewPresenter(var parameter: Parameter,
                             private var cameraWrapper: CameraWrapper? = null)
    : SurfaceTexture.OnFrameAvailableListener{

    init {
        cameraWrapper = CameraWrapper.open(parameter, this)
    }
}

  接下来在CameraWrapper里面,我们同样看到了Parameter,这个类会贯穿整个结构层级。onFrameAvailableListener也是由外部调用者赋值进来的,也就是CameraPreviewPresenter。

  由于Camera的初始化是一个耗时任务,所以需要在线程中初始化,这里使用了一个HandlerThread。熟悉HandlerThread的人应该知道,HandlerThread会和一个Handler关联起来,通过Handler给Thread发送消息,从而实现子线程中的消息消费,具体的效果就是可以让线程以规定的顺序间歇性的处理我们的任务,而无须关心线程的等待问题。

class CameraWrapper(private var parameter: Parameter,
                    private var onFrameAvailableListener: SurfaceTexture.OnFrameAvailableListener,
                    var textureWrapper: TextureWrapper = CameraTextureWrapper()) {
    private var mHandlerThread = HandlerThread("Renderer_Thread")
    private var mHandler: Handler? = null
    private var mCamera: Camera? = null
    private var mCameras = 0
    private var mCameraIndex = Camera.CameraInfo.CAMERA_FACING_BACK
}
  1. 在CameraWrapper初始化时获取摄像头数量,并初始化开启HandlerThread线程,之后向HandlerThread发送准备消息,在子线程中准备Camera的初始化。
    init {
        mCameras = CameraHelper.getNumberOfCameras()
        initThread()
        mHandler?.removeMessages(PREPARE)
        mHandler?.sendEmptyMessage(PREPARE)
    }
  1. Handler比较简单,只包含了一个Camera初始化的状态。代码就比较长了,在prepare()中,主要任务就是根据Parameter中的cameraIndex参数来打开前置或者后置摄像头,然后确定Camera的PreviewSize(预览分辨率)、ColorFormat(颜色格式)、FocusMode(对焦模式)、Fps(预览帧率)以及一些情景模式等。
    private fun initThread() {
        mHandlerThread.start()
        mHandler = object : Handler(mHandlerThread.looper) {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    PREPARE -> {
                        prepare()
                    }
                }
            }
        }
    }

    private fun prepare() {
        if (0 == mCameras) {
            debug_e("Unavailable camera")
            return
        }
        //如果没有前置摄像头,则强制使用后置摄像头
        if (parameter.cameraIndex == Camera.CameraInfo.CAMERA_FACING_FRONT && mCameras < 2)
            parameter.cameraIndex = Camera.CameraInfo.CAMERA_FACING_BACK
        mCameraIndex = parameter.cameraIndex

        val time = System.currentTimeMillis()
        mCamera = openCamera(mCameraIndex)
        debug_e("open time: ${System.currentTimeMillis() - time}")
        if (null == mCamera) return
        val cameraParam = mCamera!!.parameters
        CameraHelper.setPreviewSize(cameraParam, parameter)
        CameraHelper.setColorFormat(cameraParam, parameter)
        CameraHelper.setFocusMode(cameraParam, parameter)
        CameraHelper.setFps(cameraParam, parameter)
        CameraHelper.setAutoExposureLock(cameraParam, false)
        CameraHelper.setSceneMode(cameraParam, Camera.Parameters.SCENE_MODE_AUTO)
        CameraHelper.setFlashMode(cameraParam, Camera.Parameters.FLASH_MODE_OFF)
        CameraHelper.setAntibanding(cameraParam, Camera.
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值