如何对 AVAssetWriter 输出进行颜色管理

Posted

技术标签:

【中文标题】如何对 AVAssetWriter 输出进行颜色管理【英文标题】:How to color manage AVAssetWriter output 【发布时间】:2018-06-15 18:08:39 【问题描述】:

我无法让渲染视频的颜色与源内容的颜色相匹配。我将图像渲染到 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:^];

【问题讨论】:

【参考方案1】:

这是一个相当令人困惑的主题,Apple 文档确实没有太大帮助。我将描述基于使用 BT.709 色彩空间的解决方案,我相信有人会基于色度正确性和各种视频标准的怪异性提出异议,但这是一个复杂的话题。首先,不要使用 kCVPixelFormatType_32ARGB 作为像素类型。始终传递 kCVPixelFormatType_32BGRA,因为 BGRA 是 MacOSX 和 iPhone 硬件上的原生像素布局,而且 BGRA 更快。接下来,当您创建 CGBitmapContext 以使用 BT.709 颜色空间 (kCGColorSpaceITUR_709) 进行渲染时。此外,不要渲染到 malloc() 缓冲区,通过在同一内存上创建位图上下文直接渲染到 CoreVideo 像素缓冲区,CoreGraphics 将处理从您的输入图像到 BT.709 及其相关伽玛。然后你需要告诉 AVFoundation 像素缓冲区的颜色空间,通过制作 ICC 配置文件副本并在 CoreVideo 像素缓冲区上设置 kCVImageBufferICCProfileKey 来做到这一点。这可以解决您的问题 1 和 2,您不需要使用这种方法在相同的色彩空间中输入图像。现在,这当然很复杂,而且实际工作的源代码(是的,实际工作的)很难获得。这是一个执行这些确切步骤的小项目的 github 链接,代码是 BSD 许可的,所以请随意使用。特别注意 H264Encoder 类,它将所有这些恐怖包装到一个可重用的模块中。你可以在 encode_h264.m 中找到调用代码,这是一个 MacOSX 命令行工具,用于将 PNG 编码为 M4V。还附上了 3 个与此主题相关的 Apple 文档 1、2、3。

MetalBT709Decoder

【讨论】:

与不同色彩空间中的图像相关的问题。 ***.com/questions/53911662/…

以上是关于如何对 AVAssetWriter 输出进行颜色管理的主要内容,如果未能解决你的问题,请参考以下文章

AVAssetWriter视频数据编码

AVAssetWriter - 设置自定义帧率

旋转视频而不旋转 AVCaptureConnection 并在 AVAssetWriter 会话中间

使用 AVAssetWriter 和 AVAssetReader 进行音频录制和播放

如何使用python进行颜色输出? [复制]

AVAssetWriter 停止在 iOS7 中写入文件