Рендеринг динамического текста на CVPixelBufferRef во время записи видео

Я записываю видео и аудио, используя AVCaptureVideoDataOutput а также AVCaptureAudioDataOutput и в 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. Наконец, я отображаю выходное изображение фильтра в pixelBuffer. 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;
}

4 ответа

Хотите как ниже?введите описание изображения здесь

Вместо того, чтобы использовать CIBlendWithMask, вы должны использовать CISourceOverCompositing, попробуй это:

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

Вы также можете использовать CoreGraphics а также CoreText рисовать прямо поверх существующего CVPixelBufferRef если это RGBA (или на копии, если это YUV). У меня есть пример кода в этом ответе: /questions/2210960/kak-mne-narisovat-na-cvpixelbufferref-kotoryij-yavlyaetsya-ploskim-ycbcr420fyuvnv12-ne-rgb/2210967#2210967

Я спросил Apple DTS об этой же проблеме, так как все мои подходы работали очень медленно или делали странные вещи, и мне прислали следующее:

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

Что привело меня к рабочему решению очень быстро! Вы можете вообще обойти CVPixelBuffer, используя CIFilter, с которым IMHO намного проще работать. Так что если вам на самом деле НЕ НУЖНО использовать CVPixelBuffer, то этот подход быстро станет вашим новым другом.

Комбинация CIFilter(s), чтобы объединить исходное изображение и изображение с текстом, который я генерировал для каждого кадра, добилась цели.

Я надеюсь, что это помогает кому-то еще!

Быстрая версия ответа Баннингса.

        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())
Другие вопросы по тегам