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

Dan*_*iel 5 macos video core-graphics avfoundation color-space

我无法使渲染视频的颜色与源内容的颜色相匹配。我将图像渲染到 CGContext 中,将支持数据转换为 CVPixelBuffer 并将其作为帧附加到 AVAssetWriterInputPixelBufferAdaptor 中。这会导致我绘制到 CGContext 中的图像与生成的视频文件之间存在轻微的颜色差异。

\n\n

看来有3件事需要解决:

\n\n
    \n
  1. 告诉 AVFoundation 视频所在的色彩空间。
  2. \n
  3. 使 AVAssetWriterInputPixelBufferAdaptor 和我附加到它的 CVPixelBuffers 与该颜色空间匹配。
  4. \n
  5. 对 CGContext 使用相同的色彩空间。
  6. \n
\n\n

该文档很糟糕,所以我希望获得有关如何执行这些操作的任何指导,或者我是否需要做其他事情来使颜色在整个过程中得以保留。

\n\n

完整代码:

\n\n
AVAssetWriter                        *_assetWriter;\nAVAssetWriterInput                   *_assetInput;\nAVAssetWriterInputPixelBufferAdaptor *_assetInputAdaptor;\n\nNSDictionary *outputSettings = @{ AVVideoCodecKey :AVVideoCodecH264,\n                                  AVVideoWidthKey :@(outputWidth),\n                                  AVVideoHeightKey:@(outputHeight)};\n\n_assetInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo\n                                                 outputSettings:outputSettings];\n\n\nNSDictionary *bufferAttributes = @{\xc3\xa5(NSString*)kCVPixelBufferPixelFormatTypeKey:@(kCVPixelFormatType_32ARGB)};\n_assetInputAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:_assetInput\n                                                                                      sourcePixelBufferAttributes:bufferAttributes];\n\n\n_assetWriter = [AVAssetWriter assetWriterWithURL:aURL fileType:AVFileTypeMPEG4 error:nil];\n[_assetWriter addInput:_assetInput];\n[_assetWriter startWriting];\n[_assetWriter startSessionAtSourceTime:kCMTimeZero];\n\nNSInteger bytesPerRow = outputWidth * 4;\nlong size = bytesPerRow * outputHeight;\nCGColorSpaceRef srgbSpace = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);\n\nUInt8 *data = (UInt8 *)calloc(size, 1);\nCGContextRef ctx = CGBitmapContextCreateWithData(data, outputWidth, outputHeight, 8, bytesPerRow, srgbSpace, kCGImageAlphaPremultipliedFirst, NULL, NULL);\n\n// draw everything into ctx\n\nCVPixelBufferRef pixelBuffer;\nCVPixelBufferCreateWithBytes(kCFAllocatorSystemDefault,\n                                 outputWidth, outputHeight,\n                                 k32ARGBPixelFormat,\n                                 data,\n                                 bytesPerRow,\n                                 ReleaseCVPixelBufferForCVPixelBufferCreateWithBytes,\n                                 NULL,\n                                 NULL,\n                             &pixelBuffer);\n\nNSDictionary *pbAttachements = @{(id)kCVImageBufferCGColorSpaceKey : (__bridge id)srgbSpace};\nCVBufferSetAttachments(pixelBuffer, (__bridge CFDictionaryRef)pbAttachements, kCVAttachmentMode_ShouldPropagate);\n[_assetInputAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:CMTimeMake(0, 60)];\n\nCGColorSpaceRelease(srgbSpace);\n\n[_assetInput markAsFinished];\n[_assetWriter finishWritingWithCompletionHandler:^{}];\n
Run Code Online (Sandbox Code Playgroud)\n

MoD*_*oDJ 5

这是一个相当令人困惑的主题,苹果文档确实没有多大帮助。我将描述我基于使用 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

金属BT709解码器