前一篇介绍是渲染一张图片,今天是在MediaPlayer播放过程中,渲染视频,看下Agenda:

  • 与渲染图片的区别

  • 创建SurfaceTexture

  • 设置shader(着色器)

  • 建立纹理坐标 

    • UV坐标介绍

    • UV纹理坐标设定与贴图规则是什么?

  • 视频播放

与渲染图片的区别

渲染视频画面和渲染图片不同,视频需要不断地刷新,每当有新的一帧来时,我们都应该更新纹理,然后重新绘制。我们使用SurfaceTexture来设置MediaPlayer的setSurface.

创建一个纹理时,视频的每一帧都可以看成图片,也就是要不断的更新纹理

主要的原因是,MediaPlayer的输出往往不是RGB格式(一般是YUV),而GLSurfaceView需要RGB格式才能正常显示,另外,获取每一帧的数据并没有那么方便。 
所以,我们创建的纹理应该稍有不同,SurfaceTexture在《Android Multimedia框架总结(三)MediaPlayer中创建到setDataSource过程》就曾详细介绍过,这里贴出来:

SurfaceTexture: SurfaceTexture是从Android3.0(API 11)加入的一个新类。这个类跟SurfaceView很像,可以从video decode里面获取图像流(image stream)。但是,和SurfaceView不同的是,SurfaceTexture在接收图像流之后,不需要显示出来。SurfaceTexture不需要显示到屏幕上,因此我们可以用SurfaceTexture接收来自decode出来的图像流,然后从SurfaceTexture中取得图像帧的拷贝进行处理,处理完毕后再送给另一个SurfaceView用于显示即可。

创建SurfaceTexture

  1. public VideoTexture(Context context, int textureId) {

  2.       mContext = context;

  3.       mTexureId = textureId;

  4.       mSurfaceTexture = new SurfaceTexture(textureId);

  5.       mSurface = new Surface(mSurfaceTexture);

  6.       // 初始化顶点坐标与着色数据      

  7.       initVertexData();

  8.       // 初始化着色器    

  9.       initShader();

  10. }

在onDrawFrame中

  1. mSurfaceTexture.updateTexImage();

  2. checkGlError("onDrawFrame start");

  3. mSurfaceTexture.getTransformMatrix(mSTMatrix);

  4. GLES30.glUseProgram(mProgram);

  5. checkGlError("glUseProgram");

  6. GLES30.glActiveTexture(GLES30.GL_TEXTURE0);

  7. GLES30.glBindTexture(mTarget, mTexId);

其中mTarget是 GLES11Ext.GL_TEXTURE_EXTERNAL_OES,GLES11Ext.GL_TEXTURE_EXTERNAL_OES的到底是做什么的? 
我们知道视频解码的输出格式是YUV的(YUV420sp),那么这个扩展纹理的作用就是实现YUV格式到RGB的自动转化,我们就不需要再为此写YUV转RGB的代码。

我们用上面的textureId是构造时传入的,

  1. // 生成纹理ID

  2. int[] textures = new int[2];

  3. GLES20.glGenTextures(//

  4.    2, // 产生的纹理id的数量

  5.    textures, // 纹理id的数组

  6.    0 // 偏移量

  7. );

  8. mVideoTexture = new VideoTexture(mContext, textures[0]);

这个textureId是用于去创建一个SurfaceTexture,然后用surfaceTexture去创建一个Surface ,再把surface送给mediaPlayer进行输出。 
updateTexImage,就是来更新纹理,其中getTransformMatrix的目的,是让新的纹理和纹理坐标系能够正确的对应,mSTMatrix的定义是和mMVPMatrix完全一样的。

  1. private float[] mMVPMatrix = new float[16];

  2. private float[] mSTMatrix = new float[16];

设置shader(着色器):

  1. public void initShader() {

  2.        // 加载顶点着色器的脚本内容

  3.        String mVertexShader =

  4. ShaderUtil.readRawTextFile(mContext, R.raw.vertex_oes);

  5.        // 加载片元着色器的脚本内容

  6.        String mFragmentShader =

  7. ShaderUtil.readRawTextFile(mContext, R.raw.frag_oes);

  8.        // 基于顶点着色器与片元着色器创建程序

  9.        mProgram =

  10. ShaderUtil.createProgram(mVertexShader, mFragmentShader);

  11.        // 获取程序中顶点位置属性引用id

  12.        maPositionHandle =

  13. GLES30.glGetAttribLocation(mProgram, "aPosition");

  14.        // 获取程序中顶点纹理坐标属性引用id

  15.        maTextureCoordHandle =

  16. GLES30.glGetAttribLocation(mProgram,

  17.                "aTextureCoord");

  18.        // 获取程序中总变换矩阵引用id

  19.        muMVPMatrixHandle =

  20. GLES30.glGetUniformLocation(mProgram, "uMVPMatrix");

  21.        muSTMatrixHandle =

  22. GLES30.glGetUniformLocation(mProgram, "uSTMatrix");

  23. }

其中vextex_oes.glsl

  1. uniform mat4 uMVPMatrix; //总变换矩阵

  2. uniform mat4 uSTMatrix;

  3. attribute vec4 aPosition;  //顶点位置

  4. attribute vec4 aTextureCoord;    //顶点纹理坐标

  5. varying vec2 vTextureCoord;  //用于传递给片元着色器的变量

  6. void main()

  7. {

  8.   gl_Position = uMVPMatrix * aPosition;

  9.   //根据总变换矩阵计算此次绘制此顶点位置

  10.   vTextureCoord = (uSTMatrix * aTextureCoord).xy;

  11.   //将接收的纹理坐标传递给片元着色器

  12. }

frag_oes.glsl

  1. #extension GL_OES_EGL_image_external : require

  2. precision mediump float;

  3. varying vec2 vTextureCoord; //接收从顶点着色器过来的参数

  4. uniform samplerExternalOES sTexture;//纹理内容数据

  5. void main()

  6. {

  7.   //给此片元从纹理中采样出颜色值

  8.   gl_FragColor = texture2D(sTexture, vTextureCoord);

  9. }  

         

建立纹理坐标:

  1. private final float[] mTriangleVerticesData = {

  2.     // X, Y, Z, U, V

  3.     -3.2f, -1.5f, 0, 0.f, 0.f, //

  4.     3.2f, -1.5f, 0, 1.f, 0.f, //

  5.     -3.2f, 1.5f, 0, 0.f, 1.f, //

  6.     3.2f, 1.5f, 0, 1.f, 1.f, //

  7. };    

其中有X,Y,Z,还有U,V,那么什么是UV坐标?

对于三维模型,有两个最重要的坐标系统,一是顶点的位置(X,Y,Z)坐标,另一个就是UV坐标。什么是UV?简单的说,就是贴图影射到模型表面的依据。 完整的说,其实应该是UVW(因为XYZ已经用过了,所以另选三个字母表示)。U和V分别是图片在显示器水平、垂直方向上的坐标,取值一般都是0~1,也就是(水平方向的第U个像素/图片宽度,垂直方向的第V个像素/图片高度)。那W呢?贴图是二维的,何来三个坐标?嗯嗯,W的方向垂直于显示器表面,一般 用于程序贴图或者某些3D贴图技术(记住,确实有三维贴图这种概念!),对于游戏而言不常用到,所以一般我们就简称UV了。

所有的图象文件都是二维的一个平面。水平方向是U,垂直方向是V,通过这个平面的,二维的UV坐标系。我们可以定位图象上的任意一个象素。但是一个问题是如何把这个二维的平面贴到三维的NURBS表面和多边形表面呢? 对于NURBS表面。由于他本身具有UV参数,尽管这个UV值是用来定位表面上的点的参数,但由于它也是二维的,所以很容易通过换算把表面上的点和平面图象上的象素对应起来。所以把图象贴带NURBS是很直接的一件事。但是对于多变形模型来讲,贴图就变成一件麻烦的事了。所以多边形为了贴图就额外引进了一个UV坐标,以便把多边形的顶点和图象文件上的象素对应起来,这样才能在多边形表面上定位纹理贴图。所以说多边形的顶点除了具有三维的空间坐标外。还具有二维的UV坐标。

UV” 这里是指u,v纹理贴图坐标的简称(它和空间模型的X, Y, Z轴是类似的). 它定义了图片上每个点的位置的信息. 这些点与3D模型是相互联系的, 以决定表面纹理贴图的位置. UV就是将图像上每一个点精确对应到模型物体的表面. 在点与点之间的间隙位置由软件进行图像光滑插值处理. 这就是所谓的UV贴图. 
那为什么用UV坐标而不是标准的投影坐标呢? 通常给物体纹理贴图最标准的方法就是以planar(平面),cylindrical(圆柱), spherical(球形),cubic(方盒)坐标方式投影贴图. 
Planar projection(平面投影方式)是将图像沿x,y或z轴直接投影到物体. 这种方法使用于纸张, 布告, 书的封面等 - 也就是表面平整的物体.平面投影的缺点是如果表面不平整, 或者物体边缘弯曲, 就会产生如图A的不理想接缝和变形. 避免这种情况需要创建带有alpha通道的图像, 来掩盖临近的平面投影接缝, 而这会是非常烦琐的工作. 所以不要对有较大厚度的物体和不平整的表面运用平面投影方式. 对于立方体可以在x, y方向分别进行平面投影, 但是要注意边缘接缝的融合. 或者采用无缝连续的纹理, 并使用cubic投影方式. 多数软件有图片自动缩放功能, 使图像与表面吻合. 显然, 如果你的图像与表面形状不同, 自动缩放就会改变图像的比例以吻合表面. 这通常会产生不理想的效果, 所以制作贴图前先测量你的物体尺寸.

uv纹理坐标设定与贴图规则是什么?

当opengl对一个四方形进行贴图时,会定义纹理贴图坐标,一串数组。

当纹理映射启动后绘图时,你必须为OpenGL ES提供其他数据,即顶点数组中各顶点的纹理坐标。纹理坐标定义了图像的哪一部分将被映射到多边形。

视频播放

  1. try {

  2.    mMediaPlayer = new MediaPlayer();

  3.    mMediaPlayer.setSurface(mSurface);

  4.    mMediaPlayer.setLooping(true);

  5.    String url =

  6.    Environment.getExternalStorageDirectory() + "/节目.mp4";

  7.    Log.w("videoTexture", " datasource file url " + url);

  8.    mMediaPlayer.setDataSource(mContext, Uri.parse(url));

  9.    mMediaPlayer.prepare();

  10.    mMediaPlayer.start();

  11. } catch (Exception e) {

  12.      e.printStackTrace();

  13. }  

最后效果图:

OpenGL ES总结(四)OpenGL 渲染视频画面_JAVA

https://mp.weixin.qq.com/s/RD-iXuCT5V89hjn4jCJk0gOpenGL ES总结(四)OpenGL 渲染视频画面_JAVA_02