Как управлять цветом вывода AVAssetWriter

У меня возникают проблемы с тем, чтобы цвета визуализированного видео соответствовали цветам исходного содержимого. Я визуализирую изображения в CGContext, конвертирую данные резервного копирования в CVPixelBuffer и добавляю их как фрейм в AVAssetWriterInputPixelBufferAdaptor. Это вызывает небольшие различия в цвете между изображениями, которые я рисую в CGContext, и результирующим видеофайлом.

Похоже, есть 3 вещи, которые необходимо решить:

  1. сообщить AVFoundation, в каком цветовом пространстве находится видео.
  2. сделайте так, чтобы AVAssetWriterInputPixelBufferAdaptor и CVPixelBuffers, которые я добавляю к нему, соответствовали этому цветовому пространству.
  3. используйте то же цветовое пространство для 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:^{}];

person Hooper    schedule 15.06.2018    source источник


Ответы (1)


Это довольно запутанная тема, и документы 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 по этой теме 1, 2, 3.

MetalBT709Decoder

person MoDJ    schedule 10.01.2019
comment
Связанный вопрос с изображениями в разных цветовых пространствах. stackoverflow.com/questions/53911662/ - person MoDJ; 10.01.2019