Android播放器(三) 使用OpenGL ES播放yuv数据

本文主要介绍通过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);
    }
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值