【Android 音视频开发打怪升级:OpenGL渲染视频画面篇】四、深入了解OpenGL之EGL

【声 明】首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。码字不易,转载请注明出处!教程代码:【Github传送门】目录一、Android音视频硬解码篇:1...
摘要由CSDN通过智能技术生成

【声 明】

首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。
其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。
最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。

码字不易,转载请注明出处!

教程代码:【Github传送门
目录
一、Android音视频硬解码篇:
二、使用OpenGL渲染视频画面篇
三、Android FFmpeg音视频解码篇
  • 1,FFmpeg so库编译
  • 2,Android 引入FFmpeg
  • 3,Android FFmpeg视频解码播放
  • 4,Android FFmpeg+OpenSL ES音频解码播放
  • 5,Android FFmpeg+OpenGL ES播放视频
  • 6,Android FFmpeg简单合成MP4:视屏解封与重新封装
  • 7,Android FFmpeg视频编码

本文你可以了解到

EGL作为OpenGL与本地窗口渲染的中间桥梁,很多时候是不会被刚入门OpenGL的开发者关注的,甚至有点忽略了。随着学习的深入,EGL将是不得不面对的东西。本文将介绍EGL是什么,有什么用,以及如何使用EGL。

一、EGL是什么

作为Android开发者,EGL仿佛是一个很陌生的东西,为什么?

都怪Android的GLSurfaceView封装的太好了。哈哈哈

1,为什么onDrawFrame会不断的回调呢?

前面的文章就介绍过,OpenGL是基于线程的,直到目前为止,我们并没有深刻的认识到这个问题,但我们知道的是,当我们继承GLSurfaceView.Renderer时,系统会回调以下方法:

override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
   
}

override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
   
}

override fun onDrawFrame(gl: GL10?) {
   
}

并且onDrawerFrame方法是会被不断的调用,我们就是在这里面实现了OpenGL的绘制流程。

这里我们就可以猜测,能够不断被调用的,有没有可能就是一个while循环的线程呢?

答案是:Yes。

如果你去看一下GLSurfaceView的源码,你会找到一个叫GLThread的线程,在线程中就初始化了EGL相关的内容。并且在合适的时机,分别调用了Renderer中的三个方法。

那么,EGL究竟是个什么东西呢?

2,EGL是个啥?

我们知道OpenGL是一组可以操作GPU的API,然而仅仅能够操作GPU,并不能够将图像渲染到设备的显示窗口上。那么,就需要一个中间层,连接OpenGL与设备窗口,并且最好是跨平台的。

于是EGL出现了,由Khronos Group提供的一组平台无关的API。

3,EGL的一些基础知识
  • EGLDisplay

EGL定义的一个抽象的系统显示类,用于操作设备窗口。

  • EGLConfig

EGL配置,如rgba位数

  • EGLSurface

渲染缓存,一块内存空间,所有要渲染到屏幕上的图像数据,都要先缓存在EGLSurface上。

  • EGLContext

OpenGL上下文,用于存储OpenGL的绘制状态信息、数据。

初始化EGL的过程其实就是配置以上几个信息的过程。

二、如何使用EGL

单单看上面的介绍,其实还是比较难理解EGL究竟有什么作用,或者应该怎么样去使用EGL。


请大家先思考一个问题

如果同时有两个GLSurfaceView在渲染视频画面,OpenGL为什么能够正确的把画面分别绘制到两个GLSurfaceView中?

仔细回想一下OpenGL ES的每个API,有没有哪个API是指定当前画面是渲染到哪个GLSurfaceView的?

没有!


请带着这个疑问,阅读下面的内容。

1,封装EGL核心API

首先,对EGL初始化的核心(第一节中介绍的4个)内容进行封装,命名为 EGLCore

const val FLAG_RECORDABLE = 0x01

private const val EGL_RECORDABLE_ANDROID = 0x3142

class EGLCore {
   

    private val TAG = "EGLCore"

    // EGL相关变量
    private var mEGLDisplay: EGLDisplay = EGL14.EGL_NO_DISPLAY
    private var mEGLContext = EGL14.EGL_NO_CONTEXT
    private var mEGLConfig: EGLConfig? = null

    /**
     * 初始化EGLDisplay
     * @param eglContext 共享上下文
     */
    fun init(eglContext: EGLContext?, flags: Int) {
   
        if (mEGLDisplay !== EGL14.EGL_NO_DISPLAY) {
   
            throw RuntimeException("EGL already set up")
        }

        val sharedContext = eglContext ?: EGL14.EGL_NO_CONTEXT

        // 1,创建 EGLDisplay
        mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
        if (mEGLDisplay === EGL14.EGL_NO_DISPLAY) {
   
            throw RuntimeException("Unable to get EGL14 display")
        }

        // 2,初始化 EGLDisplay
        val version = IntArray(2)
        if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
   
            mEGLDisplay = EGL14.EGL_NO_DISPLAY
            throw RuntimeException("unable to initialize EGL14")
        }

        // 3,初始化EGLConfig,EGLContext上下文
        if (mEGLContext === EGL14.EGL_NO_CONTEXT) {
   
            val config = getConfig(flags, 2) ?: throw RuntimeException("Unable to find a suitable EGLConfig")
            val attr2List = intArrayOf(EGL14.EGL_CONTEXT_CLIENT_VERSION, 2, EGL14.EGL_NONE)
            val context = EGL14.eglCreateContext(
                mEGLDisplay, config, sharedContext,
                attr2List, 0
            )
            mEGLConfig = config
            mEGLContext = context
        }
    }

    /**
     * 获取EGL配置信息
     * @param flags 初始化标记
     * @param version EGL版本
     */
    private fun getConfig(flags: Int, version: Int): EGLConfig? {
   
        var renderableType = EGL14.EGL_OPENGL_ES2_BIT
        if (version >= 3) {
   
            // 配置EGL 3
            renderableType = renderableType or EGLExt.EGL_OPENGL_ES3_BIT_KHR
        }

        // 配置数组,主要是配置RAGA位数和深度位数
        // 两个为一对,前面是key,后面是value
        // 数组必须以EGL14.EGL_NONE结尾
        val attrList = intArrayOf(
            EGL14.EGL_RED_SIZE, 8,
            EGL14.EGL_GREEN_SIZE, 8,
            EGL14.EGL_BLUE_SIZE, 8,
            EGL14.EGL_ALPHA_SIZE, 8,
            //EGL14.EGL_DEPTH_SIZE, 16,
            //EGL14.EGL_STENCIL_SIZE, 8,
            EGL14.EGL_RENDERABLE_TYPE, renderableType,
            EGL14.EGL_NONE, 0, // placeholder for recordable [@-3]
            EGL14.EGL_NONE
        )
        //配置Android指定的标记
        if (flags and FLAG_RECORDABLE != 0) {
   
            attrList[attrList.size - 3] = EGL_RECORDABLE_ANDROID
            attrList[attrList.size - 2] = 1
        }
        val configs = arrayOfNulls<EGLConfig>(1)
        val numConfigs = IntArray(1)

        //获取可用的EGL配置列表
        if (!EGL14.eglChooseConfig(mEGLDisplay, attrList, 0,
                configs, 0, configs.size
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

开发的猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值