如何使用 OpenGL 将 RGBA 转换为 NV12?
How to convert RGBA to NV12 using OpenGL?
我需要使用 OpenGL 着色器作为编码器输入将 RGBA 转换为 NV12。
已经渲染了两个不同的片段着色器,两个纹理都来自一个相机。使用 v4l2 获取相机图像(YUV)。然后,将 YUV 转换为 RGB 让 OpenGL 渲染。下一步,我需要将 RGB 转换为 NV12 作为编码器输入,因为编码器只接受 NV12 格式。
使用计算着色器将 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 中读取数据的最有效方式——您可能需要在计算下一帧时至少读取一帧。
我需要使用 OpenGL 着色器作为编码器输入将 RGBA 转换为 NV12。
已经渲染了两个不同的片段着色器,两个纹理都来自一个相机。使用 v4l2 获取相机图像(YUV)。然后,将 YUV 转换为 RGB 让 OpenGL 渲染。下一步,我需要将 RGB 转换为 NV12 作为编码器输入,因为编码器只接受 NV12 格式。
使用计算着色器将 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 中读取数据的最有效方式——您可能需要在计算下一帧时至少读取一帧。