【声 明】
首先,这一系列文章均基于自己的理解和实践,可能有不对的地方,欢迎大家指正。
其次,这是一个入门系列,涉及的知识也仅限于够用,深入的知识网上也有许许多多的博文供大家学习了。
最后,写文章过程中,会借鉴参考其他人分享的文章,会在文章最后列出,感谢这些作者的分享。
码字不易,转载请注明出处!
教程代码:【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视频编码
本文你可以了解到
渲染多视频画面,是实现音视频编辑的基础,本文将介绍如何将多个视频画面渲染到OpenGL中,以及如何对画面进行混合、缩放、移动等。
写在前面
距离上次更新已经有两个星期,由于这段时间事情比较多,还请各位关注本系列文章的小伙伴见谅,一有时间我会加紧码字,感谢大家的关注和督促。
下面就来看看如何在OpenGL中渲染多视频画面。
一、渲染多画面
在上篇文章中,详细的讲解了如何通过OpenGL渲染视频画面,以及对视频画面进行比例矫正,基于前面系列文章中封装好的工具,可以非常容易地实现在OpenGL中渲染多个视频画面。
上文的OpenGL Render非常简单如下:
class SimpleRender(private val mDrawer: IDrawer): GLSurfaceView.Renderer {
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES20.glClearColor(0f, 0f, 0f, 0f)
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
mDrawer.setTextureID(OpenGLTools.createTextureIds(1)[0])
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
mDrawer.setWorldSize(width, height)
}
override fun onDrawFrame(gl: GL10?) {
mDrawer.draw()
}
}
只支持一个Drawer,这里改造一下,把Drawer修改为列表,以支持多个绘制器。
class SimpleRender: GLSurfaceView.Renderer {
private val drawers = mutableListOf<IDrawer>()
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
GLES20.glClearColor(0f, 0f, 0f, 0f)
val textureIds = OpenGLTools.createTextureIds(drawers.size)
for ((idx, drawer) in drawers.withIndex()) {
drawer.setTextureID(textureIds[idx])
}
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
for (drawer in drawers) {
drawer.setWorldSize(width, height)
}
}
override fun onDrawFrame(gl: GL10?) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT or GLES20.GL_DEPTH_BUFFER_BIT)
drawers.forEach {
it.draw()
}
}
fun addDrawer(drawer: IDrawer) {
drawers.add(drawer)
}
}
同样非常简单,
- 增加一个addDrawer方法,用来添加多个绘制器。
- 在onSurfaceCreated中为每个绘制器设置一个纹理ID。
- 在onSurfaceChanged中为每个绘制器设置显示区域宽高。
- 在onDrawFrame中,遍历所有绘制器,启动绘制。
接着,新建一个新页面,生成多个解码器和绘制器。
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">
<android.opengl.GLSurfaceView
android:id="@+id/gl_surface"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
class MultiOpenGLPlayerActivity: AppCompatActivity() {
private val path = Environment.getExternalStorageDirectory().absolutePath + "/mvtest.mp4"
private val path2 = Environment.getExternalStorageDirectory().absolutePath + "/mvtest_2.mp4"
private val render = SimpleRender()
private val threadPool = Executors.newFixedThreadPool(10)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_opengl_player)
initFirstVideo()
initSecondVideo()
initRender()
}
private fun initFirstVideo() {
val drawer = VideoDrawer()
drawer.setVideoSize(1920, 1080)
drawer.getSurfaceTexture {
initPlayer(path, Surface(it), true)
}
render.addDrawer(drawer)
}
private fun initSecondVideo() {
val drawer = VideoDrawer()
drawer.setVideoSize(1920, 1080)
drawer.getSurfaceTexture {
initPlayer(path2, Surface(it), false)
}
render.addDrawer(drawer)
}
private fun initPlayer(path: String, sf: Surface, withSound: Boolean) {
val videoDecoder = VideoDecoder(path, null, sf)
threadPool.execute(videoDecoder)
videoDecoder.goOn()
if (withSound) {
val audioDecoder = AudioDecoder(path)
threadPool.execute(audioDecoder)
audioDecoder.goOn()
}
}
private fun initRender() {
gl_surface.setEGLContextClientVersion(2)
gl_surface.setRenderer(render)
}
}
代码比较简单,通过之前封装好的解码工具和绘制工具,添加了两个视频画面的渲染。
当然了,你可以添加更多的画面到OpenGL中渲染。
并且,你应该发现了,渲染多个视