7

我有一个由类型 1(P 帧)、5(I 帧)、7(SPS)和 8(PPS)组成的 H.264/AVC NAL 流。我想将它们写入 .mov 文件而不重新编码。我正在尝试使用它AVAssetWriter来执行此操作。状态的文档AVAssetWriterInput

为 outputSettings 传递 nil 指示输入通过附加的样本,在将它们写入输出文件之前不进行任何处理。例如,如果您要附加已采用所需压缩格式的缓冲区,这将很有用。但是,目前仅在写入 QuickTime 电影文件时才支持直通(即 AVAssetWriter 是使用 AVFileTypeQuickTimeMovie 初始化的)。对于其他文件类型,您必须指定非零输出设置。

我正在尝试从这些 NAL 中创建 CMSampleBuffers 并将它们附加到资产编写器输入中,但是我无法以产生格式良好的 .mov 文件的方式输入数据,而且我在任何地方都找不到任何线索关于如何做到这一点。

到目前为止,我得到的最好结果是以附件 B 字节流格式(按 7 8 5 1 1 1....重复的顺序)传递 NAL,并在 VLC 中播放结果。因此,我知道 NAL 包含有效数据,但由于 .mov 文件没有 avcC 原子并且 mdat 原子填充了附件 B 字节流,QuickTime 不会播放视频。

现在我正在尝试使用 4 字节(由lengthSizeMinusOne字段指定)长度字段而不是附件 B 分隔符传入 NAL,据我所知,这就是它们应该被打包到 mdat 原子中的方式.

我不知道如何让资产编写者编写 avcC 原子。我添加的每个样本都会被推入 mdat 原子。

有谁知道我如何将原始 H.264 数据传递到配置为通过(nil outputSettings)的 AVAssetWriterInput 并让它生成正确格式的 QuickTime 文件?

4

1 回答 1

9

我已经向苹果提交了一份 TSI 并找到了答案。我希望这可以节省将来的时间。

CMSampleBuffers 与它们关联了一个 CMFormatDescription,其中包含对样本缓冲区中数据的描述。

创建格式描述的函数原型如下:

OSStatus CMVideoFormatDescriptionCreate (
  CFAllocatorRef allocator,
  CMVideoCodecType codecType,
  int32_t width,
  int32_t height,
  CFDictionaryRef extensions,
  CMVideoFormatDescriptionRef *outDesc
);

我从 Apple 技术人员那里了解到,我可以使用 extensions 参数传入包含 avcC atom 数据的字典。

扩展字典应采用以下形式:

[kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms ---> ["avcC" ---> <avcC Data>]]

[] 代表字典。除了 avcC 之外,该字典还可以潜在地用于传递任意原子的数据。

这是我用来创建extensions我传入的字典的代码CMVideoFormatDescriptionCreate

    const char *avcC = "avcC";
    const CFStringRef avcCKey = CFStringCreateWithCString(kCFAllocatorDefault, avcC, kCFStringEncodingUTF8);
    const CFDataRef avcCValue = CFDataCreate(kCFAllocatorDefault, [_avccData bytes], [_avccData length]);
    const void *atomDictKeys[] = { avcCKey };
    const void *atomDictValues[] = { avcCValue };
    CFDictionaryRef atomsDict = CFDictionaryCreate(kCFAllocatorDefault, atomDictKeys, atomDictValues, 1, nil, nil);

    const void *extensionDictKeys[] = { kCMFormatDescriptionExtension_SampleDescriptionExtensionAtoms };
    const void *extensionDictValues[] = { atomsDict };
    CFDictionaryRef extensionDict = CFDictionaryCreate(kCFAllocatorDefault, extensionDictKeys, extensionDictValues, 1, nil, nil);
于 2013-04-03T23:16:02.787 回答