zoukankan      html  css  js  c++  java
  • Display yuv formatted video by shader

    版权声明  此文版权归作者Chen Shun所有。欢迎非营利性转载,转载时必须包含原始链接http://chenshun87.blog.163.com/blog/,且必须包含此版权声明的完整内容

    前一段时间学习了FFMpeg+GLSL显示YUV图像的方法,现在总结如下:流程:抽取YUV分量----->发送到shader--->在shaer中进行YUV到RGB的转换---->贴图 本文旨在讲述如果运用GPU来处理YUV到RGB的转换,其中涉及的GL ES的具体的细节知识,网上很多 这里不在赘述.
    1.分别抽取Y U V分量
    FFMpeg解码得出的YUV数据保存在AVFrame的data数组中, 在解出一帧的时候AVContext的宽度和高度表示图像的宽高.我们假设解码后的数据格式是YUV420(YV12).
    首先定义这样一个数据结构用于存储解码后的数据

    #define MAX_PLANES  3

    typedef unsigned char   BYTE;

    typedef struct _DVDVideoPicture

    {

        BYTE* data[4];      // [4] = alpha channel, currently not used

        int iLineSize[4];   // [4] = alpha channel, currently not used

    }DVDVideoPicture;


    YV12Image这个结构体用于保存抽取的Y U V数据

    typedef struct YV12Image

    {

        BYTE     *planeData[MAX_PLANES];

        int      planeSize[MAX_PLANES];

        unsigned stride[MAX_PLANES];

        unsigned width;

        unsigned height;

        unsigned flags;

        

        unsigned cshift_x; /* this is the chroma shift used */

        unsigned cshift_y;

    }YV12Image;

    可能有人要问了, DVDVideoPicture不是已经保存了 Y U V数据了吗,干嘛又定义一个YV12Image来拷贝一份呢?理由很简单,ffmpeg解码得出的图像右侧可能会有一段padding区域,所以DVDVideoPicture的iLineSize并不表示图像的宽度, AVContext的width才是图像的宽,并且 对应Y分量iLineSize[0] = pContext->width + padding;其中pContext AVContext类型, UV类似.那么YV12Image就是保存从DVDVideoPicture中出去pContext->width的图像数据.下面看具体的实现:


    定义如下的结构体

    用于保存YUV分别对应的texture结构体

    typedef struct _YUVPLANE

    {

        GLuint ID;//texture的ID

        

        unsigned texwidth;

        unsigned texheight;


    }YUVPLANE;


    typedef  YUVPLANE        YUVPLANES[MAX_PLANES];


    typedef struct YUVBUFFER

    {

        YV12Image image;// YUV源数据

        YUVPLANES  planes;// YUV对应的texture

    }YUVBUFFER;


    //////////////////////////////////////////////////////////////////

    YUVBUFFER _yuvbuffer;


    在解出一帧 的时候调用如下函数 初始化_yuvbuffer

    - (void)configure:(unsigned int)width height:(unsigned int)height

    {

        _sourceWidth = width;

        _sourceHeight = height;

        

        if (!_bConfiged)

        {

            YV12Image &im = _yuvbuffer.image;       // YUV对应的参数

            YUVPLANES &planes = _yuvbuffer.planes;  // YUV对应的纹理

            

            im.width = _sourceWidth;

            im.height = _sourceHeight;

            im.cshift_x = 1;

            im.cshift_y = 1;

            

            im.stride[0] = im.width;

            im.stride[1] = im.width >> im.cshift_x;

            im.stride[2] = im.width >> im.cshift_x;

            

            im.planeSize[0] = im.stride[0] * im.height;

            im.planeSize[1] = im.stride[1] * (im.height >> im.cshift_y);

            im.planeSize[2] = im.stride[2] * (im.height >> im.cshift_y);

            

            for (int i = 0; i < 3; i++)

            {

                im.planeData[i] = new BYTE[im.planeSize[i]];

                int shift = (i == 0) ? 0 : 1;

                planes[i].texwidth = im.width >> shift;

                planes[i].texheight = im.height >> shift;

            }

    _bConfiged = YES;

        }

    }

    如果对这个代码有困惑的需要先了解下YUV的数据格式.

    接下来就是Y U V数据的抽取

    - (void)copyPictureFrom:(DVDVideoPicture *)pic

    {

        YV12Image &img = _yuvbuffer.image;

        BYTE *s = pic->data[0];

        BYTE *d = img.planeData[0];

        int w = pic->iWidth;

        int h = pic->iHeight;

        if ( (w == pic->iLineSize[0]) && ((unsigned int) pic->iLineSize[0] == img.stride[0]))

        {

            fast_memcpy(d, s, w*h);

        }

        else

        {

            for (int y = 0; y < h; y++)

            {

                fast_memcpy(d, s, w);

                s += pic->iLineSize[0]; // iLineSize padding

                d += img.stride[0];

            }

        }

        s = pic->data[1];

        d = img.planeData[1];

        w = pic->iWidth >> 1;

        h = pic->iHeight >> 1;

        if ( (w == pic->iLineSize[1]) && ((unsigned int) pic->iLineSize[1] == img.stride[1]))

        {

            fast_memcpy(d, s, w*h);

        }

        else 

        {

            for (int y = 0; y < h; y++)

            {

                fast_memcpy(d, s, w);

                s += pic->iLineSize[1];

                d += img.stride[1];

            }

        }

        s = pic->data[2];

        d = img.planeData[2];

        if ((w==pic->iLineSize[2]) && ((unsigned int) pic->iLineSize[2]==img.stride[2]))

        {

            fast_memcpy(d, s, w*h);

        }

        else

        {

            for (int y = 0; y < h; y++)

            {

                fast_memcpy(d, s, w);

                s += pic->iLineSize[2];

                d += img.stride[2];

            }

        }

       

        @synchronized(glContext) {

            if (glContext) {

                [EAGLContext setCurrentContext:glContext];

         [self uploadYV12Texture];

                glFlush();

                [EAGLContext setCurrentContext:nil];

            }

        }

    }


    2.以上工作完成了Y U V数据的抽取,接下来就是把数据发送给GL ES,用到了OpenGL ES的纹理加载,感兴趣的可以去查找下纹理加载方法, 这里不再赘述, 需要主要的是Y U V对应三个不同的texture.


    3.GLSL处理YUV-->RGB的转换(重点)


    3.1先看顶点着色器process.vsh的实现:


    attribute vec4 position;

    attribute mediump vec4 textureCoordinate;

    varying mediump vec2 coordinate;


    void main()

    {

    gl_Position = position;

    coordinate = textureCoordinate.xy;

    }



    3.2 再就是片段着色器的process.fsh的实现

    precision mediump float;

    uniform sampler2D SamplerY;

    uniform sampler2D SamplerU;

    uniform sampler2D SamplerV;

    varying highp vec2 coordinate;


    void main()

    {

        mediump vec3 yuv;

        lowp vec3 rgb;

        

        yuv.x = texture2D(SamplerY, coordinate).r;

        

        yuv.y = texture2D(SamplerU, coordinate).r - 0.5;

        

        yuv.z = texture2D(SamplerV, coordinate).r - 0.5;

        

        rgb = mat3(      1,       1,       1,

                   0, -.214822.12798,

                   1.28033, -.38059,       0) * yuv;

      

        gl_FragColor = vec4(rgb, 1);

    }


    4.纹理贴图(不在赘述)

  • 相关阅读:
    Atitit 集团与个人的完整入口列表 attilax的完整入口 1. 集团与个人的完整入口列表 1 2. 流量入口概念 2 3. 流量入口的历史与发展 2 1.集团与个人的完整入口列表
    atitit 每季度日程表 每季度流程 v3 qaf.docx Ver history V2 add diary cyar data 3 cate V3 fix detail 3cate ,
    Atitit react 详细使用总结 绑定列表显示 attilax总结 1. 前言 1 1.1. 资料数量在百度内的数量对比 1 1.2. 版本16 v15.6.1 1 1.3. 引入js 2
    Atitit r2017 r3 doc list on home ntpc.docx
    Atitit r2017 ra doc list on home ntpc.docx
    Atiitt attilax掌握的前后技术放在简历里面.docx
    Atitit q2016 qa doc list on home ntpc.docx
    Atitit r7 doc list on home ntpc.docx 驱动器 D 中的卷是 p2soft 卷的序列号是 9AD0D3C8 D:\ati\r2017 v3 r01\
    Atitit 可移植性之道attilax著
    Atitit q2016 q5 doc list on home ntpc.docx
  • 原文地址:https://www.cnblogs.com/weinyzhou/p/2750067.html
Copyright © 2011-2022 走看看