借助Android的高性能视频渲染方案
为什么不用Unity自带的视频播放器:要播4K片源,自带的播放器性能不够看
为什么不用AVPro:不支持当前项目视频加解密方案
Android端
1.SurfaceTexture
1 2 3 4 5 6 7 8 9 10 11 12 13
| if (m_iSurfaceTextureID == -1) { int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); m_iSurfaceTextureID = textures[0]; GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, m_iSurfaceTextureID); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_NEAREST); GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR); }
m_SurfaceTexture = new SurfaceTexture(m_iSurfaceTextureID); m_SurfaceTexture.setOnFrameAvailableListener(this); m_Surface = new Surface(m_SurfaceTexture);
|
- SurfaceTexture的作用是从 Image Stream 中捕获帧数据,视频流可以是来自相机也可以来自视频解码
- 这里用到GL_TEXTURE_EXTERNAL_OES(外部 GLES 纹理),与GL_TEXTURE_2D(传统 GLES 纹理)不同,并在Unity的shader中声明samplerExternalOES类型
2.更新视频帧
1 2 3 4
| public synchronized void UpdateVideoTexture() { m_SurfaceTexture.updateTexImage(); }
|
updateTexImage调用时SurfaceTexture对象所绑定的GLES纹理的内容被更新为Image Stream中最新的一帧图像(必须显式地调用,将数据更新到GLES纹理对象后SurfaceTexture才有空间获取下一帧数据,否则下一帧数据永远不会交给SurfaceTexture),后边Unity中会显式调用该方法推动渲染不断进行
Unity端
1.获取textureID并基于外部创建的原生纹理创建一个Unity纹理
1 2
| int nativeTex = GetJavaObject().Call<int>("GetSurfaceTextureID"); m_VideoTexture = Texture2D.CreateExternalTexture(w, h, TextureFormat.RGBA32, false, true, new IntPtr(nativeTex));
|
2.更新视频帧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| for(int i = 0 { MeshRenderer renderer = m_TargetMaterial[i].GetComponent<MeshRenderer>() Texture tempTex = renderer.sharedMaterial.GetTexture(texName) if(tempTex != m_VideoTexture) { renderer.sharedMaterial.SetTexture(texName, m_VideoTexture) } }
GetJavaObject().Call("UpdateVideoTexture")
float[] mat = GetJavaObject().Call<float[]>("GetSurfaceMatrix") Matrix4x4 matx = ConvertFloatArrayToMatrix(mat)
for(int i = 0 { MeshRenderer renderer = m_TargetMaterial[i].GetComponent<MeshRenderer>() renderer.sharedMaterial.SetMatrix(videoMatrixPropertyId, matx) }
|
- 获取MeshRenderer组件并将刚才创建的纹理赋给他
- 显示调用Android方法更新视频帧
总结:安卓端用到 ExoPlayer2 进行视频解码并输出视频流,同时更新视频帧到纹理,提供给Unity端最终显示在材质球上完成视频播放。