Android MediaPlayer + GLSurfaceView 播放视频

杀鸡焉用牛刀  金牌会员 | 2024-9-22 02:24:47 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 836|帖子 836|积分 2508

概述

    在Android开辟中,使用OpenGL ES来渲染视频是一种常见的需求,尤其是在需要实现自定义的视频播放界面或者视频殊效时。联合MediaPlayer,我们可以实现一个功能强盛的视频播放器。以下是一个简单的示例,展示如安在Android应用中使用OpenGL ES和MediaPlayer播放当地视频。
    常规的视频播放方式有VideoView, MediaPlayer + SurfaceView / TextureView, 以TextureView为例, 它与OpenGL(GLSurfaceView)播放的一些比较:
TextureView的优缺点



  • 长处

    • 灵活性高:TextureView可以与其他View叠加使用,非常适合在复杂的视图层次结构中使用。
    • 硬件加速支持:由于它在硬件加速层进行渲染,其性能也较优。
    • 支持绘制操纵:可以从其他线程更新内容,适合用于播放视频、显示及时殊效等。

  • 缺点

    • 内存占用较高:TextureView的内部缓冲队列导致比SurfaceView使用更多的内存。
    • 在5.0以前在主线程渲染:在5.0版本之前,TextureView在主线程渲染,可能会导致性能题目。

OpenGL的优缺点



  • 长处

    • 高度定制化:OpenGL提供低级别的图形渲染接口,答应开辟者高度定制视频播放界面和殊效。
    • 性能优化:通过优化渲染代码,可以在一定程度上进步视频播放的服从和性能。

  • 缺点

    • 开辟复杂度较高:使用OpenGL需要编写大量的底层代码,包罗顶点着色器和片段着色器的编写,这增长了开辟的复杂度和难度。

    TextureView在灵活性、硬件加速支持和多线程更新方面具有优势,适合需要与其他视图交互的场景。而OpenGL则提供了更高的定制化程度,适合需要实现复杂图形结果的场景
实现

    梳理收集来的参考代码, 实现视频播放结果如下:

   GLVideoActivity.java
  1. package com.ansondroider.sdktester.activity;
  2. import android.media.MediaPlayer;
  3. import android.opengl.GLSurfaceView;
  4. import android.os.Bundle;
  5. import com.ansondroider.acore.BaseActivity;
  6. import com.ansondroider.sdktester.gl.GLVideoView;
  7. import java.io.IOException;
  8. public class GLVideoActivity extends BaseActivity {
  9.     MediaPlayer mmp;
  10.     GLVideoView glView;
  11.     @Override
  12.     protected void onCreate(Bundle savedInstanceState) {
  13.         super.onCreate(savedInstanceState);
  14.         glView = new GLVideoView(this);
  15.         setContentView(glView);
  16.     }
  17.     @Override
  18.     protected void onStart() {
  19.         super.onStart();
  20.         //postDelayed(new Runnable() {
  21.         //    @Override
  22.         //    public void run() {
  23.                 mmp = new MediaPlayer();
  24.                 try {
  25.                     mmp.setDataSource("/sdcard/Movies/376463.mp4");
  26.                     mmp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
  27.                         @Override
  28.                         public void onPrepared(MediaPlayer mediaPlayer) {
  29.                             glView.onVideoPrepared(mediaPlayer);
  30.                         }
  31.                     });
  32.                     mmp.prepare();
  33.                 } catch (IOException e) {
  34.                     e.printStackTrace();
  35.                 }
  36.         //    }
  37.         //}, 100);
  38.     }
  39.     @Override
  40.     protected void onStop() {
  41.         super.onStop();
  42.         mmp.stop();
  43.         mmp.release();
  44.     }
  45. }
复制代码
  GLVideoView.java
  1. package com.ansondroider.sdktester.gl;
  2. import android.content.Context;
  3. import android.graphics.SurfaceTexture;
  4. import android.media.MediaPlayer;
  5. import android.opengl.GLES11Ext;
  6. import android.opengl.GLES20;
  7. import android.opengl.GLSurfaceView;
  8. import android.opengl.Matrix;
  9. import android.util.AttributeSet;
  10. import android.util.Log;
  11. import android.view.Surface;
  12. import java.nio.ByteBuffer;
  13. import java.nio.ByteOrder;
  14. import java.nio.FloatBuffer;
  15. import javax.microedition.khronos.egl.EGLConfig;
  16. import javax.microedition.khronos.opengles.GL10;
  17. /**
  18. * @ProjectName: TheSimpllestplayer
  19. * @Package: com.yw.thesimpllestplayer.renderview
  20. * @ClassName: VideoDrawer
  21. * @Description: 视频渲染器
  22. * @Author: wei.yang
  23. * @CreateDate: 2021/11/6 14:23
  24. * @UpdateUser: 更新者:wei.yang
  25. * @UpdateDate: 2021/11/6 14:23
  26. * @UpdateRemark: 更新说明:
  27. * @Version: 1.0
  28. */
  29. public class GLVideoView extends GLSurfaceView {
  30.     final String TAG = "GLVideoView";
  31.     //顶点坐标,此处的坐标系是物体坐标系:中心店坐标是(0,0)
  32.     private float[] mVertexCoors = new float[]{
  33.             -1f, -1f,
  34.             1f, -1f,
  35.             -1f, 1f,
  36.             1f, 1f
  37.     };
  38.     //纹理坐标系,中心坐标点为(0.5,0.5),上方向为t从0~1,右边方向为s,从0~1.刚好和计算器物理坐标系是反过来的。
  39.     private float[] mTextureCoors = new float[]{
  40.             0f, 1f,
  41.             1f, 1f,
  42.             0f, 0f,
  43.             1f, 0f
  44.     };
  45.     private String vertextShaderSource = "attribute vec4 aPosition;" +
  46.             "precision mediump float;" +
  47.             "uniform mat4 uMatrix;" +
  48.             "attribute vec2 aCoordinate;" +
  49.             "varying vec2 vCoordinate;" +
  50.             "attribute float alpha;" +
  51.             "varying float inAlpha;" +
  52.             "void main(){" +
  53.             "gl_Position = uMatrix*aPosition;" +
  54.             "vCoordinate = aCoordinate;" +
  55.             "inAlpha = alpha;" +
  56.             "}";
  57.     private String fragmentShaderSource = "#extension GL_OES_EGL_image_external : require\n" +
  58.             "precision mediump float;" +
  59.             "varying vec2 vCoordinate;" +
  60.             "varying float inAlpha;" +
  61.             "uniform samplerExternalOES uTexture;" +
  62.             "void main() {" +
  63.             "vec4 color = texture2D(uTexture, vCoordinate);" +
  64.             "gl_FragColor = vec4(color.r, color.g, color.b, inAlpha);" +
  65.             "}";
  66.     //视频宽高
  67.     private int mVideoWidth = -1;
  68.     private int mVideoHeight = -1;
  69.     //物理屏幕的宽高
  70.     private int mWorldWidth = -1;
  71.     private int mWorldHeight = -1;
  72.     //纹理ID
  73.     private int mTextureId = -1;
  74.     //定义SurfaceTexture 为显示视频做准备;
  75.     private SurfaceTexture mSurfaceTexture = null;
  76.     // 定义OpenGL 程序ID
  77.     private int mProgram = -1;
  78.     //矩阵变换接受者(shader中)
  79.     private int mVertexMatrixHandler = -1;
  80.     //顶点坐标接收者
  81.     private int mVertexPosHandler = -1;
  82.     //纹理坐标接受者
  83.     private int mTexturePosHandler = -1;
  84.     //纹理接受者
  85.     private int mTextureHandler = -1;
  86.     //半透明值接受者
  87.     private int mAlphaHandler = -1;
  88.     //顶点缓冲
  89.     private FloatBuffer mVertexBuffer = null;
  90.     //纹理缓冲
  91.     private FloatBuffer mTextureBuffer = null;
  92.     //矩阵
  93.     private float[] mMatrix = null;
  94.     //透明度
  95.     private float mAlpha = 1f;
  96.     //旋转角度
  97.     private float mWidthRatio = 1f;
  98.     private float mHeightRatio = 1f;
  99.     private int floatLength = 16;
  100.     public GLVideoView(Context context) {
  101.         super(context);
  102.         init();
  103.     }
  104.     public GLVideoView(Context context, AttributeSet attrs) {
  105.         super(context, attrs);
  106.         init();
  107.     }
  108.     private void init(){
  109.         setEGLContextClientVersion(2);
  110.         setRenderer(new VideoRender());
  111.         setRenderMode(RENDERMODE_WHEN_DIRTY);
  112.         initPos();
  113.     }
  114.     /**
  115.      * 初始化顶点坐标
  116.      */
  117.     private void initPos() {
  118.         ByteBuffer vByteBuffer = ByteBuffer.allocateDirect(mVertexCoors.length * 4);
  119.         vByteBuffer.order(ByteOrder.nativeOrder());
  120.         //将坐标转换为floatbuffer,用以传给opengl程序
  121.         mVertexBuffer = vByteBuffer.asFloatBuffer();
  122.         mVertexBuffer.put(mVertexCoors);
  123.         mVertexBuffer.position(0);
  124.         ByteBuffer tByteBuffer = ByteBuffer.allocateDirect(mTextureCoors.length * 4);
  125.         tByteBuffer.order(ByteOrder.nativeOrder());
  126.         mTextureBuffer = tByteBuffer.asFloatBuffer();
  127.         mTextureBuffer.put(mTextureCoors);
  128.         mTextureBuffer.position(0);
  129.     }
  130.     /**
  131.      * 初始化矩阵变换,主要是防止视频拉伸变形
  132.      */
  133.     private void initDefMatrix() {
  134.         //Log.d(TAG, "initDefMatrix");
  135.         if (mMatrix != null) return;
  136.         if (mVideoWidth != -1 && mVideoHeight != -1 &&
  137.                 mWorldWidth != -1 && mWorldHeight != -1) {
  138.             mMatrix = new float[floatLength];
  139.             float[] prjMatrix = new float[floatLength];
  140.             float originRatio = mVideoWidth / (float) mVideoHeight;
  141.             float worldRatio = mWorldWidth / (float) mWorldHeight;
  142.             if (mWorldWidth > mWorldHeight) {
  143.                 if (originRatio > worldRatio) {
  144.                     mHeightRatio = originRatio / worldRatio;
  145.                     Matrix.orthoM(
  146.                             prjMatrix, 0,
  147.                             -mWidthRatio, mWidthRatio,
  148.                             -mHeightRatio, mHeightRatio,
  149.                             3f, 5f
  150.                     );
  151.                 } else {// 原始比例小于窗口比例,缩放高度度会导致高度超出,因此,高度以窗口为准,缩放宽度
  152.                     mWidthRatio = worldRatio / originRatio;
  153.                     Matrix.orthoM(
  154.                             prjMatrix, 0,
  155.                             -mWidthRatio, mWidthRatio,
  156.                             -mHeightRatio, mHeightRatio,
  157.                             3f, 5f
  158.                     );
  159.                 }
  160.             } else {
  161.                 if (originRatio > worldRatio) {
  162.                     mHeightRatio = originRatio / worldRatio;
  163.                     Matrix.orthoM(
  164.                             prjMatrix, 0,
  165.                             -mWidthRatio, mWidthRatio,
  166.                             -mHeightRatio, mHeightRatio,
  167.                             3f, 5f
  168.                     );
  169.                 } else {// 原始比例小于窗口比例,缩放高度会导致高度超出,因此,高度以窗口为准,缩放宽度
  170.                     mWidthRatio = worldRatio / originRatio;
  171.                     Matrix.orthoM(
  172.                             prjMatrix, 0,
  173.                             -mWidthRatio, mWidthRatio,
  174.                             -mHeightRatio, mHeightRatio,
  175.                             3f, 5f
  176.                     );
  177.                 }
  178.             }
  179.             //设置相机位置
  180.             float[] viewMatrix = new float[floatLength];
  181.             Matrix.setLookAtM(
  182.                     viewMatrix, 0,
  183.                     0f, 0f, 5.0f,
  184.                     0f, 0f, 0f,
  185.                     0f, 1.0f, 0f
  186.             );
  187.             //计算变换矩阵
  188.             Matrix.multiplyMM(mMatrix, 0, prjMatrix, 0, viewMatrix, 0);
  189.         }
  190.     }
  191.     private Surface mSurface = null;
  192.     MediaPlayer mMediaPlayer;
  193.     public void onVideoPrepared(MediaPlayer mp){
  194.         Log.d(TAG, "onVideoPrepared");
  195.         mMediaPlayer = mp;
  196.         if(mSurfaceTexture != null) {
  197.             int videoWidth = mMediaPlayer.getVideoWidth();
  198.             int videoHeight = mMediaPlayer.getVideoHeight();
  199.             setVideoSize(videoWidth, videoHeight);
  200.             mSurface = new Surface(mSurfaceTexture);
  201.             mMediaPlayer.setSurface(mSurface);
  202.             mMediaPlayer.start();
  203.         }
  204.     }
  205.     private void setVideoSize(int videoWidth, int videoHeight) {
  206.         Log.d(TAG, "setVideoSize " + videoWidth + "x" + videoHeight);
  207.         mVideoWidth = videoWidth;
  208.         mVideoHeight = videoHeight;
  209.     }
  210.     private void setWorldSize(int worldWidth, int worldHeight) {
  211.         mWorldWidth = worldWidth;
  212.         mWorldHeight = worldHeight;
  213.     }
  214.     @Override
  215.     public void setAlpha(float alpha) {
  216.         super.setAlpha(alpha);
  217.         mAlpha = alpha;
  218.     }
  219.     private SurfaceTexture getSurfaceTexture() {
  220.         return mSurfaceTexture;
  221.     }
  222.     /**
  223.      * 创建并使用opengles程序
  224.      */
  225.     private void createGLPrg() {
  226.         if (mProgram == -1) {
  227.             int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertextShaderSource);
  228.             int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderSource);
  229.             //创建programe陈谷
  230.             mProgram = GLES20.glCreateProgram();
  231.             //将顶点着色器加入程序
  232.             GLES20.glAttachShader(mProgram, vertexShader);
  233.             //将片元着色器加入程序
  234.             GLES20.glAttachShader(mProgram, fragmentShader);
  235.             GLES20.glLinkProgram(mProgram);
  236.             //从程序中获取句柄
  237.             mVertexMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix");
  238.             mVertexPosHandler = GLES20.glGetAttribLocation(mProgram, "aPosition");
  239.             mTextureHandler = GLES20.glGetUniformLocation(mProgram, "uTexture");
  240.             mTexturePosHandler = GLES20.glGetAttribLocation(mProgram, "aCoordinate");
  241.             mAlphaHandler = GLES20.glGetAttribLocation(mProgram, "alpha");
  242.         }
  243.         //使用opengl程序
  244.         GLES20.glUseProgram(mProgram);
  245.     }
  246.     /**
  247.      * 激活并绑定纹理单元
  248.      */
  249.     private void activateTexture() {
  250.         //激活指定纹理单元
  251.         GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  252.         //绑定纹理ID到纹理单元
  253.         GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureId);
  254.         //将激活并绑定的纹理id传递到着色器里面
  255.         GLES20.glUniform1i(mTextureHandler, 0);
  256.         //配置边缘过滤参数
  257.         GLES20.glTexParameterf(
  258.                 GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
  259.                 GLES20.GL_TEXTURE_MIN_FILTER,
  260.                 GLES20.GL_LINEAR
  261.         );
  262.         GLES20.glTexParameterf(
  263.                 GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
  264.                 GLES20.GL_TEXTURE_MAG_FILTER,
  265.                 GLES20.GL_LINEAR
  266.         );
  267.         //配置s轴和t轴的方式
  268.         GLES20.glTexParameteri(
  269.                 GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
  270.                 GLES20.GL_TEXTURE_WRAP_S,
  271.                 GLES20.GL_CLAMP_TO_EDGE
  272.         );
  273.         GLES20.glTexParameteri(
  274.                 GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
  275.                 GLES20.GL_TEXTURE_WRAP_T,
  276.                 GLES20.GL_CLAMP_TO_EDGE
  277.         );
  278.     }
  279.     private void updateTexture() {
  280.         mSurfaceTexture.updateTexImage();
  281.     }
  282.     /**
  283.      * 加载着色器
  284.      *
  285.      * @param shaderType 着色器类型
  286.      * @param shaderCode 着色器代码
  287.      * @return
  288.      */
  289.     private int loadShader(int shaderType, String shaderCode) {
  290.         //根据着色器类型创建着色器
  291.         int shader = GLES20.glCreateShader(shaderType);
  292.         //将着色其代码加入到着色器
  293.         GLES20.glShaderSource(shader, shaderCode);
  294.         //编译zhuoseq
  295.         GLES20.glCompileShader(shader);
  296.         return shader;
  297.     }
  298.     /**
  299.      * 开始绘制渲染
  300.      */
  301.     public void doDraw() {
  302.         if(mMatrix == null)return;
  303.         //启用顶点坐标句柄
  304.         GLES20.glEnableVertexAttribArray(mVertexPosHandler);
  305.         GLES20.glEnableVertexAttribArray(mTexturePosHandler);
  306.         GLES20.glUniformMatrix4fv(mVertexMatrixHandler, 1, false, mMatrix, 0);
  307.         //设置着色器参数, 第二个参数表示一个顶点包含的数据数量,这里为xy,所以为2
  308.         GLES20.glVertexAttribPointer(mVertexPosHandler, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer);
  309.         GLES20.glVertexAttribPointer(
  310.                 mTexturePosHandler,
  311.                 2,
  312.                 GLES20.GL_FLOAT,
  313.                 false,
  314.                 0,
  315.                 mTextureBuffer
  316.         );
  317.         GLES20.glVertexAttrib1f(mAlphaHandler, mAlpha);
  318.         //开始绘制
  319.         GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mVertexCoors.length / 2);
  320.     }
  321.     @Override
  322.     protected void onDetachedFromWindow() {
  323.         Log.d(TAG, "onDetachedFromWindow");
  324.         super.onDetachedFromWindow();
  325.         GLES20.glDisableVertexAttribArray(mVertexPosHandler);
  326.         GLES20.glDisableVertexAttribArray(mTexturePosHandler);
  327.         GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
  328.         GLES20.glDeleteTextures(1, new int[]{mTextureId}, 0);
  329.         GLES20.glDeleteProgram(mProgram);
  330.         if(mMediaPlayer != null){
  331.             //mMediaPlayer.setSurface(null);
  332.             mMediaPlayer.release();
  333.             mSurface.release();
  334.         }
  335.     }
  336.     public void translate(float dx, float dy) {
  337.         Matrix.translateM(mMatrix, 0, dx * mWidthRatio * 2, -dy * mHeightRatio * 2, 0f);
  338.     }
  339.     public void scale(float sx, float sy) {
  340.         Matrix.scaleM(mMatrix, 0, sx, sy, 1f);
  341.         mWidthRatio /= sx;
  342.         mHeightRatio /= sy;
  343.     }
  344.     public class VideoRender implements GLSurfaceView.Renderer {
  345.         @Override
  346.         public void onSurfaceCreated(GL10 gl, EGLConfig config) {
  347.             Log.d(TAG, "onSurfaceCreated");
  348.             GLES20.glClearColor(0f, 0f, 0f, 0f);
  349.             //开启混合,即半透明
  350.             GLES20.glEnable(GLES20.GL_BLEND);
  351.             GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
  352.             int[] textureIds = new int[1];
  353.             GLES20.glGenTextures(1, textureIds, 0);
  354.             mTextureId = textureIds[0];
  355.             //根据textureId初始化一个SurfaceTexture
  356.             mSurfaceTexture = new SurfaceTexture(mTextureId);
  357.             mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
  358.                 @Override
  359.                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
  360.                     requestRender();
  361.                 }
  362.             });
  363.             if(mMediaPlayer != null){
  364.                 onVideoPrepared(mMediaPlayer);
  365.             }
  366.         }
  367.         @Override
  368.         public void onSurfaceChanged(GL10 gl, int width, int height) {
  369.             Log.d(TAG, "onSurfaceChanged");
  370.             GLES20.glViewport(0, 0, width, height);
  371.             setWorldSize(width, height);
  372.         }
  373.         @Override
  374.         public void onDrawFrame(GL10 gl) {
  375.             Log.d(TAG, "onDrawFrame");
  376.             if(mMediaPlayer == null || !mMediaPlayer.isPlaying())return;
  377.             //清除颜色缓冲和深度缓冲
  378.             GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
  379.             if (mTextureId != -1) {
  380.                 initDefMatrix();
  381.                 //2/创建、编译、启动opengles着色器
  382.                 createGLPrg();
  383.                 //3.激活并绑定纹理单元
  384.                 activateTexture();
  385.                 //4.绑定图元到纹理单元
  386.                 updateTexture();
  387.                 //5.开始绘制渲染
  388.                 doDraw();
  389.             }
  390.         }
  391.     }
  392. }
复制代码
复杂图形结果的场景

平凡的视频播放方式很难实现 曲面 百叶窗 这类的结果, 如:

而使用OpenGL播放, 只需要调整下顶点和纹理的坐标即可:
  1.     //顶点坐标,此处的坐标系是物体坐标系:中心店坐标是(0,0)
  2.     private float[] mVertexCoors = new float[]{
  3.             -1f, 1f,
  4.             -1f, -1f,
  5.             0, 0.5f,
  6.             0, -0.5f,
  7.             1f, 1f,
  8.             1f, -1f,
  9.     };
  10.     //纹理坐标系,中心坐标点为(0.5,0.5),上方向为t从0~1,右边方向为s,从0~1.刚好和计算器物理坐标系是反过来的。
  11.     private float[] mTextureCoors = new float[]{
  12.             0f, 0f,
  13.             0f, 1f,
  14.             0.5f, 0,
  15.             0.5f, 1f,
  16.             1f, 0f,
  17.             1f, 1f
  18.     };
复制代码
参考


  • 10.GLSurfaceView+MediaPlayer播放视频.md
  • 【Android 音视频开辟打怪升级:OpenGL渲染视频画面篇】二、使用OpenGL渲染视频画面
  • Android 最简单的视频播放器之OpenGL ES视频渲染工具封装(三)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表