Unity&Android纹理共享

借助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; i < m_TargetMaterial.Length; i++)
{
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; i < m_TargetMaterial.Length; i++)
{
MeshRenderer renderer = m_TargetMaterial[i].GetComponent<MeshRenderer>();
renderer.sharedMaterial.SetMatrix(videoMatrixPropertyId, matx);
}
  • 获取MeshRenderer组件并将刚才创建的纹理赋给他
  • 显示调用Android方法更新视频帧
总结:安卓端用到 ExoPlayer2 进行视频解码并输出视频流,同时更新视频帧到纹理,提供给Unity端最终显示在材质球上完成视频播放。

Unity&Android纹理共享
https://baifabaiquan.cn/2022/10/27/Unity&Android纹理共享/
作者
白发败犬
发布于
2022年10月27日
许可协议