在录制视频时将动态文本渲染到CVPixelBufferRef上

8
我正在使用AVCaptureVideoDataOutputAVCaptureAudioDataOutput记录视频和音频,在captureOutput:didOutputSampleBuffer:fromConnection:代理方法中,我想将文本绘制到从视频连接接收到的每个单独的样本缓冲区上。文本大约每帧更改一次(它是一个秒表标签),我希望将其记录在捕获的视频数据之上。
以下是我目前想到的解决方案:
//1.
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

//2.   
UIImage *textImage = [self createTextImage];
CIImage *maskImage = [CIImage imageWithCGImage:textImage.CGImage];

//3.
CVPixelBufferLockBaseAddress(pixelBuffer, 0); 
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
NSDictionary *options = [NSDictionary dictionaryWithObject:(__bridge id)colorSpace forKey:kCIImageColorSpace];
CIImage *inputImage = [CIImage imageWithCVPixelBuffer:pixelBuffer options:options];

//4.
CIFilter *filter = [CIFilter filterWithName:@"CIBlendWithMask"];
[filter setValue:inputImage forKey:@"inputImage"];
[filter setValue:maskImage forKey:@"inputMaskImage"];
CIImage *outputImage = [filter outputImage];
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);

//5.   
[self.renderContext render:outputImage toCVPixelBuffer:pixelBuffer bounds:[outputImage extent] colorSpace:CGColorSpaceCreateDeviceRGB()];

//6.
[self.pixelBufferAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:timestamp];
  1. 我获取像素缓冲区,非常容易。
  2. 我使用核心图形将文本写入空白的UIImage(这就是createTextImage的作用)。我能够验证这一步骤是否有效;我保存了一个带有绘制文本的图像到我的照片中。
  3. 我从像素缓冲区创建CGImage。
  4. 我创建了一个CIFilter用于CIBlendWithMask,将输入图像设置为从原始像素缓冲区创建的图像,将输入掩模设置为从绘制文本的图像创建的CIImage
  5. 最后,我将滤镜输出图像渲染到像素缓冲区。 CIContext事先使用[CIContext contextWithOptions:nil];创建。
  6. 在所有这些操作之后,我使用适当的时间戳将像素缓冲区附加到我的pixelBufferAdaptor中。

录制结束后保存的视频没有任何可见变化,即没有掩模图像绘制到像素缓冲区上。

有人知道我在哪里出错了吗?我已经卡在这里几天了,任何帮助都将不胜感激。

编辑:

- (UIImage *)createTextImage {
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(self.view.bounds.size.width, self.view.bounds.size.height), NO, 1.0);
    NSMutableAttributedString *timeStamp = [[NSMutableAttributedString alloc]initWithString:self.timeLabel.text attributes:@{NSForegroundColorAttributeName:self.timeLabel.textColor, NSFontAttributeName: self.timeLabel.font}];
    NSMutableAttributedString *countDownString = [[NSMutableAttributedString alloc]initWithString:self.cDownLabel.text attributes:@{NSForegroundColorAttributeName:self.cDownLabel.textColor, NSFontAttributeName:self.cDownLabel.font}];
    [timeStamp drawAtPoint:self.timeLabel.center];
    [countDownString drawAtPoint:self.view.center];
    UIImage *blank = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return blank;
}

1
你好!你的问题已经在我的问题中得到了回答。但是self.renderContext是什么? - Dmitriy Pushkarev
这是一个 CIContext 对象。 - barndog
你最终是如何完成这个的? - Pablo Martinez
最终我使用了GPUImage来录制实时视频,然后对结果应用了一个滤镜。 - barndog
你能分享一下那个示例代码吗?@barndog 我正在使用GPUImage,但是无法达到相同的效果。 - Deep Rathod
4个回答

4

您想要如下所述吗? 图片描述

不要使用CIBlendWithMask,而应该使用CISourceOverCompositing,请尝试以下代码:

//4.
CIFilter *filter = [CIFilter filterWithName:@"CISourceOverCompositing"];
[filter setValue:maskImage forKey:kCIInputImageKey];
[filter setValue:inputImage forKey:kCIInputBackgroundImageKey];
CIImage *outputImage = [filter outputImage];

是的,我刚刚检查过了。 - barndog
你能发一下你的 createTextImage 函数吗? - Bannings
当然,我刚刚已经完成了。 - barndog
似乎maskImage的方向不正确。尝试使用以下代码:CIImage *maskImage = [[CIImage imageWithCGImage:textImage.CGImage] imageByApplyingOrientation:8]; - Bannings
你能在GitHub上发布你的项目吗? - Bannings
显示剩余4条评论

1

1

我向苹果的DTS询问了与此相同的问题,因为我所尝试的所有方法都运行缓慢或出现异常情况,他们给我发送了以下内容:

https://developer.apple.com/documentation/avfoundation/avasynchronousciimagefilteringrequest?language=objc

使用CIFilters可以完全绕过CVPixelBuffer,这样做更容易。因此,如果您实际上不需要使用CVPixelBuffer,则这种方法将很快成为您的新朋友。使用组合的CIFilter来合成源图像和每个帧生成的文本图像即可解决问题。希望这对其他人有所帮助!

有没有关于如何使用CIFilters将文本添加到CVPixelBuffer的示例? - user924

0

Bannings的答案的Swift版本。

        let combinedFilter = CIFilter(name: "CISourceOverCompositing")!
        combinedFilter.setValue(maskImage.oriented(.left), forKey: "inputImage")
        combinedFilter.setValue(inputImage, forKey: "inputBackgroundImage")

        let outputImage = combinedFilter.outputImage!

        let tmpcontext = CIContext(options: nil)
        tmpcontext.render(outputImage, to: pixelBuffer, bounds: outputImage.extent, colorSpace: CGColorSpaceCreateDeviceRGB())

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接