为什么 AVFoundation 不接受 iOS 设备上的平面像素缓冲区?



我一直在努力弄清楚我的代码有什么问题。我正在创建一个平面CVPixelBufferRef来写入AVAssetWriter。这个像素缓冲区是通过其他过程手动创建的(即,我不会从相机或类似的东西中获取这些样本)。在iOS模拟器上,它可以添加样本并创建有效的输出电影。

但在该设备上,它在第一次采样时立即失败,并提供了少于无用的错误信息:

AVAssetWriterError: Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSUnderlyingError=0x12fd2c670 {Error Domain=NSOSStatusErrorDomain Code=-12780 "(null)"}, NSLocalizedFailureReason=An unknown error occurred (-12780), NSLocalizedDescription=The operation could not be completed}

我对像素格式很陌生,如果我以某种方式创建了无效的像素缓冲区,我也不会感到惊讶,但它在模拟器(即OS X)上运行得很好,这让我感到困惑。

这是我的代码:

const int pixelBufferWidth = img->get_width();
const int pixelBufferHeight = img->get_height();
size_t planeWidths[3];
size_t planeHeights[3];
size_t planeBytesPerRow[3];
void* planeBaseAddresses[3];
for (int c=0;c<3;c++) {
  int stride;
  const uint8_t* p = de265_get_image_plane(img, c, &stride);
  int width = de265_get_image_width(img,c);
  int height = de265_get_image_height(img, c);
  planeWidths[c] = width;
  planeHeights[c] = height;
  planeBytesPerRow[c] = stride;
  planeBaseAddresses[c] = const_cast<uint8_t*>(p);
}
void* descriptor = calloc(1, sizeof(CVPlanarPixelBufferInfo_YCbCrPlanar));
CVPixelBufferRef pixelBufferRef;
CVReturn result = CVPixelBufferCreateWithPlanarBytes(NULL,
                                                     pixelBufferWidth,
                                                     pixelBufferHeight,
                                                     kCVPixelFormatType_420YpCbCr8Planar,
                                                     NULL,
                                                     0,
                                                     3,
                                                     planeBaseAddresses,
                                                     planeWidths,
                                                     planeHeights,
                                                     planeBytesPerRow,
                                                     &pixelBufferReleaseCallback,
                                                     NULL,
                                                     NULL,
                                                     &pixelBufferRef);

CMFormatDescriptionRef formatDescription = NULL;
CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixelBufferRef, &formatDescription);
if (assetWriter == nil) {
  // ... create output file path in Caches directory
  assetWriter = [AVAssetWriter assetWriterWithURL:fileOutputURL fileType:AVFileTypeMPEG4 error:nil];
  NSDictionary *videoSettings = @{AVVideoCodecKey : AVVideoCodecH264,
                                  AVVideoWidthKey : @(pixelBufferWidth),
                                  AVVideoHeightKey : @(pixelBufferHeight),
                                  AVVideoCompressionPropertiesKey : @{AVVideoMaxKeyFrameIntervalKey : @1}};
  assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings sourceFormatHint:formatDescription];
  [assetWriter addInput:assetWriterInput];
  NSDictionary *pixelBufferAttributes = @{(id)kCVPixelBufferPixelFormatTypeKey : @(kCVPixelFormatType_420YpCbCr8Planar),
                                          (id)kCVPixelBufferWidthKey : @(pixelBufferWidth),
                                          (id)kCVPixelBufferHeightKey : @(pixelBufferHeight)};
  pixelBufferAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:assetWriterInput sourcePixelBufferAttributes:pixelBufferAttributes];
  [assetWriter startWriting];
  [assetWriter startSessionAtSourceTime:kCMTimeZero];
}
samplePresentationTime = CMTimeMake(frameIndex++, framesPerSecond);
BOOL success = [pixelBufferAdaptor appendPixelBuffer:pixelBufferRef withPresentationTime:samplePresentationTime];

success总是NO,来自资产编写器的错误就是我在上面粘贴的。

我还尝试手动创建示例缓冲区,而不是使用AVAssetWriterInputPixelBufferAdaptor来消除这一可能的问题,但结果是一样的。

同样,这个在模拟器上运行,所以我知道我的像素缓冲区确实包含正确的数据。

此外,我还验证了我可以写入到文件目的地。我尝试在那个位置创建一个伪文件,它成功了。

我想避免将我的缓冲区转换为RGB,因为我不应该这样做。我一开始就有Y’CbCr缓冲区,我只想将它们编码为支持Y’Cb Cr的H.264视频。

创建这些缓冲区的源状态如下:

The image is currently always 3-channel YCbCr, with 4:2:0 chroma.

我确认它总是进入处理8位YUV通道的循环逻辑。

我做错了什么?

所以,我不能正式确认这一点,但AVAssetWriter似乎不喜欢iOS上的3平面像素格式(即kCVPixelFormatType_420YpCbCr8Planar)。在OSX上,它几乎可以与任何东西配合使用。当我将3平面缓冲区转换为双平面像素缓冲区格式时,这在iOS上起到了作用。这并不奇怪,因为相机本身以kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange像素格式拍摄,所以AV基金会可能也会使用这种格式。

尽管vImageConvert_PlanarToChunky8有助于将Cb和Cr平面交错成一个平面,但如果我不必自己做这个显式转换步骤,那就太好了。

最新更新