本文档探讨讨论iOS真机上CVOpenGLESTextureCacheCreateTextureFromImage返回-6683问题,-6683表示kCVReturnPixelBufferNotOpenGLCompatible。因此函数名过长,方便行文起见,文档后续将其简称为CreateTextureFromImage。调试过程使用了CreateTextureFromImage和glTexImage2D等函数,同时,数据源为Video Toolbox解码的视频。因此,这实际还是个iOS上硬解问题的延伸。
在使用Video Toolbox的过程中,我编写了以下文档,因开发任务等原因,部分文档处于草稿状态:
- 获取VideoToolbox解码直播等H.264流的颜色转换矩阵
- Video Toolbox Multi-pass Encoding
- iOS 音视频高级编程:AVAsset、CoreVideo、VideoToolbox、FFmpeg与CMTime
- iOS 音视频高级编程:AVAssetReaderTrackOutput改变CMFormatDescription导致Video Toolbox解码失败与不解码GPU直接显示H.264帧
- iOS 音视频高级编程:读写VideoToolbox解码回调的CVImageBufferRef中YUV图像
- iOS VideoToolbox硬编H.265(HEVC)H.264(AVC):4 同步编码
- 【草稿】iOS VideoToolbox硬编H.265(HEVC)H.264(AVC):2 H264数据写入文件
- iOS VideoToolbox硬编H.265(HEVC)H.264(AVC):1 概述
题外话,刚才回答了StackOverflow上一个问题,OpenGLES 3 Error at CVOpenGLESTextureCacheCreateTextureFromImage -6683,自此人生又多了一个同性社交网站的账号<手动斜眼.jpg>。自己还是个初学者,能回答的问题少,放个图让自己开心一下。存在不少语法错误,大二开始就没怎么写过完整的句子,现在只能乱写了-_-#。
明确一点,CreateTextureFromImage在使用kCVPixelBufferPixelFormatTypeKey为以下值的pixelBuffer可正常运行:
- kCVPixelFormatType_32BGRA
- kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
- kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
这些值在CreateTextureFromImage文档也有说明。另外,使用前后摄像头时,可设置AVCaptureVideoDataOutput.videoSettings支持这些值。当然,还可以给AVCaptureVideoDataOutput.videoSettings指定为yuv420,系统会进行格式转换,我在iPhone 6真机上测试过,具体参考我另一个文档iOS VideoToolbox硬编H.265(HEVC)H.264(AVC):1 概述。
下面以Video Toolbox解码H.264得到的CVPixelBuffer为例进行讨论。
1、当VTDecompressionSessionCreate的参数destinationImageBufferAttributes为NULL时,Video Toolbox输出的像素格式为kCVPixelFormatType_420YpCbCr8BiPlanar*,通常由CreateTextureFromImage读取Y、UV平面创建纹理,因为大家都参考了苹果官方的示例代码。相应的参数internalFormat、format设置如下:
- OpenGL ES 2.0:GL_RED_EXT、GL_RG_EXT。
- OpenGL ES 3.0:GL_LUMINANCE、GL_LUMINANCE_ALPHA。
无论GL_RED_EXT或GL_LUMINANCE,只是“欺骗”OpenGL (ES),我们上传的数据是可渲染或可解析。实际上,我们在着色器真正指定了这些数据该如何处理,至于OpenGL ES怎么理解它,我们并不关心,只让它不拒绝我们的数据即可,比如
texture(SamplerY, v_texCoord).r;
texture(SamplerUV, v_texCoord).ra
GL_LUMINANCE等宏定义表明了,这是大小不一的纹理格式,OpenGL ES 3.0 Programming Guide中说明如下。
CreateTextureFromImage创建纹理遵守glTexImage2D函数的相关参数约定。
2、当destinationImageBufferAttributes指定为kCVPixelFormatType_420YpCbCr8BiPlanar*,则生成纹理的参数与前述说明完全一致。就OpenGL ES实现YUV转换RGB而言,是否指定kCVPixelBufferOpenGLESCompatibilityKey并不影响CreateTextureFromImage创建纹理的行为。至于是否有性能影响,没进行详细测试。
3、当destinationImageBufferAttributes指定为kCVPixelFormatType_420YpCbCr8Planar时,表明我们让Video Toolbox输出yuv420p格式数据,那么,无论是否指定kCVPixelBufferOpenGLESCompatibilityKey,通过CreateTextureFromImage生成纹理时都返回-6683错误码。Y、U、V都指定为GL_LUMINANCE进行创建。
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
_videoTextureCache,
pixelBuffer,
NULL,
GL_TEXTURE_2D,
GL_LUMINANCE,
frameWidth, frameHeight,
GL_LUMINANCE,
GL_UNSIGNED_BYTE,
0,
&_lumaTexture);
CVOpenGLESTextureCacheCreateTextureFromImage的功能相当于创建并上传纹理,类似于Android Direct Texture,提高了纹理上传效率,其调用栈帧如下。
根据Creating IOSurface-backed CVPixelBuffers for accessing video data in OpenGL ES提示,CVPixelBuffer需有IOSurface支撑,然而,下面代码段依然在创建纹理时返回-6683错误码。
NSDictionary *destinationImageBufferAttributes = @{
(id) kCVPixelBufferOpenGLESCompatibilityKey : @(YES),
(id) kCVPixelBufferIOSurfacePropertiesKey : [NSDictionary dictionary],
(id) kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8Planar)
};
根据之前读写CVPixelBuffer的经验(详情见我另一个文档iOS 音视频高级编程:读写VideoToolbox解码回调的CVImageBufferRef中YUV图像),kCVPixelBufferIOSurfacePropertiesKey为空时才能生成CIImage,这就和苹果的文档说法相反了,当然,这也是无效的,代码如下。
(id) kCVPixelBufferIOSurfacePropertiesKey : @{}
另外,作为iOS 9新增的键值,在yuv420p情况下,是否指定kCVPixelBufferOpenGLESTextureCacheCompatibilityKey对CreateTextureFromImage能否创建纹理并不影响最终结果。
(id) kCVPixelBufferOpenGLESTextureCacheCompatibilityKey : @(YES)
4、glTexImage2D上传纹理
const int PlaneCount = 3;
for (int i = 0; i < PlaneCount; ++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]);
}
上述代码确实将图像数据上传至GPU,然而,图像显示不出来,相关资源占用如下。
Y、U和V在GPU中分别表示为:
现在,新问题:Mipmapping enabled without a complete mip-chain (Issue) Texture #0 has mipmapping enabled, but is missing levels of the mip-chain.
先尝试加上生成mipmap,问题依旧。
glGenerateMipmap(GL_TEXTURE_2D);
那么,Y通道的mipmap级别如何判断或者如何关闭mipmap的生成呢?经搜索,找到如下信息:
- “This texture have mipmapping enabled, but is missing levels of the mip-chain.”
This must be caused by drawing sprites at reduced size, the texture having mipmapping enabled, but no mipmaps being generated.
You can generate the mipmap, using CCTexture2D generateMipmap.
Normally this should have no impact on performance, but "only" on visual quality.
然而,这并没什么卵用,因为我刚才尝试了glGenerateMipmap。
- OpenGL ES Tuning and Optimization,WWDC这个讲座有处理这个问题,但是,它的处理办法就是调用glGenerateMipmap,这解决不了我们现在这个问题。
然而,根据调试工具,这个原因是在glkView:drawInRect:方法出现的,代码创建纹理并不在此处。那么,修改绘制函数是否能处理此问题呢?