[OpenGL]从零开始写一个Android平台下的全景视频播放器——2.3 使用GLSurfaceView和MediaPlayer播放一个平面视频(下)
来源:互联网 发布:ubuntu镜像文件安装 编辑:程序博客网 时间:2024/06/05 16:51
Github项目地址,欢迎star~!
为了方便没有准备好梯子的同学,我把项目在CSDN上打包下载,不过更新会慢一些
回到目录
视频颠倒的问题
其实,不要更新mSTMatrix,将他设为单位阵,一般就会显示正常的视频。。
在使用mSTMatrix的情况下,解决方法就是修改顶点数组或者修改纹理数组,我们采用修改顶点数组的方案:
private final float[] vertexData = { 1f,-1f,0f, -1f,-1f,0f, 1f,1f,0f, -1f,1f,0f};
屏幕尺寸自适应
如何才能让显示出来的宽高比符合视频的宽高比呢? 答案依然是正交投影!
但是有一些不同,因为我们现在有两个尺寸,一个是屏幕的宽高,一个是视频的宽高,在之前显示图片的时候,我们直接采用了把图片去掉一部分,以适应”绘制一个正方形区域”的要求,那有没有更好的解决方案呢?
分析一下吧,其实无非就两种情况:
- 屏幕宽高比小于视频宽高比,如图:
- 屏幕宽高比大于视频宽高比,如图:
其中,蓝色的是我们的视频区域,蓝色坐标轴是OpenGL屏幕坐标轴,黄色的是纹理坐标轴,注意,我将所有的规范坐标都放到了视频的四个角落,这是因为我们要保证视频完全显示,不变形并且最大化的显示,而我们的纹理坐标和顶点坐标就是像这四个顶点描述的这样子。
这两种情况,只要搞定一种,另外一种也就很容易推导出来了。
获取视频尺寸
获取视频的尺寸可以设置一个监听器:
mediaPlayer.setOnVideoSizeChangedListener(this);@Overridepublic void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.d(TAG, "onVideoSizeChanged: "+width+" "+height); updateProjection(width,height);}
之前提过,屏幕尺寸是在onSurfaceCreated获取的。
有了这两个尺寸,我们就可以更新Projection矩阵了,代码可能有些难以理解,对于videoRatio>screenRatio,其实就是我们是将videoHeight/videoWidth
当做一个单位来处理(因为之前的坐标映射),然后我们用screenHeight/screenWidth
再去除这个单位,来获得屏幕Y轴方向应该有的范围
private void updateProjection(int videoWidth, int videoHeight){ float screenRatio=(float)screenWidth/screenHeight; float videoRatio=(float)videoWidth/videoHeight; if (videoRatio>screenRatio){ Matrix.orthoM(projectionMatrix,0,-1f,1f,-videoRatio/screenRatio,videoRatio/screenRatio,-1f,1f); }else Matrix.orthoM(projectionMatrix,0,-screenRatio/videoRatio,screenRatio/videoRatio,-1f,1f,-1f,1f);}
屏幕比例16:10,第一个视频比例4:3,第二个视频比例2:1
MediaPlayer和GLSurfaceView生命周期控制
首先,主活动的生命周期中和GLSurfaceView主要有两个,和MediaPlayer相关的有三个,我们会在后面做播放控制的时候再详细说明。
@Overrideprotected void onDestroy() { super.onDestroy(); glRenderer.getMediaPlayer().release();}@Overrideprotected void onPause() { super.onPause(); glSurfaceView.onPause(); glRenderer.getMediaPlayer().pause();}@Overrideprotected void onResume() { super.onResume(); glSurfaceView.onResume();}
顺便贴一下所有的代码 GLRenderer.java
package com.martin.ads.panoramaopengltutorial;import android.content.Context;import android.graphics.SurfaceTexture;import android.media.AudioManager;import android.media.MediaPlayer;import android.net.Uri;import android.opengl.GLES11Ext;import android.opengl.GLES20;import android.opengl.GLSurfaceView;import android.opengl.Matrix;import android.util.Log;import android.view.Surface;import java.io.IOException;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;import java.nio.ShortBuffer;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;/** * Created by Ads on 2016/11/13. */public class GLRenderer implements GLSurfaceView.Renderer , SurfaceTexture.OnFrameAvailableListener, MediaPlayer.OnVideoSizeChangedListener { private static final String TAG = "GLRenderer"; private Context context; private int aPositionHandle; private int programId; private FloatBuffer vertexBuffer; private final float[] vertexData = { 1f,-1f,0f, -1f,-1f,0f, 1f,1f,0f, -1f,1f,0f }; private final float[] projectionMatrix=new float[16]; private int uMatrixHandle; private final float[] textureVertexData = { 1f,0f, 0f,0f, 1f,1f, 0f,1f }; private FloatBuffer textureVertexBuffer; private int uTextureSamplerHandle; private int aTextureCoordHandle; private int textureId; private SurfaceTexture surfaceTexture; private MediaPlayer mediaPlayer; private float[] mSTMatrix = new float[16]; private int uSTMMatrixHandle; private boolean updateSurface; private boolean playerPrepared; private int screenWidth,screenHeight; public GLRenderer(Context context,String videoPath) { this.context = context; playerPrepared=false; synchronized(this) { updateSurface = false; } vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(vertexData); vertexBuffer.position(0); textureVertexBuffer = ByteBuffer.allocateDirect(textureVertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(textureVertexData); textureVertexBuffer.position(0); mediaPlayer=new MediaPlayer(); try{ mediaPlayer.setDataSource(context, Uri.parse(videoPath)); }catch (IOException e){ e.printStackTrace(); } mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setLooping(true); mediaPlayer.setOnVideoSizeChangedListener(this); } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { String vertexShader = ShaderUtils.readRawTextFile(context, R.raw.vertex_shader); String fragmentShader= ShaderUtils.readRawTextFile(context, R.raw.fragment_shader); programId=ShaderUtils.createProgram(vertexShader,fragmentShader); aPositionHandle= GLES20.glGetAttribLocation(programId,"aPosition"); uMatrixHandle=GLES20.glGetUniformLocation(programId,"uMatrix"); uSTMMatrixHandle = GLES20.glGetUniformLocation(programId, "uSTMatrix"); uTextureSamplerHandle=GLES20.glGetUniformLocation(programId,"sTexture"); aTextureCoordHandle=GLES20.glGetAttribLocation(programId,"aTexCoord"); int[] textures = new int[1]; GLES20.glGenTextures(1, textures, 0); textureId = textures[0]; GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId); ShaderUtils.checkGlError("glBindTexture mTextureID"); 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); surfaceTexture = new SurfaceTexture(textureId); surfaceTexture.setOnFrameAvailableListener(this); Surface surface = new Surface(surfaceTexture); mediaPlayer.setSurface(surface); surface.release(); if (!playerPrepared){ try { mediaPlayer.prepare(); playerPrepared=true; } catch (IOException t) { Log.e(TAG, "media player prepare failed"); } mediaPlayer.start(); playerPrepared=true; } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { Log.d(TAG, "onSurfaceChanged: "+width+" "+height); screenWidth=width; screenHeight=height; } @Override public void onDrawFrame(GL10 gl) { GLES20.glClear( GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); synchronized (this){ if (updateSurface){ surfaceTexture.updateTexImage(); surfaceTexture.getTransformMatrix(mSTMatrix); updateSurface = false; } } GLES20.glUseProgram(programId); GLES20.glUniformMatrix4fv(uMatrixHandle,1,false,projectionMatrix,0); GLES20.glUniformMatrix4fv(uSTMMatrixHandle, 1, false, mSTMatrix, 0); vertexBuffer.position(0); GLES20.glEnableVertexAttribArray(aPositionHandle); GLES20.glVertexAttribPointer(aPositionHandle, 3, GLES20.GL_FLOAT, false, 12, vertexBuffer); textureVertexBuffer.position(0); GLES20.glEnableVertexAttribArray(aTextureCoordHandle); GLES20.glVertexAttribPointer(aTextureCoordHandle,2,GLES20.GL_FLOAT,false,8,textureVertexBuffer); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,textureId); GLES20.glUniform1i(uTextureSamplerHandle,0); GLES20.glViewport(0,0,screenWidth,screenHeight); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); } @Override synchronized public void onFrameAvailable(SurfaceTexture surface) { updateSurface = true; } @Override public void onVideoSizeChanged(MediaPlayer mp, int width, int height) { Log.d(TAG, "onVideoSizeChanged: "+width+" "+height); updateProjection(width,height); } private void updateProjection(int videoWidth, int videoHeight){ float screenRatio=(float)screenWidth/screenHeight; float videoRatio=(float)videoWidth/videoHeight; if (videoRatio>screenRatio){ Matrix.orthoM(projectionMatrix,0,-1f,1f,-videoRatio/screenRatio,videoRatio/screenRatio,-1f,1f); }else Matrix.orthoM(projectionMatrix,0,-screenRatio/videoRatio,screenRatio/videoRatio,-1f,1f,-1f,1f); } public MediaPlayer getMediaPlayer() { return mediaPlayer; }}
MainActivity.java
package com.martin.ads.panoramaopengltutorial;import android.opengl.GLSurfaceView;import android.os.Environment;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;public class MainActivity extends AppCompatActivity { private GLSurfaceView glSurfaceView; private GLRenderer glRenderer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); glSurfaceView= (GLSurfaceView) findViewById(R.id.surface_view); glSurfaceView.setEGLContextClientVersion(2); glRenderer=new GLRenderer(this, Environment.getExternalStorageDirectory().getPath()+"/360Video/video.mp4"); glSurfaceView.setRenderer(glRenderer); glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } @Override protected void onDestroy() { super.onDestroy(); glRenderer.getMediaPlayer().release(); } @Override protected void onPause() { super.onPause(); glSurfaceView.onPause(); glRenderer.getMediaPlayer().pause(); } @Override protected void onResume() { super.onResume(); glSurfaceView.onResume(); }}
ShaderUtils.java
package com.martin.ads.panoramaopengltutorial;import android.content.Context;import android.opengl.GLES20;import android.util.Log;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;public class ShaderUtils { private static final String TAG = "ShaderUtils"; public static void checkGlError(String label) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, label + ": glError " + error); throw new RuntimeException(label + ": glError " + error); } } public static int createProgram(String vertexSource, String fragmentSource) { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } int program = GLES20.glCreateProgram(); if (program != 0) { GLES20.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); GLES20.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); GLES20.glLinkProgram(program); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] != GLES20.GL_TRUE) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } public static int loadShader(int shaderType, String source) { int shader = GLES20.glCreateShader(shaderType); if (shader != 0) { GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); int[] compiled = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(TAG, "Could not compile shader " + shaderType + ":"); Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } public static String readRawTextFile(Context context, int resId) { InputStream inputStream = context.getResources().openRawResource(resId); try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } reader.close(); return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return null; }}
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.3 使用GLSurfaceView和MediaPlayer播放一个平面视频(下)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.3 使用GLSurfaceView和MediaPlayer播放一个平面视频(下)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.1 使用GLSurfaceView和MediaPlayer播放一个平面视频(上)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.2 使用GLSurfaceView和MediaPlayer播放一个平面视频(中)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.1 使用GLSurfaceView和MediaPlayer播放一个平面视频(上)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.2 使用GLSurfaceView和MediaPlayer播放一个平面视频(中)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——5.8 使用MediaPlayer播放在线视频
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——目录
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——目录
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——5.1 使用OpenGL把全景视频贴到球上
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——5.6 播放器的UI制作
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.1 OpenGL ES 2.0基础知识
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——5.3 如何实现分屏效果
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——3.2 使用OpenGL ES 2.0绘制一个球
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——3.1 全景视频是如何实现的
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.4 用OpenGL ES 2.0显示一张图片(下)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——5.7 使用OpenGL ES接口保存屏幕截图
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——1.3 用OpenGL ES 2.0显示一张图片(上)
- Netstat 命令详解
- Java并发编程(Concurrency)并发模型
- ORA-01078和LRM-00109错误解决
- windows下将redis做成服务,随系统自启动
- 使用httpclient实现上传下载(javaWeb系统数据传输http实现)
- [OpenGL]从零开始写一个Android平台下的全景视频播放器——2.3 使用GLSurfaceView和MediaPlayer播放一个平面视频(下)
- Android 内存分析
- 穷举法-柏松分酒
- js setInterval 与 setTimeout
- exit(0)与exit(1)、return区别
- 接口与继承的区别
- mysql支持中文汉字排序的实现方法
- 如何清除电脑屏幕
- 在Mac 上装java jdk 。路径的配置