本文主要介绍通过OpenGL ES来播放yuv数据
项目地址OpenGl-Play-YUV
java层的代码主要是创建用于显示的SurfaceView,然后在子线程中将YUV的文件路径和SurfaceView所持有的Surface传递给native层。
public class PlayView extends GLSurfaceView implements Runnable, SurfaceHolder.Callback,GLSurfaceView.Renderer {
public PlayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public void run() {
// 将YUV的文件路径和Surface传递给native层
open("/storage/emulated/0/out.yuv", getHolder().getSurface());
}
public void startPlay() {
new Thread(this).start();
}
public native void open(String url, Surface surfaceView);
@Override
public void surfaceCreated(SurfaceHolder holder) {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
setRenderer(this);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {}
@Override
public void onDrawFrame(GL10 gl) { }
}
以下是native层代码
主要步骤
1 EGL初始化配置
2 顶点和片元着色器代码
3 shader初始化
4 创建渲染程序
5 传递顶点坐标和纹理坐标
6 设置纹理属性
7纹理的修改和显示
1 EGL初始化配置
//打开文件
const char *url = env->GetStringUTFChars(url_, 0);
LOGD("open url is s%", url);
FILE *fp = fopen(url, "rb");
if(!fp){
LOGD("open file %s failed!", url);
return;
}
//获取原始窗口
ANativeWindow *nwin = ANativeWindow_fromSurface(env, surfaceView);
// 1 EGL dispaly创建和初始化
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if(display == EGL_NO_DISPLAY)
{
LOGD("eglGetDisplay failed");
return;
}
//2 surface窗口配置
//输出配置
EGLConfig config;
EGLint configNum;
EGLint configSpec[] = {
EGL_RED_SIZE,8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_SURFACE_TYPE,EGL_WINDOW_BIT,EGL_NONE
};
if(EGL_TRUE != eglChooseConfig(display, configSpec, &config, 1, &configNum)){
LOGD("eglChooseConfig failed!");
return;
}
//创建surface
EGLSurface winsurface = eglCreateWindowSurface(display, config, nwin, 0);
if(winsurface == EGL_NO_SURFACE){
LOGD("eglCreateWindowSurface failed!");
return;
}
//3 context 创建关联的上下文
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);
if(context == EGL_NO_CONTEXT){
LOGD("eglCreateContext failed!");
return;
}
if(EGL_TRUE != eglMakeCurrent(display, winsurface, winsurface, context)){
LOGD("eglMakeCurrent failed!");
return;
}
LOGD("EGL Init Success!");
2 顶点和片元着色器代码
//顶点着色器glsl
#define GET_STR(x) #x
static const char *vertexShader = GET_STR(
attribute vec4 aPosition; //顶点坐标
attribute vec2 aTexCoord; //材质顶点坐标
varying vec2 vTexCoord; //输出的材质坐标
void main(){
vTexCoord = vec2(aTexCoord.x, 1.0-aTexCoord.y);
gl_Position = aPosition;
}
);
static const char *fragYUV420P = GET_STR(
precision mediump float; //精度
varying vec2 vTexCoord; //顶点着色器传递的坐标
uniform sampler2D yTexture; //输入的材质 (不透明度, 单像素)
uniform sampler2D uTexture;
uniform sampler2D vTexture;
void main(){
vec3 yuv;
vec3 rgb;
yuv.r = texture2D(yTexture,vTexCoord).r;
yuv.g = texture2D(uTexture,vTexCoord).r- 0.5;
yuv.b = texture2D(vTexture,vTexCoord).r - 0.5;
rgb = mat3(1.0, 1.0, 1.0,
0.0, -0.39465, 2.03211,
1.13983, -0.58060, 0.0)*yuv;
//输出像素颜色
gl_FragColor = vec4(rgb,1.0);
}
);
3 shader初始化
//顶点shader初始化
GLint vsh = InitShader(vertexShader, GL_VERTEX_SHADER);
//片元yuv420 shader初始化
GLint fsh = InitShader(fragYUV420P, GL_FRAGMENT_SHADER);
GLint InitShader(const char *const code, GLint type){
//创建shader
GLint sh = glCreateShader(type);
if(sh == 0){
LOGD("glCreateShader %d failed!", type);
return 0;
}
//加载shader
glShaderSource(sh,
1, //shader数量
&code, //shader代码
0); //代码长度
//编译shader
glCompileShader(sh);
//获取编译情况
GLint status;
glGetShaderiv(sh, GL_COMPILE_STATUS, &status);
if(status == 0){
LOGD("glCompileShader failed");
return 0;
}
LOGD("glCompileShader success");
return sh;
}
4 创建渲染程序
//创建渲染程序
GLint program = glCreateProgram();
if(program == 0){
LOGD("glCreateProgram failed!");
return;
}
//创建渲染程序中加入着色器代码
glAttachShader(program, vsh);
glAttachShader(program, fsh);
//链接程序
glLinkProgram(program);
GLint status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if(status != GL_TRUE){
LOGD("glLinkProgram failed");
return;
}
glUseProgram(program);
LOGD("glUseProgram success");
5 传递顶点坐标和纹理坐标
//加入三维顶点数据 两个三角形组成正方形
static float vers[] = {
1.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f
};
GLuint apos = glGetAttribLocation(program, "aPosition");
glEnableVertexAttribArray(apos);
//传递顶点
glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 12, vers);
//加入材质坐标数据
static float txts[] = {
1.0f, 0.0f, //右下
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
GLuint atex = (GLuint)glGetAttribLocation(program, "aTexCoord");
glEnableVertexAttribArray(atex);
glVertexAttribPointer(atex, 2, GL_FLOAT,GL_FALSE, 8, txts);
int width = 1920;
int height = 1080;
//材质纹理初始化
//设置纹理层
glUniform1i( glGetUniformLocation(program, "yTexture"), 0);//对于纹理第1层
glUniform1i( glGetUniformLocation(program, "uTexture"), 1);//对于纹理第1层
glUniform1i( glGetUniformLocation(program, "vTexture"), 2);//对于纹理第1层
//创建opengl纹理
GLuint texts[3] = {0};
//创建三个纹理
glGenTextures(3, texts);
6 设置纹理属性
//设置纹理属性
glBindTexture(GL_TEXTURE_2D, texts[0]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//设置纹理格式和大小
glTexImage2D(GL_TEXTURE_2D,
0, //细节处理 0默认
GL_LUMINANCE, //gpu内部格式 亮度, 灰度图
width,height, //拉升到全屏
0, //边框
GL_LUMINANCE, //数据的像素格式 亮度, 灰度图 要与上面一直
GL_UNSIGNED_BYTE, //像素数据类型
NULL); //纹理的数据
//设置纹理属性
glBindTexture(GL_TEXTURE_2D, texts[1]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//设置纹理格式和大小
glTexImage2D(GL_TEXTURE_2D,
0, //细节处理 0默认
GL_LUMINANCE, //gpu内部格式 亮度, 灰度图
width/2,height/2, //拉升到全屏
0, //边框
GL_LUMINANCE, //数据的像素格式 亮度, 灰度图 要与上面一直
GL_UNSIGNED_BYTE, //像素数据类型
NULL); //纹理的数据
//设置纹理属性
glBindTexture(GL_TEXTURE_2D, texts[2]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//设置纹理格式和大小
glTexImage2D(GL_TEXTURE_2D,
0, //细节处理 0默认
GL_LUMINANCE, //gpu内部格式 亮度, 灰度图
width/2,height/2, //拉升到全屏
0, //边框
GL_LUMINANCE, //数据的像素格式 亮度, 灰度图 要与上面一直
GL_UNSIGNED_BYTE, //像素数据类型
NULL); //纹理的数据
7纹理的修改和显示
unsigned char *buf[3] = {0};
buf[0] = new unsigned char[width*height];
buf[1] = new unsigned char[width*height/4];
buf[2] = new unsigned char[width*height/4];
for(int i = 0; i<10000; i++)
{
/*memset(buf[0], i, width*height);
memset(buf[1], i, width*height/4);
memset(buf[2], i, width*height/4);*/
if(feof(fp) == 0)
{
fread(buf[0], 1, width*height, fp);
fread(buf[1], 1, width*height/4, fp);
fread(buf[2], 1, width*height/4, fp);
}
//激活第1层纹理, 绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texts[0]);
//替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[0]);
//激活第2层纹理, 绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE0+1);
glBindTexture(GL_TEXTURE_2D, texts[1]);
//替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[1]);
//激活第3层纹理, 绑定到创建的opengl纹理
glActiveTexture(GL_TEXTURE0+2);
glBindTexture(GL_TEXTURE_2D, texts[2]);
//替换纹理内容
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[2]);
//三维绘制
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//窗口显示
eglSwapBuffers(display, winsurface);
}