Android OpenGL ES 播放 yuv420p文件

(1)用 opengles shader (glsl语言)显示yuv文件,安卓中用EGL,EGL是opengl与窗口系统对应的适配层
![在这里插入图片描述](https://img-blog.csdn.net/20181011113007631?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTAxNDExNjA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
  (2)YUV格式中,“Y”表示明亮度,也就是灰度值,而“U”和“V” 表示的则是色度 

1,准备播放文件

ffmpeg -i 720.mp4 -pix_fmt yuv420p -s 432x640 out.yuv
生成的yuv文件上传到手机sd卡路径准备

2,创建工程,配置,创建Player

 (1)配置库

GLESv2
EGL
android

(2)全屏配置
  //去掉标题
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        //全屏.隐藏状态
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
        //横屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

(3)player

public class YUVPlayer extends GLSurfaceView implements  Runnable,SurfaceHolder.Callback
{

    public YUVPlayer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        new Thread(this).start();//回调run
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {

    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

    }


    @Override
    public void run() {//传GLSurfaceView地址进去

    Open("/sdcard/outCat.yuv",getHolder().getSurface());

    }


    public  native void Open(String url,Object surface);



}
   

3,EGL 渲染程序

  • (0)文件打开
  FILE *fp = fopen(url, "rb");
    if (!fp){
        LOGE(" open file %s failed !",url);
        return;
    }
  • (1)EGL配置
   //获取原始窗口
    ANativeWindow *nwin = ANativeWindow_fromSurface(env,surface);

    //------------------------
    //EGL
    //1  display 显示
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if (display == EGL_NO_DISPLAY){
        LOGE("get display failed!");
        return;
    }
    //初始化 后面两个参数是版本号
   if (EGL_TRUE != eglInitialize(display,0,0)){
       LOGE("eglInitialize failed!");
       return;
   }
  • (2)surface context
 //2  surface (关联原始窗口)
    //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))
    {
        LOGE("eglChooseConfig failed!");
        return;
    }
    //创建surface (关联原始窗口)
    EGLSurface  winSurface = eglCreateWindowSurface(display,config,nwin,0);

    if (winSurface == EGL_NO_SURFACE){
        LOGE("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){
        LOGE("eglCreateContext failed!");
        return;
    }
  • (3)egl 关联 openl
 if (EGL_TRUE !=   eglMakeCurrent(display,winSurface,winSurface,context))
    {
        LOGE("eglMakeCurrent failed!");
        return;
    }
    LOGE("EGL Init Success!");
  • (4)创建渲染程序
  GLint program = glCreateProgram();
    if (program == 0){
        LOGE("glCreateProgram failed!");
        return;
    }
    //向渲染程序中加入着色器
    glAttachShader(program,vsh);
    glAttachShader(program,fsh);
    //链接程序
    glLinkProgram(program);
    GLint status;
    glGetProgramiv(program,GL_LINK_STATUS,&status);
    if (status != GL_TRUE) {
        LOGE("glLinkProgram failed!");
        return;
    }
    //激活渲染程序
    glUseProgram(program);
    LOGE("glLinkProgram 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,
    };
    //获取shader中的顶点变量
    GLuint apos = (GLuint)glGetAttribLocation(program,"aPosition");
    glEnableVertexAttribArray(apos);
    //传递顶点
    /*
     * apos 传到哪
     * 每一个点有多少个数据
     * 格式
     * 是否有法线向量
     * 一个数据的偏移量
     * 12 顶点有三个值(x,y,z)float存储 每个有4个字节 每一个值的间隔是 3*4 =12
     * ver 顶点数据
     * */
    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_FLOAT,8,txts);

    //材质纹理初始化

    int width  = 432;//实际应用中从ffmpeg 获取
    int height = 640;


    //设置纹理层
    glUniform1i(glGetUniformLocation(program,"yTexture"),0);//对于纹理第1层
    glUniform1i(glGetUniformLocation(program,"uTexture"),1);//对于纹理第2层
    glUniform1i(glGetUniformLocation(program,"vTexture"),2);//对于纹理第3层

    //创建opengl纹理
    /*
     * 创建多少个纹理
     * 存放数组
     *
     * */
    GLuint texts[3] = {0};
    glGenTextures(3,texts); //创建3个纹理
    //设置纹理属性
    glBindTexture(GL_TEXTURE_2D,texts[0]);//绑定纹理,下面的属性针对这个纹理设置
    /*
     * GL_TEXTURE_2D 2D材质
     * GL_TEXTURE_MIN_FILTER 缩小的过滤
     * GL_LINEAR 线性差值 当前渲染像素最近的4个纹理做加权平均值
     *
     * */
    //缩小放大过滤器
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    //设置纹理的格式和大小
    /*
     * GL_TEXTURE_2D
     * 显示细节的级别
     * 内部gpu 格式 亮度 灰度图
     * 宽
     * 高
     * 边框
     * 数据的像素格式
     * 像素的数据类型
     * 纹理数据
     * */
    glTexImage2D(GL_TEXTURE_2D,
                 0,//默认
                 GL_LUMINANCE,
                 width, height, //尺寸要是2的次方  拉升到全屏
                 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,//默认
                 GL_LUMINANCE,
                 width/2, height/2, //尺寸要是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,//默认
                 GL_LUMINANCE,
                 width/2, height/2, //尺寸要是2的次方  拉升到全屏
                 0,
                 GL_LUMINANCE,//数据的像素格式,要与上面一致
                 GL_UNSIGNED_BYTE,// 像素的数据类型
                 NULL
    );
  • (6)纹理的修改和显示
 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 < 100000; ++i) {

//        memset(buf[0],i,width*height);
//        memset(buf[1],i,width*height/4);
//        memset(buf[2],i,width*height/4);

        // 420p yyyyyy uu vv
        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]);//绑定纹理

        //替换纹理内容
        /*
         * GL_TEXTURE_2D
         * 细节级别
         * 偏移位置yoffset
         * 偏移位置xoffset
         * 宽
         * 高
         * 数据格式
         * 存储搁置
         * 纹理数据写入buf中
         *
         * */
        glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf[0]);


        //激活第2层纹理 绑定到创建的opengl纹理
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D,texts[1]);//绑定纹理
        glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width/2,height/2,GL_LUMINANCE,GL_UNSIGNED_BYTE,buf[1]);



        //激活第1层纹理 绑定到创建的opengl纹理
        glActiveTexture(GL_TEXTURE2);
        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);//从0顶点开始 一共4个顶点

        //窗口显示
        eglSwapBuffers(display,winSurface);//交换buf


    }

3,着色器

//顶点着色器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;//显示顶点
    }
);

//片元着色器 软解码和部分x86硬解码 YUV420P
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.39425,2.03211,
                     1.13983,-0.5806,0.0)*yuv;
        //输出像素颜色
        gl_FragColor = vec4(rgb,1.0);
    }


);


//初始化着色器
GLint InitShader(const char *code,GLint type)
{
    //创建shader
    GLint sh =  glCreateShader(type);
    if (sh == 0 ){
        LOGE(" glCreateShader  %d failed ! ",type);
        return 0;
    }
    //加载shader
    glShaderSource(
            sh,
            1,//shader数量
            &code,//shader代码
            0);//代码长度 传0自动查找计算

    //编译shader 显卡
    glCompileShader(sh);

    //获取编译情况
    GLint status;
    glGetShaderiv(sh,GL_COMPILE_STATUS,&status);
    if (status == 0){
        LOGE(" GL_COMPILE_STATUS   failed ! ");
        return 0;
    }
    LOGE(" init shader   success ! ");
    return sh;
}

demo

你可能感兴趣的:(OpenGL,ES,OpenGL,ES,Android)