【声 明】
首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。
其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。
最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。
码字不易,转载请注明出处!
教程代码:【Github传送门】 |
---|
目录
一、Android音视频硬解码篇:
二、使用OpenGL渲染视频画面篇
- 1,初步了解OpenGL ES
- 2,使用OpenGL渲染视频画面
- 3,OpenGL渲染多视频,实现画中画
- 4,深入了解OpenGL之EGL
- 5,OpenGL FBO数据缓冲区
- 6,Android音视频硬编码:生成一个MP4
三、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