Рендеринг динамического текста на 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];
- Здесь я беру пиксельный буфер, легкий как пирог.
- Я использую основную графику для записи текста в пустой UIImage (вот что
createTextImage
делает. Я был в состоянии проверить, что этот шаг работает; Я сохранил изображение с текстом, нарисованным на нем, на мои фотографии. - Я создаю CGImage из пиксельного буфера.
- Я делаю CIFilter для
CIBlendWithMask
, устанавливая входное изображение как созданное из исходного пиксельного буфера и входную маску какCIImage
сделано из изображения с текстом, нарисованным на нем. - Наконец, я отображаю выходное изображение фильтра в pixelBuffer.
CIContext
был создан заранее с[CIContext contextWithOptions:nil];
, - После всего этого я добавляю пиксельный буфер к своему
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 об этой же проблеме, так как все мои подходы работали очень медленно или делали странные вещи, и мне прислали следующее:
Что привело меня к рабочему решению очень быстро! Вы можете вообще обойти 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())