【问题标题】:How to convert RGBA to NV12 using OpenGL?如何使用 OpenGL 将 RGBA 转换为 NV12?
【发布时间】:2019-07-09 17:27:51
【问题描述】:

我需要使用 OpenGL 着色器作为编码器输入将 RGBA 转换为 NV12。

已经渲染了两个不同的片段着色器,两个纹理都来自一个相机。使用 v4l2 获取相机图像(YUV)。然后,将 YUV 转换为 RGB 让 OpenGL 渲染。下一步,我需要将 RGB 转换为 NV12 作为编码器输入,因为编码器只接受 NV12 格式。

【问题讨论】:

    标签: opengl shader yuv rgba nv12-nv21


    【解决方案1】:

    使用计算着色器将 RGB 转换为平面 YUV,然后将 UV 平面下采样两倍。

    这是计算着色器:

    #version 450 core
    layout(local_size_x = 32, local_size_y = 32) in;
    layout(binding = 0) uniform sampler2D src;
    layout(binding = 0) uniform writeonly image2D dst_y;
    layout(binding = 1) uniform writeonly image2D dst_uv;
    void main() {
        ivec2 id = ivec2(gl_GlobalInvocationID.xy);
        vec3 yuv = rgb_to_yuv(texelFetch(src, id).rgb);
        imageStore(dst_y, id, vec4(yuv.x,0,0,0));
        imageStore(dst_uv, id, vec4(yuv.yz,0,0));
    }
    

    有很多不同的 YUV 约定,我不知道您的编码器需要哪一个。因此,将上面的 rgb_to_yuv 替换为您的 YUV -> RGB 转换的倒数。

    然后进行如下操作:

    GLuint in_rgb = ...; // rgb(a) input texture
    int width = ..., height = ...; // the size of in_rgb
    
    GLuint tex[2]; // output textures (Y plane, UV plane)
    
    glCreateTextures(GL_TEXTURE_2D, tex, 2);
    glTextureStorage2D(tex[0], 1, GL_R8, width, height); // Y plane
    
    // UV plane -- TWO mipmap levels
    glTextureStorage2D(tex[1], 2, GL_RG8, width, height);
    
    // use this instead if you need signed UV planes:
    //glTextureStorage2D(tex[1], 2, GL_RG8_SNORM, width, height);
    
    glBindTextures(0, 1, &in_rgb);
    glBindImageTextures(0, 2, tex);
    glUseProgram(compute); // the above compute shader
    
    int wgs[3];
    glGetProgramiv(compute, GL_COMPUTE_WORK_GROUP_SIZE, wgs);
    glDispatchCompute(width/wgs[0], height/wgs[1], 1);
    
    glUseProgram(0);
    glGenerateTextureMipmap(tex[1]); // downsamples tex[1] 
    
    // copy data to the CPU memory:
    uint8_t *data = (uint8_t*)malloc(width*height*3/2);
    glGetTextureImage(tex[0], 0, GL_RED, GL_UNSIGNED_BYTE, width*height, data);
    glGetTextureImage(tex[1], 1, GL_RG, GL_UNSIGNED_BYTE, width*height/2,
        data + width*height);
    

    免责声明:

    • 此代码未经测试。
    • 假设宽度和高度可以被 32 整除。
    • 它可能在某处缺少内存屏障。
    • 这不是从 GPU 读取数据的最有效方法 - 您可能需要在计算下一帧时至少读取一帧。

    【讨论】:

    • 我想使用 YUV 约定作为 NV12 (YYYYYYYY UUVV)。所以我需要使用计算着色器来传播 Y 和 UV。最后我会得到两个纹理 Y 和 UV。但是我怎样才能将这两种纹理合二为一。就像我可以输出一个缓冲区或其他可以保存为NV12格式并可以在yuv播放器中成功显示的东西。
    • @JuliaDing:你的NV12消费者的界面是什么?它期待什么? GPU缓冲区? GPU纹理? CPU RAM 内存?
    • 它是CPU RAM内存。
    • @JuliaDing:我相应地对其进行了编辑。请注意,它给出了YYYYYYYYUVUV,这是 NV12 通常的意思。如果你需要YYYYYYYYUUVV,那么你需要三个纹理。
    猜你喜欢
    • 1970-01-01
    • 2020-05-13
    • 1970-01-01
    • 1970-01-01
    • 2021-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多