android opengl 播放 yuv数据
程序员文章站
2022-03-29 14:06:55
...
ios opengl 播放 yuv数据:
http://blog.****.net/m0_37677536/article/details/78782501
这个播放是通过renderer的方式,iOS版的是通过EAGLContext的方式,总体流程是一样的:需要这么几个步骤:编写shader->编译shader->链成gpu程序(代码中的program)->分别创建yuv纹理对象->找到yuv纹理对象对应的显卡插槽(也就是要给gpu中运行的纹理对象传数据的地址)->给yuv纹理对象绑定数据->绘图。
因此iOS版的和android版的shader是完全一样的,opengl运行流程是一样的。
完整的播放器及源码下载:
http://blog.****.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);
}
推荐阅读
-
Android 使用Vitamio打造自己的万能播放器(4)——本地播放(快捷搜索、数据存储)
-
Android 使用Vitamio打造自己的万能播放器(4)——本地播放(快捷搜索、数据存储)
-
Android实现语音数据实时采集、播放
-
Android实现语音数据实时采集、播放
-
Android手机通过rtp发送aac数据给vlc播放的实现步骤
-
OpenGL ES for Android 播放视频
-
Android OpenGL YUV 旋转花屏解决及Camera获取图像
-
Android万能播放器(基于FFmpeg+OpenSL+OpenGL+Mediacodec)
-
Android万能视频播放器03-获取视频AVFrame的YUV数据
-
Android万能视频播放器02-获取视频AVFrame的帧数据