1 class PlayRenderer(
2 private var context: Context,
3 private var glSurfaceView: GLSurfaceView
4) : GLSurfaceView.Renderer,
5 VideoRender.OnNotifyFrameUpdateListener, MediaPlayer.OnPreparedListener,
6 MediaPlayer.OnVideoSizeChangedListener, MediaPlayer.OnCompletionListener,
7 MediaPlayer.OnErrorListener {
8 companion object {
9 private const val TAG = "PlayRenderer"
10 }
11 private lateinit var videoRender: VideoRender
12 private lateinit var mediaPlayer: MediaPlayer
13 private val projectionMatrix = FloatArray(16)
14 private val viewMatrix = FloatArray(16)
15 private val vPMatrix = FloatArray(16)
16 // 用于视频比例的计算,详见下文
17 private var screenWidth: Int = -1
18 private var screenHeight: Int = -1
19 private var videoWidth: Int = -1
20 private var videoHeight: Int = -1
22 override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
23 L.i(TAG, "onSurfaceCreated")
24 GLES20.glClearColor(0f, 0f, 0f, 0f)
25 videoRender = VideoRender(context)
26 videoRender.setTextureID(TextureHelper.createTextureId())
27 videoRender.onNotifyFrameUpdateListener = this
28 initMediaPlayer()
29 }
31 override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
32 L.i(TAG, "onSurfaceChanged > width:$width,height:$height")
33 screenWidth = width
34 screenHeight = height
35 GLES20.glViewport(0, 0, width, height)
36 }
38 override fun onDrawFrame(gl: GL10) {
39 L.i(TAG, "onDrawFrame")
40 gl.glClear(GL10.GL_COLOR_BUFFER_BIT or GL10.GL_DEPTH_BUFFER_BIT)
41 videoRender.draw(vPMatrix)
42 }
44 override fun onPrepared(mp: MediaPlayer?) {
45 L.i(OpenGLActivity.TAG, "onPrepared")
46 mediaPlayer.start()
47 }
49 override fun onVideoSizeChanged(mp: MediaPlayer?, width: Int, height: Int) {
50 L.i(OpenGLActivity.TAG, "onVideoSizeChanged > width:$width ,height:$height")
51 this.videoWidth = width
52 this.videoHeight = height
53 }
55 override fun onCompletion(mp: MediaPlayer?) {
56 L.i(OpenGLActivity.TAG, "onCompletion")
57 }
59 override fun onError(mp: MediaPlayer?, what: Int, extra: Int): Boolean {
60 L.i(OpenGLActivity.TAG, "error > what:$what,extra:$extra")
61 return true
62 }
64 private fun initMediaPlayer() {
65 mediaPlayer = MediaPlayer()
66 mediaPlayer.setOnPreparedListener(this)
67 mediaPlayer.setOnVideoSizeChangedListener(this)
68 mediaPlayer.setOnCompletionListener(this)
69 mediaPlayer.setOnErrorListener(this)
70 mediaPlayer.setDataSource(Environment.getExternalStorageDirectory().absolutePath + "/video.mp4")
71 mediaPlayer.setSurface(videoRender.getSurface())
72 mediaPlayer.prepareAsync()
73 }
74 // 通知请求渲染
75 override fun onNotifyUpdate() {
76 glSurfaceView.requestRender()
77 }
79 fun destroy() {
80 mediaPlayer.stop()
81 mediaPlayer.release()
82 }
上述代码中的VideoRender主要是渲染操作,这部分代码和上篇文章中的大同小异,这里就不贴了。使用 OpenGL ES 进行使用视频渲染的时候,需调用SurfaceTetre的updateTexImage方法更新图像帧,该方法必须在 OpenGL ES 上下文中使用,可以设置GLSurfaceView的渲染模式为RENDERMODE_WHEN_DIRTY避免一直绘制,当onFrameAvailable会调的时候,也就是有了可用的数据之后再进行requestRender以减少必要的消耗。
上面视频是全屏播放的,但是屏幕分辨率和视频分辨率不一样,导致视频画面被拉升,这就需要根据屏幕分辨率和视频分辨率的大小来计算合适视频画面大小,并且基本适配了三角形的变形,这里视频也是一样,它相当于矩形。投影主要有正交投影和透视投影,正交投影一般用于渲染 2D 画面,比如普通视频的渲染,透视投影有近大远小的特点,一般用于 3D 画面渲染,比如 VR 的渲染,所以这里使用正交投影的方式来矫正画面。先看下Shader的修改,主要是顶点顶点着色器的变化,如下:
屏幕向外为 z 轴,相机位置(0, 0, 5)表示相机在距离屏幕为 5 的位置,即 z 轴方向,该值必须在视景体的 near 和 far 之间,否则是看不见的,比如该案例中该值应该在 3~5 之间,目标位置(0, 0, 0)表示的就是屏幕,也就是 x 和 y 轴构成的平面,相机正上方向量(0, 1, 0)表示沿着 y 轴正方向,最后是计算投影和视图变化,如下通过矩阵乘法将projectionMatrix和viewMatrix合并为vPMatrix: