gpt4 book ai didi

ios - 如何在平面/ycbcr/420f/yuv/NV12/非 rgb 的 CVPixelBufferRef 上绘图?

转载 作者:可可西里 更新时间:2023-11-01 05:42:55 34 4
gpt4 key购买 nike

我从系统 API 收到了 CMSampleBufferRef,其中包含 CVPixelBufferRef,但不是 RGBA(线性像素)。缓冲区包含平面像素(例如 420f 又名 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 又名 yCbCr 又名 YUV)。

我想在将视频数据发送到 VideoToolkit 以编码为 h264(绘制一些文本、覆盖 Logo 、旋转图像等),但我希望它是高效和实时的。 Buuuut 平面图像数据看起来非常困惑 - 有色度平面和亮度平面,它们的大小不同,而且......在字节级别上处理它似乎需要大量工作。

我可能会使用 CGContextRef 并直接在像素顶部绘制,但据我所知,它只支持 RGBA 像素。关于如何通过尽可能少的数据复制和尽可能少的代码行来做到这一点,有什么建议吗?

最佳答案

CGBitmapContextRef 只能绘制成类似 32ARGB 的内容,正确。这意味着您需要创建 ARGB(或 RGBA)缓冲区,然后找到一种方法快速将 YUV 像素传输到此 ARGB 表面。这个秘诀包括使用 CoreImage,一个通过池的自制 CVPixelBufferRef,一个引用你自制像素缓冲区的 CGBitmapContextRef,然后重新创建一个 CMSampleBufferRef 类似于您的输入缓冲区,但引用您的输出像素。换句话说,

  1. 将传入的像素提取到 CIImage 中。
  2. 使用您正在创建的像素格式和输出尺寸创建一个 CVPixelBufferPool。您不想在没有池的情况下实时创建 CVPixelBuffer:如果您的生产者速度太快,您将耗尽内存;你会碎片化你的 RAM,因为你不会重复使用缓冲区;这是对周期的浪费。
  3. 使用您将在缓冲区之间共享的默认构造函数创建一个 CIContext。它不包含外部状态,但文档说在每一帧上重新创建它的成本非常高。
  4. 在传入帧上,创建一个新的像素缓冲区。确保使用分配阈值,以免 RAM 使用失控。
  5. 锁定像素缓冲区
  6. 创建引用像素缓冲区中字节的位图上下文
  7. 使用CIContext将平面图像数据渲染到线性缓冲区
  8. 在 CGContext 中执行您的应用程序特定绘图!
  9. 解锁像素缓冲区
  10. 获取原始样本缓冲区的时间信息
  11. 通过向像素缓冲区询问其确切格式来创建 CMVideoFormatDescriptionRef
  12. 为像素缓冲区创建样本缓冲区。完成!

这是一个示例实现,我选择了 32ARGB 作为要使用的图像格式,因为 CGBitmapContextCoreVideo 都喜欢在 iOS 上使用它:

{
CGPixelBufferPoolRef *_pool;
CGSize _poolBufferDimensions;
}
- (void)_processSampleBuffer:(CMSampleBufferRef)inputBuffer
{
// 1. Input data
CVPixelBufferRef inputPixels = CMSampleBufferGetImageBuffer(inputBuffer);
CIImage *inputImage = [CIImage imageWithCVPixelBuffer:inputPixels];

// 2. Create a new pool if the old pool doesn't have the right format.
CGSize bufferDimensions = {CVPixelBufferGetWidth(inputPixels), CVPixelBufferGetHeight(inputPixels)};
if(!_pool || !CGSizeEqualToSize(bufferDimensions, _poolBufferDimensions)) {
if(_pool) {
CFRelease(_pool);
}
OSStatus ok0 = CVPixelBufferPoolCreate(NULL,
NULL, // pool attrs
(__bridge CFDictionaryRef)(@{
(id)kCVPixelBufferPixelFormatTypeKey: @(kCVPixelFormatType_32ARGB),
(id)kCVPixelBufferWidthKey: @(bufferDimensions.width),
(id)kCVPixelBufferHeightKey: @(bufferDimensions.height),
}), // buffer attrs
&_pool
);
_poolBufferDimensions = bufferDimensions;
assert(ok0 == noErr);
}

// 4. Create pixel buffer
CVPixelBufferRef outputPixels;
OSStatus ok1 = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes(NULL,
_pool,
(__bridge CFDictionaryRef)@{
// Opt to fail buffer creation in case of slow buffer consumption
// rather than to exhaust all memory.
(__bridge id)kCVPixelBufferPoolAllocationThresholdKey: @20
}, // aux attributes
&outputPixels
);
if(ok1 == kCVReturnWouldExceedAllocationThreshold) {
// Dropping frame because consumer is too slow
return;
}
assert(ok1 == noErr);

// 5, 6. Graphics context to draw in
CGColorSpaceRef deviceColors = CGColorSpaceCreateDeviceRGB();
OSStatus ok2 = CVPixelBufferLockBaseAddress(outputPixels, 0);
assert(ok2 == noErr);
CGContextRef cg = CGBitmapContextCreate(
CVPixelBufferGetBaseAddress(outputPixels), // bytes
CVPixelBufferGetWidth(inputPixels), CVPixelBufferGetHeight(inputPixels), // dimensions
8, // bits per component
CVPixelBufferGetBytesPerRow(outputPixels), // bytes per row
deviceColors, // color space
kCGImageAlphaPremultipliedFirst // bitmap info
);
CFRelease(deviceColors);
assert(cg != NULL);

// 7
[_imageContext render:inputImage toCVPixelBuffer:outputPixels];

// 8. DRAW
CGContextSetRGBFillColor(cg, 0.5, 0, 0, 1);
CGContextSetTextDrawingMode(cg, kCGTextFill);
NSAttributedString *text = [[NSAttributedString alloc] initWithString:@"Hello world" attributes:NULL];
CTLineRef line = CTLineCreateWithAttributedString((__bridge CFAttributedStringRef)text);
CTLineDraw(line, cg);
CFRelease(line);

// 9. Unlock and stop drawing
CFRelease(cg);
CVPixelBufferUnlockBaseAddress(outputPixels, 0);

// 10. Timings
CMSampleTimingInfo timingInfo;
OSStatus ok4 = CMSampleBufferGetSampleTimingInfo(inputBuffer, 0, &timingInfo);
assert(ok4 == noErr);

// 11. VIdeo format
CMVideoFormatDescriptionRef videoFormat;
OSStatus ok5 = CMVideoFormatDescriptionCreateForImageBuffer(NULL, outputPixels, &videoFormat);
assert(ok5 == noErr);

// 12. Output sample buffer
CMSampleBufferRef outputBuffer;
OSStatus ok3 = CMSampleBufferCreateForImageBuffer(NULL, // allocator
outputPixels, // image buffer
YES, // data ready
NULL, // make ready callback
NULL, // make ready refcon
videoFormat,
&timingInfo, // timing info
&outputBuffer // out
);
assert(ok3 == noErr);

[_consumer consumeSampleBuffer:outputBuffer];
CFRelease(outputPixels);
CFRelease(videoFormat);
CFRelease(outputBuffer);
}

关于ios - 如何在平面/ycbcr/420f/yuv/NV12/非 rgb 的 CVPixelBufferRef 上绘图?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46524830/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com