Android OpenGL使用GLSurfaceView预览视频

第一章 相关知识介绍

在介绍具体的功能之前,先对一些主要的类和方法进行一些介绍,这样可以更好的理解整个程序

1.1 GLSurfaceView

在谷歌的官方文档中是这样解释GLSurfaceView的:

An implementation of SurfaceView that uses the dedicated surface for displaying OpenGL rendering.

大意是GLSurfaceView是一个继承了SurfaceView类,它是专门用来显示OpenGL的渲染。通俗的来说,GLSurfaceView可以用来显示视频、图像和3D模型等视图,在接下来的章节中主要是使用它来显示Camera视频数据,大家可能会有一些问题,SurfaceView也可用来预览Camera,那么这两者有什么区别吗?GLSurfaceView能够真正做到让Camera的数据和显示分离,我们就可以在此基础上对视频数据做一些处理,例如美图,增加特效等。

1.2 GLSurfaceView.Renderer

如果说GLSurfaceView是画布,那么仅仅有一张白纸是没用的,我们还需要一支画笔,Renderer的功能就是这里说的画笔。Renderer是一个接口,主要包含3个抽象的函数:onSurfaceCreatedonDrawFrameonSurfaceChanged,从名字就可以看出,分别是在SurfaceView创建、视图大小发生改变和绘制图形时调用。

1.3 Camera

从Android 5.0开始(API Level 21),可以完全控制安卓设别相机的新API Camera2(android.hardware.Camera2)被引进来了。虽然新的Camera2不管在功能上还是友好度上都强于旧的Camera,但是我们这里还是使用的旧的Camera,由于新的Camera2暂时还没有找到可以获取视频帧的接口,因为后面肯能会对Canmera视频帧做一些处理,所以这里暂时还是使用旧的Camera。

第二章 开始绘制

2.1 CameraGLSurfaceView

public class CameraGLSurfaceView extends GLSurfaceView implements Renderer, SurfaceTexture.OnFrameAvailableListener {private Context mContext;private SurfaceTexture mSurface;private int mTextureID = -1;private DirectDrawer mDirectDrawer;public CameraGLSurfaceView(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;// 设置OpenGl ES的版本为2.0setEGLContextClientVersion(2);// 设置与当前GLSurfaceView绑定的RenderersetRenderer(this);// 设置渲染的模式setRenderMode(RENDERMODE_WHEN_DIRTY);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// TODO Auto-generated method stubLOG.logI("onSurfaceCreated...");mTextureID = GlUtil.createTextureID();mSurface = new SurfaceTexture(mTextureID);mSurface.setOnFrameAvailableListener(this);mDirectDrawer = new DirectDrawer(mTextureID);CameraCapture.get().openBackCamera();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {// TODO Auto-generated method stubLOG.logI("onSurfaceChanged...");// 设置OpenGL场景的大小,(0,0)表示窗口内部视口的左下角,(w,h)指定了视口的大小GLES20.glViewport(0, 0, width, height);if (!CameraCapture.get().isPreviewing()) {CameraCapture.get().doStartPreview(mSurface);}}@Overridepublic void onDrawFrame(GL10 gl) {// TODO Auto-generated method stubLOG.logI("onDrawFrame...");// 设置白色为清屏GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);// 清除屏幕和深度缓存GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);// 更新纹理mSurface.updateTexImage();mDirectDrawer.draw();}@Overridepublic void onPause() {// TODO Auto-generated method stubsuper.onPause();CameraCapture.get().doStopCamera();}@Overridepublic void onFrameAvailable(SurfaceTexture surfaceTexture) {// TODO Auto-generated method stubLOG.logI("onFrameAvailable...");this.requestRender();}}

这个类主要做了以下几件事情:

  • 实现Renderer这个接口,并且实现GLSurfaceView的初始化。在CameraGLSurfaceView的构造函数中设置了GLSurfaceView的版本:setEGLContextClientVersion(2),如果没有这个设置,GLSurfaceView是什么也绘制不出来的,因为Android支持OpenGL ES1.1、2.0以及3.+等版本,而且版本间的差别很大,不声明版本号,GLSurfaceView是不知道使用哪个版本进行渲染;设置Renderer与当前的View绑定,然后再设置渲染的模式为RENDERMODE_WHEN_DIRTY。渲染模式的设置也很关键,渲染模式有两种:RENDERMODE_WHEN_DIRTYRENDERMODE_CONTINUOUSLY。DIRYT的含义是只有当被通知的时候才会去渲染视图,而CONTINUOUSLY的含义是视频会一直连续的渲染。
  • onSurfaceCreated()函数中,创建一个渲染的纹理,这个纹理就是用来显示Camera的图像,所以需要新创建的SurfaceTexture绑定在一起,而SurfaceTexture是作为渲染的载体,另一方面需要和DirectDrawer绑定在一起,DirectDrawer是用来绘制图像的,下面会具体介绍。最后是初始化Camera。
  • 因为在初始化的时候这是了渲染的模式为RENDERMODE_WHEN_DIRTY,所以我们就通知GLSurfaceView什么时候需要渲染图像,而接口SurfaceTexture.OnFrameAvailableListener完成这项工作,函数onFrameAvailable()在有新数据到来时,会被调用,在其中调用requestRender(),就可以完成新数据的渲染。
  • onSurfaceChanged()函数中,设置了OpenGL窗口的大小,(0,0)表示窗口内部视口的左下角,(w,h)指定了视口的大小;打开Camera的预览。
  • 最后,在onDrawFrame()函数中绘制更新的纹理。

2.2 DirectDrawer

这个类非常重要,负责将SurfaceTexture(纹理的句柄)内容绘制到屏幕上。

public class DirectDrawer {private FloatBuffer vertexBuffer, mTextureCoordsBuffer;private ShortBuffer drawListBuffer;private final int mProgram;private int mPositionHandle;private int mTextureCoordHandle;private int mMVPMatrixHandle;private short drawOrder[] = {0, 2, 1, 0, 3, 2}; // order to draw vertices// number of coordinates per vertex in this arrayprivate final int COORDS_PER_VERTEX = 2;private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertexprivate float mVertices[] = new float[8];private float mTextureCoords[] = new float[8];private float mTextHeightRatio = 0.1f;private int texture;public float[] mMVP = new float[16];public void resetMatrix() {mat4f_LoadOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, mMVP);}public DirectDrawer(int texture) {String vertextShader = TextResourceReader.readTextFileFromResource(MyApplication.getContext(), R.raw.video_vertex_shader);String fragmentShader = TextResourceReader.readTextFileFromResource(MyApplication.getContext(), R.raw.video_normal_fragment_shader);mProgram = GlUtil.createProgram(vertextShader, fragmentShader);if (mProgram == 0) {throw new RuntimeException("Unable to create program");}//get handle to vertex shader's vPosition membermPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");GlUtil.checkLocation(mPositionHandle, "vPosition");mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");GlUtil.checkLocation(mTextureCoordHandle, "inputTextureCoordinate");mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");GlUtil.checkLocation(mMVPMatrixHandle, "uMVPMatrix");this.texture = texture;// initialize vertex byte buffer for shape coordinatesupdateVertices();setTexCoords();// initialize byte buffer for the draw listByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);dlb.order(ByteOrder.nativeOrder());drawListBuffer = dlb.asShortBuffer();drawListBuffer.put(drawOrder);drawListBuffer.position(0);mat4f_LoadOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f, mMVP);}public void draw() {GLES20.glUseProgram(mProgram);GLES20.glActiveTexture(GLES20.GL_TEXTURE0);GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);// get handle to vertex shader's vPosition member// Enable a handle to the triangle verticesGLES20.glEnableVertexAttribArray(mPositionHandle);// Prepare the <insert shape here> coordinate dataGLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);GLES20.glEnableVertexAttribArray(mTextureCoordHandle);GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, mTextureCoordsBuffer);// Apply the projection and view transformationGLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVP, 0);GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);// Disable vertex arrayGLES20.glDisableVertexAttribArray(mPositionHandle);GLES20.glDisableVertexAttribArray(mTextureCoordHandle);}public static void mat4f_LoadOrtho(float left, float right, float bottom, float top, float near, float far, float[] mout) {float r_l = right - left;float t_b = top - bottom;float f_n = far - near;float tx = -(right + left) / (right - left);float ty = -(top + bottom) / (top - bottom);float tz = -(far + near) / (far - near);mout[0] = 2.0f / r_l;mout[1] = 0.0f;mout[2] = 0.0f;mout[3] = 0.0f;mout[4] = 0.0f;mout[5] = 2.0f / t_b;mout[6] = 0.0f;mout[7] = 0.0f;mout[8] = 0.0f;mout[9] = 0.0f;mout[10] = -2.0f / f_n;mout[11] = 0.0f;mout[12] = tx;mout[13] = ty;mout[14] = tz;mout[15] = 1.0f;}public void updateVertices() {final float w = 1.0f;final float h = 1.0f;mVertices[0] = -w;mVertices[1] = h;mVertices[2] = -w;mVertices[3] = -h;mVertices[4] = w;mVertices[5] = -h;mVertices[6] = w;mVertices[7] = h;vertexBuffer = ByteBuffer.allocateDirect(mVertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(mVertices);vertexBuffer.position(0);}public void setTexCoords() {mTextureCoords[0] = 0;mTextureCoords[1] = 1 - mTextHeightRatio;mTextureCoords[2] = 1;mTextureCoords[3] = 1 - mTextHeightRatio;mTextureCoords[4] = 1;mTextureCoords[5] = 0 + mTextHeightRatio;mTextureCoords[6] = 0;mTextureCoords[7] = 0 + mTextHeightRatio;mTextureCoordsBuffer = ByteBuffer.allocateDirect(mTextureCoords.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(mTextureCoords);mTextureCoordsBuffer.position(0);}
}

这个类的主要功能就是绘制图像。

(1) 定义Vertex Shader(顶点着色器,用来绘制图形的形状)、Fragment Shader(片段着色器,用来绘制图形的颜色或者纹理)和Program(OpenGL ES对象,包含了用来绘制一个或者多个形状的shader),然后接下来都是围绕着这三个变量,最后通过调用OpenGL方法进行绘制。具体的过程可以参考前面的博客 使用OpenGL ES显示图形

(2) 既然我们需要预览Camera的视频数据,那么我们可以知道现实的区域的形状大部分都是四边形,但是在OpenGL中只有提供了绘制三角形的方法,我们就需要将两个三角形拼接成一个正方形,所以需要定义一个大小为8的数组,如下面代码所示:

static float squareCoords[] = {  -1.0f,  1.0f,  // 左上点-1.0f, -1.0f,  // 左下点1.0f, -1.0f,  // 右下点1.0f,  1.0f,  // 有上点};  
  • 我们就有了一个四边形的4个点的数据了。但是,OpenGL并不是对数组的数据直接进行操作的,而是在直接内存中,即操作的数据需要保存到NIO里面的Buffer对象中。而我们上面生命的float[]对象保存在数组中,因此我们需要将float[]对象转换为Java.nio.Buffer对象,代码如下:
 public void updateVertices() {final float w = 1.0f;final float h = 1.0f;mVertices[0] = -w;mVertices[1] = h;mVertices[2] = -w;mVertices[3] = -h;mVertices[4] = w;mVertices[5] = -h;mVertices[6] = w;mVertices[7] = h;vertexBuffer = ByteBuffer.allocateDirect(mVertices.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(mVertices);vertexBuffer.position(0);}

注意,ByteBuffer和FloatBuffer以及IntBuffer都是继承自抽象类java.nio.Buffer。 
另外,OpenGL在底层的实现是C语言,与Java默认的数据存储字节顺序可能不同,即大端小端问题。因此,为了保险起见,在将数据传递给OpenGL之前,我们需要指明使用本机的存储顺序。 
此时,我们顺利地将float[]转为了FloatBuffer,后面绘制三角形的时候,直接通过成员变量mTriangleBuffer即可。

(3) 最后就是将准备好的数据绘制到屏幕上,OpenGL 提供了两个绘制的方法glDrawArrays(int mode, int first, int count)glDrawElements(int mode,int count, int type, Buffer indices)两个方法,在这里我们使用的第二种绘制的方法,关于mode有几种模式供我们选择:

  • GL_POINTS:绘制独立的点到屏幕 
  • GL_LINE_STRIP:连续的连线,第n个顶点与第n-1个顶点绘制一条直线 
  • GL_LINE_LOOP:与上一个相同,但是需要首尾相联接 
  • GL_LINES:形成对的独立的线段 
  • GL_TRIANGLE_STRIP:绘制一系列的三角形,先是顶点v0,v1,v2,然后是v2,v1,v3(注意规律),然后v2,v3,v4等。该规律确保所有的三角形都以相同的方向绘制 
  • GL_TRIANGLE_FANGL_TRANGLE_STRIP类似,但其县绘制v0,v1,v2,再是v0,v2,v3,然后v0,v3,v4等。 

(4) 需要注意的是,在这个类中,定义了mMVP这个数组,这个数组的功能是对视频帧数据进行转换的,例如旋转图像等。

总结

到此为止,使用GLSurfaceView预览Camera的介绍就完了,这篇文章,仅仅介绍了CameraGLSurfaceViewDirectDrawer这两个类,但是如何对Camera进行操作的并没有介绍,这不是本文的重点,所以就省略了。接下来还会介绍一些有关GLSurfaceView的文章。

Android OpenGL渲染双视频

下载代码

特别声明文章转载自:https://blog.csdn.net/a296777513/article/details/63685658

Android OpenGL使用GLSurfaceView预览视频相关推荐

  1. [转载]玩转Android Camera开发(三):国内首发---使用GLSurfaceView预览Camera 基础拍照demo...

    GLSurfaceView 是OpenGL中的一个类,也是可以预览Camera的,而且在预览Camera上有其独到之处.独到之处在哪?当使用Surfaceview无能为力. 痛不欲生时就只有使用GLS ...

  2. 【Android RTMP】Android Camera 视频数据采集预览 ( 视频采集相关概念 | 摄像头预览参数设置 | 摄像头预览数据回调接口 )

    文章目录 安卓直播推流专栏博客总结 一. Android 端数据采集涉及到的相关概念 二. Camera 预览图像尺寸设置 三. 获取摄像头采集的数据格式 安卓直播推流专栏博客总结 Android R ...

  3. android 视频预览,预览视频  |  Android 开发者  |  Android Developers

    如需鼓励用户访问您的 TV 应用中的深层链接,预览视频是一种不错的方法. 预览内容可以是简短的视频剪辑,也可以是完整的电影预告片. 在创建预览时,请注意以下准则: 不要在预览中显示广告.如果您在客户端 ...

  4. Android 使用CameraX实现预览/拍照/录制视频/图片分析/对焦/缩放/切换摄像头等操作

    1. CameraX架构 看官方文档 CameraX架构 有如下这一段话 使用CameraX,借助名为"用例"的抽象概念与设备的相机进行交互. 预览 : 接受用于显示预览的Surf ...

  5. 【流媒體】Android 实时视频采集—Camera预览采集

    [流媒體]Android 实时视频采集-Cameara预览采集 SkySeraph Mar 26th 2012  SZ Email:skyseraph00@163.com 更多精彩请直接访问SkySe ...

  6. android firefox 版本,Android版本Firefox初期预览版发布

    不久前Mozilla宣布不再为Windows Mobile开发手机版Firefox,而将大部分精力投入到Android版本的开发商.今天Android版本Firefox终于完成了初期预览版,Andro ...

  7. 终极指南:如何为iOS8应用制作预览视频

    本文转载地址:http://blog.jobbole.com/78978/ 最近一两个月里,苹果的世界里出现了很多新东西,比如屏幕更大的iPhone 6,可穿戴设备Apple Watch,iOS8,以 ...

  8. android 摄像头花屏_关于Android 4.4相机预览、录像花屏的问题的解决方法

    关于Android 4.4相机预览.录像花屏的问题的解决方法 系统: lc android4.4 在做前后摄像头录像的时候,发现会出现花屏的时候,但不是必现,可能会在某一次重启之后会出现,而且出现之后 ...

  9. js上传视频,预览视频

    js上传视频,预览视频 <videostyle="width:300px; height:auto;object-fit: fill;"playsinlinecontrols ...

最新文章

  1. 【项目实践】车距+车辆+车道线+行人检测项目实践
  2. python量化策略代码_手把手教你用三行python 代码做一个动量策略「量化投资系列」...
  3. 2020年,知识图谱都有哪些研究风向?
  4. 人会成为虚拟现实中的“机器人代码”吗?
  5. 使用async/await——Nodejs+ExpressJs+Babel
  6. python开发工程师面试题-一名python web后端开发工程师的面试总结
  7. Python数据分析,抓取京东商品价格
  8. content-type的作用
  9. python tempfile 创建临时目录
  10. pycharm导入(import)报红(出现红色波浪线)解决办法(Mark Directory as —— Sources Root)
  11. 设计模式学习之单件模式singleton
  12. qt设置顶层窗口_Python快速入门系列:PyQt5 快速开发GUI-窗口类型以及主窗口创建...
  13. java 代码锁_Java 锁的知识总结及实例代码
  14. [LeetCode]12. Integer to Roman
  15. 流量卡之家:拥有边缘计算的物联网才是真正的物联网
  16. python 发送邮件实现 抄送,密送
  17. 微信小程序设置web-view的业务域名
  18. k8s学习-网络策略NetworkPolicy(概念、模版、创建、删除等)
  19. 一个农村孩子的大城市梦想之深入江湖!
  20. 面向三种典型程序语言的中小学计算思维课堂设计研究

热门文章

  1. 给定n本书的名称和定价,本题要求编写程序,查找并输出其中定价最高和最低的书的名称和定价
  2. 没有与参数列表匹配的 重载函数 getline 实例_面试题:方法重载的底层原理?...
  3. java response.write_response设置编码方式 print和write方法的对比
  4. mysql 最小时间 最大时间_leetcode539_go_最小时间差
  5. sap可以指定应用服务器,SAP扫盲系列之一:什么是SAP系统和应用服务器
  6. php计算格子xy,经纬度BL和直角坐标XY的正算反算 PHP代码
  7. 北理工计算机学院专业确认,北理工 2021 强基计划报名增加确认环节,限报 1 校 1 专业...
  8. 神经网络基础:(1)得分函数 or 得分函数
  9. mask rcnn网络结构笔记
  10. MathType6.9b安装及在Word2013中无法正常使用的解决方法