将纹理复制到像素缓冲区(CVPixelBufferRef)

3
我正在使用一个只提供纹理对象整数id的API,并且我需要将该纹理的数据传递给AVAssetWriter以创建视频。我知道如何从像素缓冲区(CVPixelBufferRef)创建CVOpenGLESTexture对象,但在我的情况下,我必须以某种方式复制仅具有ID的纹理的数据。换句话说,我需要将一个opengl纹理复制到基于像素缓冲区的纹理对象中。这是否可能?如果是,则如何实现?在我的示例代码中,我有类似以下的内容:
   void encodeFrame(Gluint textureOb)
    {

    CVPixelBufferPoolCreatePixelBuffer (NULL, [assetWriterPixelBufferAdaptor pixelBufferPool], &pixelBuffer[0]);

    CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault, coreVideoTextureCache, pixelBuffer[0],
                                                          NULL, // texture attributes
                                                          GL_TEXTURE_2D,
                                                          GL_RGBA, // opengl format
                                                          (int)FRAME_WIDTH,
                                                          (int)FRAME_HEIGHT,
                                                          GL_BGRA, // native iOS format
                                                          GL_UNSIGNED_BYTE,
                                                          0,
                                                          &renderTexture[0]);


    CVPixelBufferLockBaseAddress(pixelBuffer[pixelBuffernum], 0);

//Creation of textureOb is not under my control. 
//All I have is the id of texture. 
//Here I need the data of textureOb somehow be appended as a video frame. 
//Either by copying the data to pixelBuffer or somehow pass the textureOb to the Adaptor.

    [assetWriterPixelBufferAdaptor appendPixelBuffer:pixelBuffer[0] withPresentationTime:presentationTime];

    }

感谢您的提示和回答。
P.S. 在iOS上无法使用glGetTexImage。
更新:
@Larson博士,我无法为API设置纹理ID。 事实上,我不能要求第三方API使用我自己创建的纹理对象。
经过查看答案,我了解到需要做以下几点:
1- 将像素缓冲器关联的纹理对象作为颜色附件附加到texFBO。 对于每个帧:
2- 绑定从API获取的纹理
3- 绑定texFBO并调用drawElements
这段代码有什么问题吗?
P.S. 我还不熟悉着色器,所以现在很难利用它们。
更新2:
在Brad Larson的答案和使用正确的着色器的帮助下,问题得到解决。 我必须使用OpenGL ES 2.0的基本要求——着色器。
2个回答

5
要从iOS上的OpenGL ES中读取数据,基本上有两种方法:使用glReadPixels()或使用纹理缓存(仅适用于iOS 5.0+)。
你只有一个纹理ID和无法访问其他内容的事实有点奇怪,并且在这里限制了你的选择。如果您没有办法在第三方API中设置要使用的纹理,则需要重新渲染该纹理以提取其像素,使用glReadPixels()或纹理缓存。为此,您将使用与您的纹理相同尺寸的FBO,简单的四边形(由两个三角形组成的矩形)和一个通过着色器,它将仅在输出帧缓冲区中显示纹理的每个texel。
在这一点上,您只需使用glReadPixels()将字节拉回到CVPixelBufferRef的内部字节数组中,或者最好使用纹理缓存来消除该读取的需求。我在此答案中描述了如何为该方法设置缓存,以及如何将其传递给AVAssetWriter。您需要将离屏FBO设置为使用CVPixelBufferRef的关联纹理作为渲染目标,以使其正常工作。
但是,如果您有手段设置要用于此呈现纹理的ID,则可以避免不必要地重新渲染它以获取其像素值。按上面链接的答案中描述的方式设置纹理缓存,并将该像素缓冲区的纹理ID传递给您正在使用的第三方API。然后它将呈现到与像素缓冲区相关联的纹理中,您可以直接从中记录。这是我在我的GPUImage框架中加速从OpenGL ES录制视频的方法(对于iOS 4.x来说,glReadPixels()是后备方案)。

非常感谢,请查看我问题中的“更新”部分。 - Usman.3D
1
@Usman.3D - 如果您无法提供用于纹理的ID,并且无法访问渲染纹理的时间,则需要重新将其渲染到离屏FBO以提取其颜色字节。然后,您可以使用我描述的任一方法:使用glReadPixels()从FBO中提取,或者绑定纹理与纹理缓存配对以更快地捕获结果。 - Brad Larson
@Usman.3D - 以上代码中你的glReadPixels()在哪里?如果你没有使用它来读取像素,那么你需要正确设置纹理缓存并将相关纹理绑定到渲染FBO。确保你遵循了这里的所有设置步骤:https://dev59.com/Qmkw5IYBdhLWcg3w8O-o#9704392,并且你正在使用相同的OpenGL ES上下文进行纹理上传和纹理缓存。 - Brad Larson
@Larson博士,我已经设置了纹理缓存,但是我没有在上面包含那部分代码。我会在更新中加入它。 - Usman.3D
谢谢Larson博士。将纹理附加到textureFBO的技术起作用了。我只需要使用您在另一个答案中描述的正确着色器。非常感谢您的帮助。 :) - Usman.3D
显示剩余2条评论

1

很遗憾,glGetTexImage不支持ios。当我为cocos2d实现我的CCMutableTexture2D类时,我曾经为此苦苦挣扎。

在推送到gpu之前缓存图像

如果你查看源代码,你会注意到最后我将图像的像素缓冲区保存在我的CCMutableTexture2D类中,而不是像通常一样在推送到gpu后将其丢弃。

http://www.cocos2d-iphone.org/forum/topic/2449

使用FBO和glReadPixels

很遗憾,我认为这种方法可能不适合您,因为您正在使用纹理数据创建某种视频,并且保留我们缓存的每个像素缓冲区会占用大量内存。另一种方法是动态创建FBO以使用glReadPixels填充像素缓冲区。我不太确定这种方法的成功率如何,但有一个很好的示例在这里发布了:使用glReadPixels读取纹理字节?


1
如果您将要创建纹理,那么从iOS 5.0开始,有一种更快的方式可以访问其背后的字节。您可以使用纹理缓存直接读取和写入纹理背后的支持字节,如我在这里所描述的:https://dev59.com/Qmkw5IYBdhLWcg3w8O-o#9704392 。这是一种直接内存访问,不依赖于glReadPixels()glTexImage2D() - Brad Larson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接