您的位置:首页 > 移动开发 > Android开发

OpenGL播放yuv数据流(着色器SHADER)-android(一)

2016-12-29 14:58 417 查看
OpenGL播放yuv数据流(着色器SHADER)-android(一)可以参考:http://blog.csdn.net/ueryueryuery/article/details/17608185这篇文章很有帮助。这个和windows还有ios略有不同,下面将步骤整理一下以做记录:1:在avtivity_main.xml中添加用于显示的GLsurfaceView<android.opengl.GLSurfaceViewandroid:id="@+id/lvsPlaySurfaceView"android:layout_width="match_parent"android:layout_height="400dp"/>2:将GLsurfaceView传到里面
//得到opengal渲染用的surfaceView
openglsurfaceView = (GLSurfaceView) findViewById(R.id.lvsPlaySurfaceView);
3:需要添加权限在AndroidMainfest.xml中:
<!--为了能使用OpenGLES 2.0 API,你必须在你的manifest中添加以下声明:-->
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<!-- 如果你的应用要使用纹理压缩功能,你必须还要声明设备需要支持什么样的压缩格式-->
<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
4:下面就是具体的实现代码
//.java
package com.example.zhuweigang.lvsandroidplay;import android.content.Context;import android.opengl.GLES20;import android.opengl.GLSurfaceView;import android.support.v4.app.NavUtils;import android.util.AttributeSet;import android.util.Log;import android.view.SurfaceHolder;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;import java.nio.Buffer;import java.nio.ByteBuffer;import java.nio.ByteOrder;/*** Created by zhuweigang on 2016/12/26.*/public class Lvs_OpenGl_Interface_Android implements GLSurfaceView.Renderer{private static final String TAG = "lvs_OpenGL";//顶点数组(物体表面坐标取值范围是-1到1,数组坐标:左下,右下,左上,右上)private static float[] vertexVertices = {-1.0f, -1.0f,1.0f, -1.0f,-1.0f,  1.0f,1.0f,  1.0f,};//像素,纹理数组(纹理坐标取值范围是0-1,坐标原点位于左下角,数组坐标:左上,右上,左下,右下,如果先左下,图像会倒过来)private static float[] textureVertices = {0.0f,  1.0f,1.0f,  1.0f,0.0f,  0.0f,1.0f,  0.0f,};//shader的vsh源码字符串private static final String vertexShaderString ="attribute vec4 vertexIn;" +"attribute vec2 textureIn;" +"varying vec2 textureOut;" +"void main() {" +"gl_Position = vertexIn;" +"textureOut = textureIn;" +"}";//shader的fsh源码字符串private static final String yuvFragmentShaderString ="precision mediump float;" +"uniform sampler2D tex_y;" +"uniform sampler2D tex_u;" +"uniform sampler2D tex_v;" +"varying vec2 textureOut;" +"void main() {" +"vec4 c = vec4((texture2D(tex_y, textureOut).r - 16./255.) * 1.164);" +"vec4 U = vec4(texture2D(tex_u, textureOut).r - 128./255.);" +"vec4 V = vec4(texture2D(tex_v, textureOut).r - 128./255.);" +"c += V * vec4(1.596, -0.813, 0, 0);" +"c += U * vec4(0, -0.392, 2.017, 0);" +"c.a = 1.0;" +"gl_FragColor = c;" +"}";//着色器用的顶点属性索引 position是由3个(x,y,z)组成,public int ATTRIB_VERTEX = 0;//着色器用的像素,纹理属性索引 而颜色是4个(r,g,b,a)public int ATTRIB_TEXTURE = 0;private GLSurfaceView mTargetSurface;                                                              //外部传入的GLSurfaceViewpublic int p = 0;                                                                                    //Program着色器程序的idByteBuffer vertexVertices_buffer = null;                                                           //定义顶点数组ByteBuffer textureVertices_buffer = null;                                                          //定义像素纹理数组public int m_IsInitShaders = 0;                                                                    //是否已经InitShaders,onSurfaceCreatedpublic Lvs_Play_Interface_Sdk_Android.OpenGl_DisplayCallBackInterface m_displaydatack = null;      //用于显示回调函数,参数数据及时间戳public byte m_yuvbuf[] = new byte[640*480*3];												       //存放yuv数据的buf指针,申请buffer在外面public ByteBuffer yuvplaner_y = null;                                                               //分用于渲染的变量public ByteBuffer yuvplaner_u = null;                                                               //分用于渲染的变量public ByteBuffer yuvplaner_v = null;                                                               //分用于渲染的变量;public int[] m_millis_realtime = new int[1];                                                      //实时的时间戳,每次回调会更新public int m_yuvdata_width = 0;											                            //数据宽public int m_yuvdata_height = 0;										                            //数据高public int m_frameBuffer = 0;                                                                       //framebufferpublic int m_renderBuffer = 0;                                                                      //renderbufferpublic int m_textureid_y, m_textureid_u, m_textureid_v;                                         //纹理的名称,并且,该纹理的名称在当前的应用中不能被再次使用。public int m_textureUniformY, m_textureUniformU,m_textureUniformV;                            //用于纹理渲染的变量//构造方法public Lvs_OpenGl_Interface_Android(GLSurfaceView paramGLSurfaceView){//将surfaceview传进来用于显示数据时候刷新mTargetSurface = paramGLSurfaceView;//应用GlsurfaceView版本号2.0mTargetSurface.setEGLContextClientVersion(2);}@Overridepublic void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig){Log.i(TAG, "onSurfaceCreated :");//这个必须在onSurfaceCreated中,否则失败//初始化着色器,类似于告GPU当传进去数据的时候采用什么样的规则。InitShaders();m_IsInitShaders = 1;}@Overridepublic void onSurfaceChanged(GL10 gl10, int width, int height){GLES20.glViewport(0, 0, width, height);}@Overridepublic void onDrawFrame(GL10 gl10){//这里做具体的处理//具体的显示if (mTargetSurface != null){DisplayImage(0);}}//接口初始化int lvs_opengl_interface_init(GLSurfaceView surface,int yuvdata_width,int yuvdata_height,Lvs_Play_Interface_Sdk_Android.OpenGl_DisplayCallBackInterface displaydatack){int ret  = 0;//初始化ret = initopengl(yuvdata_width,yuvdata_height,displaydatack);if (ret != 1){return -1;}ret = 1;return ret ;}//接口渲染数据(定时器,渲染时间,毫秒),数据及渲染定时时间在回调里面做处理void lvs_opengl_interface_write(int value){//这里如果有可能则调成类的成员函数,以后处理,暂时不知道怎么解决类成员函数递归TimerFunc1();}//渲染数据(定时器,渲染时间,毫秒),数据及渲染定时时间在回调里面做处理void TimerFunc1(){int ret = 0;//因为glut的定时器是调用一次才产生一次定时,所以如果要持续产生定时的话,//在定时函数末尾再次调用glutTimerFunc//调用回调函数获取数据if (m_displaydatack != null && m_IsInitShaders == 1){ret= m_displaydatack.OpenGl_DisplayDataCallback(m_yuvbuf,m_millis_realtime);if (ret > 0){//刷新让他能显示在onDrawFrame中处理mTargetSurface.requestRender();try {Thread.sleep(m_millis_realtime[0]);} catch (InterruptedException e) {e.printStackTrace();}}else{try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}//递归调用自身,java用递归调用本身有问题了,外面for循环调用处理//TimerFunc1(opengl_interface);}}//初始化int initopengl(int yuvdata_width,int yuvdata_height, Lvs_Play_Interface_Sdk_Android.OpenGl_DisplayCallBackInterface displaydatack){int ret = 0;m_yuvdata_width = yuvdata_width;m_yuvdata_height = yuvdata_height;m_displaydatack = displaydatack;//分配内存if (yuvplaner_y == null){yuvplaner_y = ByteBuffer.allocate(m_yuvdata_width*m_yuvdata_height + 100);}if (yuvplaner_u == null){yuvplaner_u = ByteBuffer.allocate(m_yuvdata_width*m_yuvdata_height + 100);}if (yuvplaner_v == null){yuvplaner_v = ByteBuffer.allocate(m_yuvdata_width*m_yuvdata_height + 100);}ret = 1;return ret;}//初始化着色器,类似于告GPU当传进去数据的时候采用什么样的规则。void InitShaders(){int error = 0;createBuffers(vertexVertices, textureVertices);p = createProgram(vertexShaderString, yuvFragmentShaderString);ATTRIB_VERTEX = GLES20.glGetAttribLocation(p, "vertexIn");if (ATTRIB_VERTEX == -1){Log.i(TAG, "glGetAttribLocation : " + error);}ATTRIB_TEXTURE = GLES20.glGetAttribLocation(p, "textureIn");if (ATTRIB_TEXTURE == -1){Log.i(TAG, "glGetAttribLocation : " + error);}//Program:  在链接了程序以后,我们可以使用glUseProgram()函数来加载并使用链接好的程序GLES20.glUseProgram(p);//获取片源着色器源码中的变量,用于纹理渲染m_textureUniformY = GLES20.glGetUniformLocation(p, "tex_y");m_textureUniformU = GLES20.glGetUniformLocation(p, "tex_u");m_textureUniformV = GLES20.glGetUniformLocation(p, "tex_v");//初始化纹理int[] textures_y = new int[1];GLES20.glGenTextures(1, textures_y,0);m_textureid_y = textures_y[0];textures_y = null;//绑定纹理GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureid_y);//设置该纹理的一些属性GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);int[] textures_u = new int[1];GLES20.glGenTextures(1, textures_u,0);m_textureid_u = textures_u[0];textures_u = null;GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureid_u);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);int[] textures_v = new int[1];GLES20.glGenTextures(1, textures_v,0);m_textureid_v = textures_v[0];textures_v = null;GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureid_v);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);return;}//具体显示图像的函数(参数是指针)int DisplayImage(long parm){int ret = 0;//关联到yuv数据的分量数组if (yuvplaner_y != null){yuvplaner_y.clear();yuvplaner_y.put(m_yuvbuf,0,m_yuvdata_width*m_yuvdata_height);yuvplaner_y.position(0);}if (yuvplaner_u != null){yuvplaner_u.clear();yuvplaner_u.put(m_yuvbuf,m_yuvdata_width*m_yuvdata_height,m_yuvdata_width*m_yuvdata_height/4);yuvplaner_u.position(0);}if (yuvplaner_v != null){yuvplaner_v.clear();yuvplaner_v.put(m_yuvbuf,m_yuvdata_width*m_yuvdata_height + m_yuvdata_width*m_yuvdata_height/4,m_yuvdata_width*m_yuvdata_height/4);yuvplaner_v.position(0);}//Clear//清除颜色设为黑色,把整个窗口清除为当前的清除颜色,glClear()的唯一参数表示需要被清除的缓冲区。GLES20.glClearColor(0.0f,0.0f,0.0f,1.0f);GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);//定义顶点数组,android平台要在这里做其他平台在initshader中,否则显示不出来图像GLES20.glVertexAttribPointer(ATTRIB_VERTEX, 2, GLES20.GL_FLOAT, false, 0, vertexVertices_buffer);//启用属性数组,android平台要在这里做其他平台在initshader中,否则显示不出来图像GLES20.glEnableVertexAttribArray(ATTRIB_VERTEX);//定义像素纹理数组,android平台要在这里做其他平台在initshader中,否则显示不出来图像GLES20. glVertexAttribPointer(ATTRIB_TEXTURE, 2, GLES20.GL_FLOAT, false, 0, textureVertices_buffer);//启用属性数组,android平台要在这里做其他平台在initshader中,否则显示不出来图像GLES20.glEnableVertexAttribArray(ATTRIB_TEXTURE);//显卡中有N个纹理单元(具体数目依赖你的显卡能力),每个纹理单元(GL_TEXTURE0、GL_TEXTURE1等)都有GL_TEXTURE_1D、GL_TEXTURE_2D等//Y//选择当前活跃的纹理单元GLES20.glActiveTexture(GLES20.GL_TEXTURE0);//允许建立一个绑定到目标纹理的有名称的纹理GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureid_y);//根据指定的参数,生成一个2D纹理(Texture)。相似的函数还有glTexImage1D、glTexImage3D。GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, m_yuvdata_width, m_yuvdata_height, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yuvplaner_y);GLES20.glUniform1i(m_textureUniformY, 0);     //设置纹理,按照前面设置的规则怎样将图像或纹理贴上(参数和选择的活跃纹理单元对应,GL_TEXTURE0)//UGLES20.glActiveTexture(GLES20.GL_TEXTURE1);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureid_u);GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, m_yuvdata_width/2, m_yuvdata_height/2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yuvplaner_u);GLES20.glUniform1i(m_textureUniformU, 1);//VGLES20.glActiveTexture(GLES20.GL_TEXTURE2);GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, m_textureid_v);GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE, m_yuvdata_width/2, m_yuvdata_height/2, 0, GLES20.GL_LUMINANCE, GLES20.GL_UNSIGNED_BYTE, yuvplaner_v);GLES20.glUniform1i(m_textureUniformV, 2);// Draw// 绘制GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);//单缓冲显示GLES20.glFlush();GLES20.glDisableVertexAttribArray(ATTRIB_VERTEX);GLES20.glDisableVertexAttribArray(ATTRIB_TEXTURE);return 1;}/*** create program and load shaders, fragment shader is very important.*/public int createProgram(String vertexSource, String fragmentSource) {// create shadersint vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);// just checkint program = GLES20.glCreateProgram();if (program != 0) {GLES20.glAttachShader(program, vertexShader);GLES20.glAttachShader(program, pixelShader);GLES20.glLinkProgram(program);int[] linkStatus = new int[1];GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);if (linkStatus[0] != GLES20.GL_TRUE) {GLES20.glDeleteProgram(program);program = 0;}}return program;}/*** create shader with given source.*/private 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){GLES20.glDeleteShader(shader);shader = 0;}}return shader;}/*** these two buffers are used for holding vertices, screen vertices and texture vertices.*/private void createBuffers(float[] vert, float[] coord) {vertexVertices_buffer = ByteBuffer.allocateDirect(vert.length * 4);vertexVertices_buffer.order(ByteOrder.nativeOrder());vertexVertices_buffer.asFloatBuffer().put(vert);vertexVertices_buffer.position(0);if (textureVertices_buffer == null) {textureVertices_buffer = ByteBuffer.allocateDirect(coord.length * 4);textureVertices_buffer.order(ByteOrder.nativeOrder());textureVertices_buffer.asFloatBuffer().put(coord);textureVertices_buffer.position(0);}}}
5:调用代码
        //GLSurfaceViewm_glsurfaceview = nsurfaceView;//opengl的view类pinterfaceOpenGL = new Lvs_OpenGl_Interface_Android(m_glsurfaceview);m_glsurfaceview.setRenderer(pinterfaceOpenGL);// 只有在绘制数据改变时才绘制viewm_glsurfaceview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
                //Opengl初始化ret = pinterfaceOpenGL.lvs_opengl_interface_init(m_glsurfaceview,m_opengl_width,m_opengl_height,opengl_displaycallback);if (ret < 0){return;}for (;;){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//渲染,带定时器,数据回调,及渲染时间回调,第一帧timer 40以后根据时间戳做调整pinterfaceOpenGL.lvs_opengl_interface_write(40);}
6:数据传入在回掉函数
ret= m_displaydatack.OpenGl_DisplayDataCallback(m_yuvbuf,m_millis_realtime);中
7:实现效果
本demo还需完善。如有错误请指正:

交流请加QQ群:62054820QQ:379969650.

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: