Swift中如何将一个CVPixelBuffer从中间分割为两个独立的CVPixelBuffer?
无需转换其他图像类型,直接分割CVPixelBuffer为两个独立缓冲区的实现方案
完全可以做到,不需要将CVPixelBuffer转换为UIImage、CGImage等其他格式,直接基于原缓冲区的内存操作就能分割出两个独立的CVPixelBuffer,下面是两种可行的实现思路:
方案一:共享原内存(高效无复制)
这种方式不需要复制像素数据,直接基于原缓冲区的内存区域创建新的CVPixelBuffer,性能最优,但需要注意原缓冲区的生命周期管理——必须保证新缓冲区存在期间,原缓冲区不被释放。
步骤:
- 提取原缓冲区的核心参数:
size_t totalWidth = CVPixelBufferGetWidth(sourceBuffer); size_t totalHeight = CVPixelBufferGetHeight(sourceBuffer); size_t singleHeight = totalHeight / 2; size_t bytesPerRow = CVPixelBufferGetBytesPerRow(sourceBuffer); OSType pixelFormat = CVPixelBufferGetPixelFormatType(sourceBuffer); - 获取原缓冲区的内存基地址,计算两张子图的起始地址:
void *baseAddr = CVPixelBufferGetBaseAddress(sourceBuffer); void *topImageAddr = baseAddr; void *bottomImageAddr = baseAddr + singleHeight * bytesPerRow; - 分别创建两个共享内存的CVPixelBuffer:
注意:如果原缓冲区是只读的,创建时需要指定// 创建顶部图像的缓冲区 CVPixelBufferRef topBuffer = NULL; CVPixelBufferCreateWithBytes(NULL, totalWidth, singleHeight, pixelFormat, topImageAddr, bytesPerRow, NULL, NULL, NULL, &topBuffer); // 创建底部图像的缓冲区 CVPixelBufferRef bottomBuffer = NULL; CVPixelBufferCreateWithBytes(NULL, totalWidth, singleHeight, pixelFormat, bottomImageAddr, bytesPerRow, NULL, NULL, NULL, &bottomBuffer);kCVPixelBufferLockFlags_ReadOnly相关属性,避免写入错误。
方案二:复制像素数据(独立安全)
如果不想依赖原缓冲区的生命周期,或者原缓冲区是不可共享的只读内存,可以通过复制对应区域的像素数据,创建完全独立的新CVPixelBuffer。
步骤:
- 同样先提取原缓冲区的核心参数(同方案一)。
- 创建两个空的目标缓冲区,确保参数和原缓冲区匹配:
NSDictionary *bufferAttributes = @{ (id)kCVPixelBufferPixelFormatTypeKey: @(pixelFormat), (id)kCVPixelBufferBytesPerRowAlignmentKey: @(bytesPerRow), (id)kCVPixelBufferWidthKey: @(totalWidth), (id)kCVPixelBufferHeightKey: @(singleHeight) }; CVPixelBufferRef topBuffer = NULL; CVPixelBufferCreate(NULL, totalWidth, singleHeight, pixelFormat, (__bridge CFDictionaryRef)bufferAttributes, &topBuffer); CVPixelBufferRef bottomBuffer = NULL; CVPixelBufferCreate(NULL, totalWidth, singleHeight, pixelFormat, (__bridge CFDictionaryRef)bufferAttributes, &bottomBuffer); - 锁定所有缓冲区的内存,执行数据复制:
// 锁定内存(必须操作,确保内存可访问) CVPixelBufferLockBaseAddress(sourceBuffer, 0); CVPixelBufferLockBaseAddress(topBuffer, 0); CVPixelBufferLockBaseAddress(bottomBuffer, 0); // 复制顶部图像数据 memcpy(CVPixelBufferGetBaseAddress(topBuffer), CVPixelBufferGetBaseAddress(sourceBuffer), singleHeight * bytesPerRow); // 复制底部图像数据 memcpy(CVPixelBufferGetBaseAddress(bottomBuffer), CVPixelBufferGetBaseAddress(sourceBuffer) + singleHeight * bytesPerRow, singleHeight * bytesPerRow); // 解锁内存 CVPixelBufferUnlockBaseAddress(sourceBuffer, 0); CVPixelBufferUnlockBaseAddress(topBuffer, 0); CVPixelBufferUnlockBaseAddress(bottomBuffer, 0);
注意事项
- 像素格式兼容性:如果原缓冲区是YUV等多平面格式(比如NV12),需要针对每个平面分别处理分割逻辑,而不是直接按整体字节数分割。
- 字节对齐:新缓冲区的
bytesPerRow必须和原缓冲区保持一致,否则会出现图像错位、花屏等问题。 - 内存权限:如果原缓冲区标记为只读,方案一中创建的新缓冲区也只能是只读的,不能进行写入操作。
内容的提问来源于stack exchange,提问作者Burkay Sabırsız
相关产品推荐
相关产品推荐

