【问题标题】:Converting RGBA to ARGB (glReadPixels -> AVAssetWriter)将 RGBA 转换为 ARGB (glReadPixels -> AVAssetWriter)
【发布时间】:2012-02-04 23:41:50
【问题描述】:

我想在AVAssetWriter 的帮助下将使用 OpenGL 渲染的图像记录到电影文件中。问题出现了,从 OpenGL 帧缓冲区访问像素的唯一方法是使用 glReadPixels,它仅支持 iOS 上的 RGBA 像素格式。但是AVAssetWriter 不支持这种格式。在这里,我可以使用 ARGB 或 BGRA。由于可以忽略 alpha 值,我得出的结论是,将 RGBA 转换为 ARGB 的最快方法是为 glReadPixels 提供移动一个字节的缓冲区:

UInt8 *buffer = malloc(width*height*4+1);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer+1);

问题是,glReadPixels 调用会导致EXC_BAD_ACCESS 崩溃。如果我不将缓冲区移动一个字节,它就可以完美地工作(但显然视频文件中的颜色错误)。这里有什么问题?

【问题讨论】:

  • 你不能改为分配 size+4,将 buffer+4 发送到 glReadPixels 调用,然后将 buffer+3 发送到 AVAssetWriter 吗?这将使 GL 调用保持一致,这可能是 iOS 的要求。

标签: ios opengl-es glreadpixels


【解决方案1】:

我得出的结论是,将 RGBA 转换为 ARGB 的最快方法是为 glReadPixels 提供移动一个字节的缓冲区

不过,这也会将您的 alpha 值移动 1 个像素。这是另一个建议:

将图片渲染为纹理(使用带有该纹理的 FBO 作为颜色附件)。接下来将该纹理渲染到另一个帧缓冲区,使用 swizzling 片段着色器:

#version ...
uniform sampler2D image;
uniform vec2 image_dim;
void main() 
{
    // we want to address texel centers by absolute fragment coordinates, this
    // requires a bit of work (OpenGL-ES SL doesn't provide texelFetch function).
    gl_FragColor.rgba = 
        texture2D(image, vec2( (2*gl_FragCoord.x + 1)/(2*image_dim.y), 
                               (2*gl_FragCoord.y + 1)/(2*image_dim.y) )
        ).argb; // this swizzles RGBA into ARGB order if read into a RGBA buffer
}

【讨论】:

  • 正如我所提到的,我不需要 alpha 信息(我正在制作一个 h264 视频)。但是在 OpenGL 着色器中调动是个好主意! :)
  • 顺便说一句。我刚刚发现了一些非常有趣的东西:在指定 BGRA 而不是 ARGB 时,AVAssetWriter 编码视频的速度要快得多(!)。所以整个问题实际上是关于转换 RGBA->BGRA 而不是 RGBA->ARGB 并且这种像素混合技术是绝对强制性的,因为我提出的单字节移位技术不能转换 RGBA->BGRA!
  • @DominikSeibold 请为您的实现发布一些示例代码
【解决方案2】:

尝试调用

glPixelStorei(GL_PACK_ALIGNMENT,1) 

在 glReadPixels 之前。

来自docs

GL_PACK_ALIGNMENT

指定内存中每个像素行开头的对齐要求。 允许的值为 1(字节对齐), 2(与偶数字节对齐的行), 4(字对齐),和 8(行从双字边界开始)。

默认值为 4(请参阅glGet)。这经常在各种“OpenGL pitfalls”类型lists 中被称为麻烦制造者,尽管这通常与其行填充效果有关,而不是缓冲区对齐。

作为一种替代方法,如果您 malloc 4 个额外字节,将 glReadPixels 从缓冲区 + 4 开始以 4 字节对齐,然后传递您的 AVAssetWriter 缓冲区 + 3,会发生什么(尽管我不知道 AVAssetWriter 是否更多容忍对齐问题)?

【讨论】:

  • glPixelStorei 没有帮助。您的第二个解决方案有效,但需要额外的 memcpy 步骤,这对性能不利。
【解决方案3】:

您需要通过执行 memcpy 或其他复制操作来移动字节。修改指针会使它们不对齐,这可能在也可能不在任何底层硬件的能力范围内(DMA 总线宽度、切片粒度等)

【讨论】:

  • 我看不出 glReadPixels 不能写入未对齐指针的任何原因。它可能不是最有效的,但它真的不应该崩溃......
【解决方案4】:

使用buffer+1 将意味着数据不是在您的 malloc 内存的开头写入,而是一个字节,因此它将写入您的 malloc 内存的末尾,从而导致崩溃。

如果 iOS 的 glReadPixels 只接受 GL_RGBA,那么我认为你必须自己重新安排它们。

更新,抱歉我错过了你的 malloc 中的 +1,StilesCrisis 可能是正确的崩溃原因。

【讨论】:

  • 不过,他在分配的缓冲区中添加了一个额外的字节。
【解决方案5】:

如果您在缓冲区末尾添加额外的 128 字节 slack,会发生什么情况?可能是 OpenGL 试图一次填充 4/8/16/etc 字节以提高性能,并且在缓冲区未对齐或其他情况下出现错误。这不是 OpenGL 中的性能优化第一次在边缘情况下出现问题 :)

【讨论】:

  • 我什至尝试将缓冲区大小加倍,但它仍然崩溃。如果我将它移动 4 个字节或一行(4*width)它可以工作,但这当然对初始问题没有帮助。
  • 在你的 malloc 之后,尝试添加 NSLog(@"Buffer at 0x%08X", buffer); .然后查看 EXC_BAD_ACCESS 出现的地址。如果错误的访问发生在缓冲区的最开始,那么可能 OpenGL 正在以一种在 ARM 上的未对齐访问时崩溃的方式写入内存。我对 ARM 的了解还不够,无法知道它未对齐时会发生什么故障(我的内心更像是一个 Mac OS 人)。当然,如果它发生在其他地方,它仍然是一个有趣的数据点……
  • 如何获取EXC_BAD_ACCESS出现的地址?
  • 对不起,我不知道你在说哪个崩溃日志。这就是我所看到的,当崩溃发生时:dominik.ws/screenshot.png
  • 您正在调试器中运行,因此不会生成崩溃日志。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多