android opengl 播放 yuv数据
16lz
2021-01-26
ios opengl 播放 yuv数据:
http://blog.csdn.net/m0_37677536/article/details/78782501
这个播放是通过renderer的方式,iOS版的是通过EAGLContext的方式,总体流程是一样的:需要这么几个步骤:编写shader->编译shader->链成gpu程序(代码中的program)->分别创建yuv纹理对象->找到yuv纹理对象对应的显卡插槽(也就是要给gpu中运行的纹理对象传数据的地址)->给yuv纹理对象绑定数据->绘图。
因此iOS版的和android版的shader是完全一样的,opengl运行流程是一样的。
完整的播放器及源码下载:
http://blog.csdn.net/m0_37677536/article/details/78775007
这里说一下跟ios不一样的地方,就是要在java层继承GLRenderer,编写自己的绘图函数,如重写这三个方法:
@Override public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { InitOpenGL(); } @Override public void onSurfaceChanged(GL10 gl10, int i, int i1) { OnViewportChanged(i,i1); } @Override public void onDrawFrame(GL10 gl10) { RenderOneFrame(); }
InitOpenGL();
OnViewportChanged(i,i1);
RenderOneFrame();
这三个方法就是调用c++层的opengl的函数了。
还有要继承:GLSurfaceView 定义自己的GLView,并给自定义的GLView绑定自定义Renderer;
shader,跟ios版的完全一样:
unsigned char* FsStr(){ const char* fsstr = "varying lowp vec2 TexCoordOut;\ uniform sampler2D SamplerY;\ uniform sampler2D SamplerU;\ uniform sampler2D SamplerV;\ void main(void)\ {\ mediump vec3 yuv;\ lowp vec3 rgb;\ yuv.x = texture2D(SamplerY, TexCoordOut).r;\ yuv.y = texture2D(SamplerU, TexCoordOut).r - 0.5;\ yuv.z = texture2D(SamplerV, TexCoordOut).r - 0.5;\ rgb = mat3( 1, 1, 1,\ 0, -0.39465, 2.03211,\ 1.13983, -0.58060, 0) * yuv;\ gl_FragColor = vec4(rgb, 1);\ }"; return (unsigned char*)fsstr;}unsigned char* VsStr(){ const char* vsstr = "attribute vec4 position;\ attribute vec2 TexCoordIn;\ varying vec2 TexCoordOut;\ void main(void)\ {\ gl_Position = position;\ TexCoordOut = TexCoordIn;\ }"; return (unsigned char*)vsstr;}
glView.h
对外接口跟ios的完全一样:
//// Created by huizai on 2017/11/24.//#ifndef FFMPEG_DEMO_GLVIEW_H#define FFMPEG_DEMO_GLVIEW_H#include "JniDefine.h"void initGL();void setVideoSize(int width,int height);void displayYUV420pData(H264YUV_Frame * frame);/** 清除画面 */void clearFrame();void Render();void SetupYUVTextures();#endif
glView.cpp
//// Created by huizai on 2017/11/24.//#include "GlView.h"#include "GlUtils.h"typedef enum { TEXY = 0, TEXU, TEXV, TEXC};EGLContext *glContext;GLuint renderBuffer;GLuint program;GLuint positionHandle,texCoord;GLuint textureYUV[3];GLuint videoW,videoH;GLsizei viewScale;GLuint CompileShader(GLenum shaderType, const char*shaderCode){ GLuint shader = glCreateShader(shaderType); if (shader == 0){ printf("glCreateShader fail\n"); return 0; } if (shaderCode == nullptr){ glDeleteShader(shader); return 0; } glShaderSource(shader, 1, &shaderCode, nullptr); glCompileShader(shader); GLint compileResult = GL_TRUE; glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult); if (compileResult == GL_FALSE){ char errorBuf[1024] = { 0 }; GLsizei logLen = 0; glGetShaderInfoLog(shader, 1024, &logLen, errorBuf); printf("Compile Shader fail error log : %s \nshader code :\n%s\n", logLen, shaderCode); glDeleteShader(shader); shader = 0; } // delete shaderCode; return shader;}GLuint CreateProgram(GLuint vsShader,GLuint fsShader){ GLuint program = glCreateProgram(); glAttachShader(program,vsShader); glAttachShader(program,fsShader); glLinkProgram(program); glDetachShader(program,vsShader); glDetachShader(program,fsShader); GLint ret; glGetProgramiv(program,GL_LINK_STATUS,&ret); if (ret == GL_FALSE){ char errorBuf[1024] = {0}; GLsizei writed = 0; glGetProgramInfoLog(program,1024,&writed,errorBuf); printf("create gpu program fail,link error:%s\n",errorBuf); glDeleteProgram(program); program = 0; } return program;}void initGL(){ //glClearColor(0.1f, 0.4f, 0.6f, 1.0f); float data[] = { -0.2f,-0.2f,-0.6f,1.0f, 0.2f,-0.2f,-0.6f,1.0f, 0.0f,0.2f,-0.6f,1.0f }; SetupYUVTextures(); unsigned char* shaderCode = VsStr(); GLuint vsShader = CompileShader(GL_VERTEX_SHADER,(char*)shaderCode); // delete(shaderCode); shaderCode = FsStr(); GLuint fsShader = CompileShader(GL_FRAGMENT_SHADER,(char*)shaderCode); // delete(shaderCode); program = CreateProgram(vsShader,fsShader); glDeleteShader(vsShader); glDeleteShader(fsShader); positionHandle = (GLuint)glGetAttribLocation(program,"position"); texCoord = (GLuint)glGetAttribLocation(program,"TexCoordIn"); //像素数据对齐,第二个参数默认为4,一般为1或4 glPixelStorei(GL_UNPACK_ALIGNMENT, 4); //使用着色器 glUseProgram(program); //获取一致变量的存储位置 GLint textureUniformY = glGetUniformLocation(program, "SamplerY"); GLint textureUniformU = glGetUniformLocation(program, "SamplerU"); GLint textureUniformV = glGetUniformLocation(program, "SamplerV"); //对几个纹理采样器变量进行设置 glUniform1i(textureUniformY, 0); glUniform1i(textureUniformU, 1); glUniform1i(textureUniformV, 2); setVideoSize(0,0);}void SetupYUVTextures(){ if(textureYUV[TEXY]){ glDeleteTextures(3,textureYUV); } //生成纹理 glGenTextures(3,textureYUV); if (!textureYUV[TEXY] || !textureYUV[TEXU] || !textureYUV[TEXV]) { printf("<<<<<<<<<<<<纹理创建失败!>>>>>>>>>>>>"); return; } //选择当前活跃单元 glActiveTexture(GL_TEXTURE0); //绑定Y纹理 glBindTexture(GL_TEXTURE_2D, textureYUV[TEXY]); //纹理过滤函数 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);//放大过滤 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);//缩小过滤 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);//水平方向 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);//垂直方向 glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureYUV[TEXU]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, textureYUV[TEXV]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);}void displayYUV420pData(H264YUV_Frame * frame){ if (frame->width==0 || frame->height ==0) return; int w = frame->width; int h = frame->height; if (w != videoW || h != videoH){ setVideoSize(frame->width,frame->height); } //绑定 glBindTexture(GL_TEXTURE_2D,textureYUV[TEXY]); /** 更新纹理 @param target#> 指定目标纹理,这个值必须是GL_TEXTURE_2D。 description#> @param level#> 执行细节级别。0是最基本的图像级别,n表示第N级贴图细化级别 description#> @param xoffset#> 纹理数据的偏移x值 description#> @param yoffset#> 纹理数据的偏移y值 description#> @param width#> 更新到现在的纹理中的纹理数据的规格宽 description#> @param height#> 高 description#> @param format#> 像素数据的颜色格式, 不需要和internalformatt取值必须相同。可选的值参考internalformat。 description#> @param type#> 颜色分量的数据类型 description#> @param pixels#> 指定内存中指向图像数据的指针 description#> */ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)w, (GLsizei)h, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->luma.dataBuffer); glBindTexture(GL_TEXTURE_2D,textureYUV[TEXU]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)w/2, (GLsizei)h/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->chromaB.dataBuffer); glBindTexture(GL_TEXTURE_2D,textureYUV[TEXV]); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, (GLsizei)w/2, (GLsizei)h/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->chromaR.dataBuffer); //渲染 Render();}void Render(){ //把数据显示在这个视窗上 /* 我们如果选定(0, 0), (0, 1), (1, 0), (1, 1)四个纹理坐标的点对纹理图像映射的话,就是映射的整个纹理图片。如果我们选择(0, 0), (0, 1), (0.5, 0), (0.5, 1) 四个纹理坐标的点对纹理图像映射的话,就是映射左半边的纹理图片(相当于右半边图片不要了),相当于取了一张320x480的图片。但是有一点需要注意,映射的纹理图片不一定是“矩形”的。实际上可以指定任意形状的纹理坐标进行映射。下面这张图就是映射了一个梯形的纹理到目标物体表面。这也是纹理(Texture)比上一篇文章中记录的表面(Surface)更加灵活的地方。 */ glClearColor(0.0f,0.0f,0.0f,1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); static const GLfloat squareVertices[] = { -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, }; static const GLfloat coordVertices[] = { 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, }; //更新属性值 glVertexAttribPointer(positionHandle, 2, GL_FLOAT, 0, 0, squareVertices); //开启定点属性数组 glEnableVertexAttribArray(positionHandle); glVertexAttribPointer(texCoord, 2, GL_FLOAT, 0, 0, coordVertices); glEnableVertexAttribArray(texCoord); //绘制 //当采用顶点数组方式绘制图形时,使用该函数。该函数根据顶点数组中的坐标数据和指定的模式,进行绘制。 //绘制方式,从数组的哪一个点开始绘制(一般为0),顶点个数 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);}void setVideoSize(int width, int height){ videoH = (GLuint)height; videoW = (GLuint)width; //开辟内存空间 size_t length = (size_t)(width * height); void *blackDataY = malloc(length); void *blackDataU = malloc(length/4); void *blackDataV = malloc(length/4); if (blackDataY){ /** 对内存空间清零,作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法 @param __b#> 源数据 description#> @param __c#> 填充数据 description#> @param __len#> 长度 description#> @return <#return value description#> */ memset(blackDataY, 0x0, length); memset(blackDataU, 0x0, length/4); memset(blackDataV, 0x0, length/4); } //绑定Y纹理 glBindTexture(GL_TEXTURE_2D, textureYUV[TEXY]); /** 根据像素数据,加载纹理 @param target#> 指定目标纹理,这个值必须是GL_TEXTURE_2D。 description#> @param level#> 执行细节级别。0是最基本的图像级别,n表示第N级贴图细化级别 description#> @param internalformat#> 指定纹理中的颜色格式。可选的值有GL_ALPHA,GL_RGB,GL_RGBA,GL_LUMINANCE, GL_LUMINANCE_ALPHA 等几种。 description#> @param width#> 纹理的宽度 description#> @param height#> 高度 description#> @param border#> 纹理的边框宽度,必须为0 description#> @param format#> 像素数据的颜色格式, 不需要和internalformatt取值必须相同。可选的值参考internalformat。 description#> @param type#> 指定像素数据的数据类型。可以使用的值有GL_UNSIGNED_BYTE,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4,GL_UNSIGNED_SHORT_5_5_5_1等。 description#> @param pixels#> 指定内存中指向图像数据的指针 description#> @return <#return value description#> */ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width, height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, blackDataY); //绑定U纹理 glBindTexture(GL_TEXTURE_2D, textureYUV[TEXU]); //加载纹理 glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width/2, height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, blackDataU); //绑定V数据 glBindTexture(GL_TEXTURE_2D, textureYUV[TEXV]); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, width/2, height/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, blackDataV); //释放malloc分配的内存空间 free(blackDataY); free(blackDataU); free(blackDataV);}
更多相关文章
- SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
- 一句话锁定MySQL数据占用元凶
- android setTag()/getTag()应用
- Android:listView控件,adapter,以及其他控件的组合
- Android以请求参数来获取网络数据
- Android中Activity之间的数据传递(Intent和Bundle)
- android 读取 AndroidManifest.xml 中的数据:版本号、应用名称、
- Android(安卓)数据业务框架分析
- Activity中的onSaveInstanceState()解析