美文网首页iOS Tips扩展眼界OpenGL的使用
FFmpeg解码帧数据上传至OpenGL ES及GPU实现YUV

FFmpeg解码帧数据上传至OpenGL ES及GPU实现YUV

作者: 熊皮皮 | 来源:发表于2016-07-21 15:59 被阅读6199次

本文档描述了经FFmpeg解码得到的多个YUV格式或RGB格式数据上传至OpenGL ES及YUV转换RGB的办法。有关YUV转换RGB的描述可参考我另一个文档音视频开发:RGB与YUV相互转换问题

1、YUV

1.1、YUV420p

yuv420p三个通道分开,分别上传即可。

const uint8_t *pixels[] = { luma, cb, cr };
const int widths[]  = { frameWidth, frameWidth / 2, frameWidth / 2 };
const int heights[] = { frameHeight, frameHeight / 2, frameHeight / 2 };

for (int i = 0; i < 3; ++i) 
{       
    glBindTexture(GL_TEXTURE_2D, textures[i]);
        
    glTexImage2D(GL_TEXTURE_2D,
        0,
        GL_LUMINANCE,
        widths[i],
        heights[i],
        0,
        GL_LUMINANCE,
        GL_UNSIGNED_BYTE,
        pixels[i]);
        
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}

internalFormat指定为GL_LUMINANCE,同时纹理格式也为GL_LUMINANCE,像素数据结构为GL_UNSIGNED_BYTE。

Fragment Shader转换YUV至RGB的示例。

Video Range[16, 255]:

precision highp float;
varying   highp vec2 vv2_Texcoord;
uniform         mat3 um3_ColorConversion;
uniform   lowp  sampler2D us2_SamplerX;
uniform   lowp  sampler2D us2_SamplerY;
uniform   lowp  sampler2D us2_SamplerZ;

void main()
{
    mediump vec3 yuv;
    lowp    vec3 rgb;

    // Subtract constants to map the video range start at 0
    yuv.x = (texture2D(us2_SamplerX, vv2_Texcoord).r - (16.0 / 255.0));
    yuv.y = (texture2D(us2_SamplerY, vv2_Texcoord).r - 0.5);
    yuv.z = (texture2D(us2_SamplerZ, vv2_Texcoord).r - 0.5);
    rgb = um3_ColorConversion * yuv;
    gl_FragColor = vec4(rgb, 1);
}

对于Video Range,亮度通道在减去16.0/255.0后映射到Full Range。Full Range则城需此处理。

1.2、YUV420sp

yuv420sp因uv交错存储,故uv一起上传至GPU。

const uint8_t * pixels[] = { luma, cbcr };
const int widths[]  = { frameWidth, frameWidth / 2};
const int heights[] = { frameHeight, frameHeight / 2};

// Y
glBindTexture(GL_TEXTURE_2D, plane[0]);
glTexImage2D(GL_TEXTURE_2D,
             0,
             GL_RED_EXT,
             widths[0],
             heights[0],
             0,
             GL_RED_EXT,
             GL_UNSIGNED_BYTE,
             pixels[0]);
// UV
glBindTexture(GL_TEXTURE_2D, plane[1]);
glTexImage2D(GL_TEXTURE_2D,
             0,
             GL_RG_EXT,
             widths[1],
             heights[1],
             0,
             GL_RG_EXT,
             GL_UNSIGNED_BYTE,
             pixels[1]);

Fragment Shader(Video Range):

precision highp float;

in   highp vec2 vv2_Texcoord;

uniform         mat3 um3_ColorConversion;
uniform   lowp  sampler2D us2_SamplerX;
uniform   lowp  sampler2D us2_SamplerY;

out vec4 fragColor;

void main()
{
    mediump vec3 yuv;
    lowp    vec3 rgb;
    
    // Subtract constants to map the video range start at 0
    yuv.x  = (texture(us2_SamplerX,  vv2_Texcoord).r  - (16.0 / 255.0));
    yuv.yz = (texture(us2_SamplerY,  vv2_Texcoord).ra - vec2(0.5, 0.5));
    rgb = um3_ColorConversion * yuv;
    fragColor = vec4(rgb, 1);
}

对于iOS Video Toolbox,使用OpenGL ES 2.0,通过CVOpenGLESTextureCacheCreateTextureFromImage使用GL_RED_EXT和GL_RG_EXT宏创建纹理,那么,数据上传至GPU则按REG方式显示Y通道,如下所示。

YUV420SP_RED YUV420SP_RG YUV420SP_结果图像

使用OpenGL ES 3.0,则可用GL_LUMINANCE和GL_LUMINANCE_ALPHA宏创建纹理。

1.3、YUV444p10LE

const GLsizei widths[]    = { frameWidth / 2, frameWidth / 2, frameWidth / 2 };
const GLsizei heights[]   = { frameHeight, frameHeight, frameHeight };
for (int i = 0; i < 3; ++i)
{
    glBindTexture(GL_TEXTURE_2D, renderer->plane_textures[i]);
    
    glTexImage2D(GL_TEXTURE_2D,
                 0,
                 GL_LUMINANCE_ALPHA,
                 widths[i],
                 heights[i],
                 0,
                 GL_LUMINANCE_ALPHA,
                 GL_UNSIGNED_BYTE,
                 pixels[i]);
}

Fragment Shader(Video Range):

precision highp float;
varying   highp vec2 vv2_Texcoord;
uniform         mat3 um3_ColorConversion;
uniform   lowp  sampler2D us2_SamplerX;
uniform   lowp  sampler2D us2_SamplerY;
uniform   lowp  sampler2D us2_SamplerZ;

void main()
{
    mediump vec3 yuv_l;
    mediump vec3 yuv_h;
    mediump vec3 yuv;
    lowp    vec3 rgb;
    
    yuv_l.x = texture2D(us2_SamplerX, vv2_Texcoord).r;
    yuv_h.x = texture2D(us2_SamplerX, vv2_Texcoord).a;
    yuv_l.y = texture2D(us2_SamplerY, vv2_Texcoord).r;
    yuv_h.y = texture2D(us2_SamplerY, vv2_Texcoord).a;
    yuv_l.z = texture2D(us2_SamplerZ, vv2_Texcoord).r;
    yuv_h.z = texture2D(us2_SamplerZ, vv2_Texcoord).a;
    
    yuv = (yuv_l * 255.0 + yuv_h * 255.0 * 256.0) / (1023.0) - vec3(16.0 / 255.0, 0.5, 0.5);
    
    rgb = um3_ColorConversion * yuv;
    gl_FragColor = vec4(rgb, 1);
}

2、RGB

2.1、RGB888

glTexImage2D(GL_TEXTURE_2D,
             0,
             GL_RGB,
             width,
             height,
             0,
             GL_RGB,
             GL_UNSIGNED_BYTE,
             pixels);

Fragment Shader:

precision highp float;
varying   highp vec2 vv2_Texcoord;
uniform   lowp  sampler2D us2_SamplerX;

void main()
{
    gl_FragColor = vec4(texture2D(us2_SamplerX, vv2_Texcoord).rgb, 1);
}

RGB888、RGB565和RGBA共用同一份着色器代码。对于RGBA,可读取纹理自带的Alpha通道:

gl_FragColor = texture2D(us2_SamplerX, vv2_Texcoord);

2.2、RGBA

glTexImage2D(GL_TEXTURE_2D,
             0,
             GL_RGBA,
             width,
             height,
             0,
             GL_RGBA,
             GL_UNSIGNED_BYTE,
             pixels);

2.3、RGB565

glTexImage2D(GL_TEXTURE_2D,
             0,
             GL_RGB,
             width,
             height,
             0,
             GL_RGB,
             GL_UNSIGNED_SHORT_5_6_5,
             pixels);

问题:

FFmpeg解码H.264后,如何确认颜色转换矩阵是Full Range还是Video Range?
H.264的SPS有标识指出颜色转换矩阵,详情见我另一个文档FFmpeg源码调试:解析H.264 SPS(Sequence Parameter Set)中视频的颜色空间及其范围

相关文章

网友评论

  • 954e787214d7:楼主可以发一份代码给我吗?最近在基于chromium开发360度视频的浏览器,想要基于这个做一些验证和开发,希望能和你多多交流

    245207463@qq.com
  • 51a6ce05572b:楼主发一份代码给我吧,281209402@qq.com
    hongge372:大神,我也要源码。谢谢
    hongge372@126.com
  • 96f398e9704a:代码能共享下吗,谢谢
    eoollo@163.com
  • dca54441cfb0:大神,终于找到我需求的东西,可否发我一份YUV420sp的比较完整的代码,包括绘制的。谢谢。
    a15edae48dce:@熊皮皮 也发一份代码给我吧,谢谢了。692278151@qq.com
    dca54441cfb0:@熊皮皮 240598460@qq.com :smile:
    熊皮皮:@林大der 邮箱多少
  • 姜益达:楼主,请问UYVY4:2:2格式的数据应该怎么转成RGB呢?
    姜益达:@熊皮皮 好的吧 谢谢楼主!!😏👊🏻
    熊皮皮:@姜益达 没处理过UYVY格式,你到fourcc之类的网站找找吧
  • 一个人zy:楼主最后的问题解决了吗,你们是准备视频直播的功能吗
    熊皮皮:@arctanbell ES 2.0用RG,3.0可以用其他格式
    c872158e4aff:楼主,nv12 uv数据是两通道,可以使用GL_RGB传进去么?确定不是使用GL_RG?
    一个人zy:@熊皮皮 问下楼主,是不是iOS想研究深一点,必须的学习操作系统的相关知识啊,我发现关于异步绘制的时候会用到这里面的思想,看了YYKit作者的文章发现我缺少操作系统的知识,不知道楼主有没有什么很好的建议关于界面流畅优化这方面

本文标题:FFmpeg解码帧数据上传至OpenGL ES及GPU实现YUV

本文链接:https://www.haomeiwen.com/subject/yeubjttx.html