iOS 7+ 从 BGRA 字节获取 CVPixelBufferRef 的最快方法
Fastest way on iOS 7+ to get CVPixelBufferRef from BGRA bytes
在 iOS 7+ 上将 BGRA / UIImage
数据的原始字节转换为 CVPixelBufferRef
的最快方法是什么?按 BGRA 顺序,字节为每个像素 4 个字节。
这里是否有直接转换与将数据复制到辅助存储的机会?
我考虑过CVPixelBufferCreateWithBytes
但我有预感它正在复制内存...
您必须使用 CVPixelBufferCreate
,因为 CVPixelBufferCreateWithBytes
不允许使用 Core Video 纹理缓存快速转换为 OpenGL 纹理。我不确定为什么会这样,但至少从 iOS 8 开始就是这样。我用分析器对此进行了测试,CVPixelBufferCreateWithBytes
导致每次调用 texSubImage2D从缓存访问核心视频纹理。
如果宽度不是 16 的倍数,CVPixelBufferCreate
会做一些有趣的事情,所以如果你计划在 CVPixelBufferGetBaseAddress
上做 CPU 操作,并且你希望它被放置像 CGImage
或 CGBitmapContext
一样,您需要将宽度填充得更高,直到它是 16 的倍数,或者确保您使用 CVPixelBufferGetRowBytes 并将其传递给您创建的任何 CGBitmapContext
.
我测试了从16到2048的所有宽高尺寸组合,只要填充到下一个16的最高倍数,内存布局就可以了。
+ (NSInteger) alignmentForPixelBufferDimension:(NSInteger)dim
{
static const NSInteger modValue = 16;
NSInteger mod = dim % modValue;
return (mod == 0 ? dim : (dim + (modValue - mod)));
}
+ (NSDictionary*) pixelBufferSurfaceAttributesOfSize:(CGSize)size
{
return @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(NSString*)kCVPixelBufferWidthKey: @(size.width),
(NSString*)kCVPixelBufferHeightKey: @(size.height),
(NSString*)kCVPixelBufferBytesPerRowAlignmentKey: @(size.width * 4),
(NSString*)kCVPixelBufferExtendedPixelsLeftKey: @(0),
(NSString*)kCVPixelBufferExtendedPixelsRightKey: @(0),
(NSString*)kCVPixelBufferExtendedPixelsTopKey: @(0),
(NSString*)kCVPixelBufferExtendedPixelsBottomKey: @(0),
(NSString*)kCVPixelBufferPlaneAlignmentKey: @(0),
(NSString*)kCVPixelBufferCGImageCompatibilityKey: @(YES),
(NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey: @(YES),
(NSString*)kCVPixelBufferOpenGLESCompatibilityKey: @(YES),
(NSString*)kCVPixelBufferIOSurfacePropertiesKey: @{ @"IOSurfaceCGBitmapContextCompatibility": @(YES), @"IOSurfaceOpenGLESFBOCompatibility": @(YES), @"IOSurfaceOpenGLESTextureCompatibility": @(YES) } };
}
有趣的是,如果您从 Core Video 缓存中请求尺寸小于填充尺寸的纹理,它将立即 return 纹理。以某种方式在它下面可以引用原始纹理,但宽度和高度较小。
总而言之,您无法使用 CVPixelBufferCreateWithBytes
使用 CVPixelBufferRef
包装现有内存并有效地使用 Core Video 纹理缓存。您必须使用 CVPixelBufferCreate
并使用 CVPixelBufferGetBaseAddress
.
在 iOS 7+ 上将 BGRA / UIImage
数据的原始字节转换为 CVPixelBufferRef
的最快方法是什么?按 BGRA 顺序,字节为每个像素 4 个字节。
这里是否有直接转换与将数据复制到辅助存储的机会?
我考虑过CVPixelBufferCreateWithBytes
但我有预感它正在复制内存...
您必须使用 CVPixelBufferCreate
,因为 CVPixelBufferCreateWithBytes
不允许使用 Core Video 纹理缓存快速转换为 OpenGL 纹理。我不确定为什么会这样,但至少从 iOS 8 开始就是这样。我用分析器对此进行了测试,CVPixelBufferCreateWithBytes
导致每次调用 texSubImage2D从缓存访问核心视频纹理。
CVPixelBufferCreate
会做一些有趣的事情,所以如果你计划在 CVPixelBufferGetBaseAddress
上做 CPU 操作,并且你希望它被放置像 CGImage
或 CGBitmapContext
一样,您需要将宽度填充得更高,直到它是 16 的倍数,或者确保您使用 CVPixelBufferGetRowBytes 并将其传递给您创建的任何 CGBitmapContext
.
我测试了从16到2048的所有宽高尺寸组合,只要填充到下一个16的最高倍数,内存布局就可以了。
+ (NSInteger) alignmentForPixelBufferDimension:(NSInteger)dim
{
static const NSInteger modValue = 16;
NSInteger mod = dim % modValue;
return (mod == 0 ? dim : (dim + (modValue - mod)));
}
+ (NSDictionary*) pixelBufferSurfaceAttributesOfSize:(CGSize)size
{
return @{ (NSString*)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32BGRA),
(NSString*)kCVPixelBufferWidthKey: @(size.width),
(NSString*)kCVPixelBufferHeightKey: @(size.height),
(NSString*)kCVPixelBufferBytesPerRowAlignmentKey: @(size.width * 4),
(NSString*)kCVPixelBufferExtendedPixelsLeftKey: @(0),
(NSString*)kCVPixelBufferExtendedPixelsRightKey: @(0),
(NSString*)kCVPixelBufferExtendedPixelsTopKey: @(0),
(NSString*)kCVPixelBufferExtendedPixelsBottomKey: @(0),
(NSString*)kCVPixelBufferPlaneAlignmentKey: @(0),
(NSString*)kCVPixelBufferCGImageCompatibilityKey: @(YES),
(NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey: @(YES),
(NSString*)kCVPixelBufferOpenGLESCompatibilityKey: @(YES),
(NSString*)kCVPixelBufferIOSurfacePropertiesKey: @{ @"IOSurfaceCGBitmapContextCompatibility": @(YES), @"IOSurfaceOpenGLESFBOCompatibility": @(YES), @"IOSurfaceOpenGLESTextureCompatibility": @(YES) } };
}
有趣的是,如果您从 Core Video 缓存中请求尺寸小于填充尺寸的纹理,它将立即 return 纹理。以某种方式在它下面可以引用原始纹理,但宽度和高度较小。
总而言之,您无法使用 CVPixelBufferCreateWithBytes
使用 CVPixelBufferRef
包装现有内存并有效地使用 Core Video 纹理缓存。您必须使用 CVPixelBufferCreate
并使用 CVPixelBufferGetBaseAddress
.