ios - 如何使用 YUV 颜色空间将 UIImage 转换为 CVPixelBufferRef?
问题描述
我正在做视频录制。我需要将视图快照到 UIImage,然后将其转换为 CVPixelBufferRef。它适用于 RGBA 色彩空间。但是我需要的 CVPixelBufferRef 应该是 YUV 颜色空间。
有人有想法么?谢谢。
+ (CVPixelBufferRef) pixelBufferFromLayer:(CALayer *)layer forSize:(CGSize)size
{
UIImage * image = [self fetchScreenShotFromLayer:layer forSize:size];
// this worked fine
// CVPixelBufferRef rgbBuffer = [self RGBPixelBufferFromCGImage:image.CGImage];
// return rgbBuffer;
// NSData * imageData = UIImageJPEGRepresentation(image, 0.5);
NSData * imageData = UIImagePNGRepresentation(image);
CVPixelBufferRef buffer = [self yuvPixelBufferWithData:imageData width:size.width heigth:size.height];
return buffer;
}
用 RGB 颜色空间创建 CVPixelBufferRef 很好。
// RGB color space pixel buffer
+ (CVPixelBufferRef) RGBPixelBufferFromCGImage:(CGImageRef)image
{
NSDictionary * options = @{
(NSString *)kCVPixelBufferCGImageCompatibilityKey: @(YES),
(NSString *)kCVPixelBufferCGBitmapContextCompatibilityKey: @(YES),
};
CVPixelBufferRef pxbuffer = NULL;
CGFloat frameWidth = CGImageGetWidth(image);
CGFloat frameHeight = CGImageGetHeight(image);
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, frameWidth, frameHeight, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef)options, &pxbuffer);
NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void * pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
NSParameterAssert(pxdata != NULL);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, frameWidth, frameHeight, 8, CVPixelBufferGetBytesPerRow(pxbuffer), rgbColorSpace, (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
NSParameterAssert(context);
CGContextConcatCTM(context, CGAffineTransformIdentity);
CGContextDrawImage(context, CGRectMake(0, 0, frameWidth, frameHeight), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
return pxbuffer;
}
// snapshoot for layer
+ (UIImage *) fetchScreenShotFromLayer:(CALayer *)layer forSize:(CGSize)size
{
UIImage * image = nil;
@autoreleasepool {
NSLock * aLock = [NSLock new];
[aLock lock];
CGSize imageSize = size;
UIGraphicsBeginImageContextWithOptions(imageSize, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();
[layer renderInContext:context];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[aLock unlock];
}
return image;
}
这有点不对劲。
// data to yuv buffer
+ (CVPixelBufferRef)yuvPixelBufferWithData:(NSData *)dataFrame
width:(size_t)w
heigth:(size_t)h
{
unsigned char* buffer = (unsigned char*) dataFrame.bytes;
CVPixelBufferRef getCroppedPixelBuffer = [self copyDataFromBuffer:buffer toYUVPixelBufferWithWidth:w Height:h];
return getCroppedPixelBuffer;
}
+ (CVPixelBufferRef) copyDataFromBuffer:(const unsigned char*)buffer toYUVPixelBufferWithWidth:(size_t)w Height:(size_t)h
{
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
CVPixelBufferRef pixelBuffer;
CVPixelBufferCreate(NULL, w, h, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, (__bridge CFDictionaryRef)(options), &pixelBuffer);
size_t count = CVPixelBufferGetPlaneCount(pixelBuffer);
NSLog(@"PlaneCount = %zu", count); // 2
CVPixelBufferLockBaseAddress(pixelBuffer, 0);
size_t d = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
const unsigned char* src = buffer;
unsigned char* dst = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
for (unsigned int rIdx = 0; rIdx < h; ++rIdx, dst += d, src += w) {
memcpy(dst, src, w);
}
d = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
dst = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
h = h >> 1;
for (unsigned int rIdx = 0; rIdx < h; ++rIdx, dst += d, src += w) {
memcpy(dst, src, w);
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
return pixelBuffer;
}
这是图像。
谢谢你的帮助。
解决方案
您可以从 RGBA 格式的 UIImage 获取原始数据,然后转换为 yuv 格式,并使用 yuv 数据填充您从 CMSampleBufferRef 获得的 CVPixelBufferRef 的不同平面。这里的CMSampleBufferRef就是captureOutput的参数。请记住在初始化 AVCaptureSession 的 AVCaptureVideoDataOutput 时将视频设置设置为 kCVPixelFormatType_420YpCbCr8BiPlanarFullRange(如果这是您想要的。)。
推荐阅读
- html - Bootstrap 4.5,分离滚动条,让 Div 贴在底部
- python - 只想考虑到目前为止的数据框
- php - 检查字符串是否具有特定数量的符号
- ubuntu - 使用 scons 在 linux 中进行 32 位编译
- asp.net-core - .Net Core 执行插入存储过程
- javascript - 如何使用 React 添加 Konva 组件?
- html - 为什么我的编辑器(VS Studio)无法识别我的媒体查询?
- javascript - 如何在 Django 上加快我的 css 和 js 加载速度
- python - Python 的 Pizzapi 模块未在 IDLE 之外运行
- python - Pandas Python 数据框:无法定位元素