Как управлять цветом при выводе AVAssetWriter
У меня проблемы с получением цветов визуализированного видео в соответствии с цветами исходного контента. Я рендерил изображения в CGContext, преобразовывал данные поддержки в CVPixelBuffer и добавлял это как фрейм к AVAssetWriterInputPixelBufferAdaptor. Это вызывает небольшие различия в цвете между изображениями, которые я рисую в CGContext, и результирующим видеофайлом.
Похоже, есть 3 вещи, которые необходимо решить:
- скажите AVFoundation, в каком цветовом пространстве находится видео.
- сделайте AVAssetWriterInputPixelBufferAdaptor и CVPixelBuffers, которые я к нему добавляю, соответствующими этому цветовому пространству.
- используйте то же цветовое пространство для CGContext.
Документация ужасна, поэтому я был бы признателен за любые рекомендации о том, как сделать эти вещи или, если есть что-то еще, что мне нужно сделать, чтобы сохранить цвета на протяжении всего этого процесса.
Полный код:
AVAssetWriter *_assetWriter;
AVAssetWriterInput *_assetInput;
AVAssetWriterInputPixelBufferAdaptor *_assetInputAdaptor;
NSDictionary *outputSettings = @{ AVVideoCodecKey :AVVideoCodecH264,
AVVideoWidthKey :@(outputWidth),
AVVideoHeightKey:@(outputHeight)};
_assetInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo
outputSettings:outputSettings];
NSDictionary *bufferAttributes = @{å(NSString*)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32ARGB)};
_assetInputAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:_assetInput
sourcePixelBufferAttributes:bufferAttributes];
_assetWriter = [AVAssetWriter assetWriterWithURL:aURL fileType:AVFileTypeMPEG4 error:nil];
[_assetWriter addInput:_assetInput];
[_assetWriter startWriting];
[_assetWriter startSessionAtSourceTime:kCMTimeZero];
NSInteger bytesPerRow = outputWidth * 4;
long size = bytesPerRow * outputHeight;
CGColorSpaceRef srgbSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
UInt8 *data = (UInt8 *)calloc(size, 1);
CGContextRef ctx = CGBitmapContextCreateWithData(data, outputWidth, outputHeight, 8, bytesPerRow, srgbSpace, kCGImageAlphaPremultipliedFirst, NULL, NULL);
// draw everything into ctx
CVPixelBufferRef pixelBuffer;
CVPixelBufferCreateWithBytes(kCFAllocatorSystemDefault,
outputWidth, outputHeight,
k32ARGBPixelFormat,
data,
bytesPerRow,
ReleaseCVPixelBufferForCVPixelBufferCreateWithBytes,
NULL,
NULL,
&pixelBuffer);
NSDictionary *pbAttachements = @{(id)kCVImageBufferCGColorSpaceKey : (__bridge id)srgbSpace};
CVBufferSetAttachments(pixelBuffer, (__bridge CFDictionaryRef)pbAttachements, kCVAttachmentMode_ShouldPropagate);
[_assetInputAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:CMTimeMake(0, 60)];
CGColorSpaceRelease(srgbSpace);
[_assetInput markAsFinished];
[_assetWriter finishWritingWithCompletionHandler:^{}];
0 ответов
Это довольно запутанная тема, и документы Apple действительно не сильно помогают. Я собираюсь описать решение, которое я выбрал на основе использования цветового пространства BT.709, я уверен, что у кого-то будут возражения, основанные на колориметрической правильности и странности различных стандартов видео, но это сложная тема. Прежде всего, не используйте kCVPixelFormatType_32ARGB в качестве типа пикселя. Вместо этого всегда передавайте kCVPixelFormatType_32BGRA, поскольку BGRA - это макет собственного пикселя как для MacOSX, так и для iPhone, и BGRA работает быстрее. Затем, когда вы создаете CGBitmapContext для рендеринга, используйте цветовое пространство BT.709 (kCGColorSpaceITUR_709). Кроме того, не выполняйте рендеринг в буфер malloc(), а выполняйте рендеринг непосредственно в пиксельный буфер CoreVideo, создавая растровый контекст в той же памяти, CoreGraphics будет обрабатывать цветовое пространство и гамма-преобразование из любого входного изображения в BT.709 и его связанная гамма. Затем вам нужно сообщить AVFoundation цветовое пространство буфера пикселей, сделайте это, скопировав профиль ICC и установив kCVImageBufferICCProfileKey в пиксельном буфере CoreVideo. Это решает ваши проблемы 1 и 2, вам не нужно иметь входные изображения в том же цветовом пространстве с этим подходом. Теперь, конечно, это сложный и фактический рабочий исходный код (да, действительно рабочий), который трудно найти. Вот ссылка github на небольшой проект, который выполняет эти точные шаги, код лицензирован BSD, так что не стесняйтесь использовать его. Обратите особое внимание на класс H264Encoder, который превращает весь этот ужас в повторно используемый модуль. Вы можете найти код вызова в encode_h264.m, это небольшая командная строка MacOSX для кодирования PNG в M4V. Также прилагаются 3 ключа Apple Docs, относящиеся к этой теме 1, 2, 3.