【问题标题】:Using AVAssetWriter with raw NAL Units将 AVAssetWriter 与原始 NAL 单元一起使用
【发布时间】:2013-03-26 03:53:22
【问题描述】:

我注意到在 AVAssetWriterInput 的 iOS 文档中,您可以将 nil 传递给 outputSettings 字典以指定不应重新编码输入数据。

用于对附加到输出的媒体进行编码的设置。传递 nil 以指定不应重新编码附加的样本。

我想利用此功能传入原始 H.264 NAL 流,但我无法将原始字节流调整为 CMSampleBuffer,我可以将其传入 AVAssetWriterInput 的 appendSampleBuffer 方法。我的 NAL 流仅包含 SPS/PPS/IDR/P NAL(1、5、7、8)。我无法找到有关如何将预编码的 H264 数据与 AVAssetWriter 一起使用的文档或结论性答案。生成的视频文件无法播放。

如何将 NAL 单元正确打包到 CMSampleBuffers 中?我需要使用起始代码前缀吗?长度前缀?我是否需要确保每个CMSampleBuffer 只放一个 NAL?我的最终目标是使用 H264/AAC 创建 MP4 或 MOV 容器。

这是我一直在玩的代码:

-(void)addH264NAL:(NSData *)nal
{
    dispatch_async(recordingQueue, ^{
        //Adapting the raw NAL into a CMSampleBuffer
        CMSampleBufferRef sampleBuffer = NULL;
        CMBlockBufferRef blockBuffer = NULL;
        CMFormatDescriptionRef formatDescription = NULL;
        CMItemCount numberOfSampleTimeEntries = 1;
        CMItemCount numberOfSamples = 1;


        CMVideoFormatDescriptionCreate(kCFAllocatorDefault, kCMVideoCodecType_H264, 480, 360, nil, &formatDescription);
        OSStatus result = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, NULL, [nal length], kCFAllocatorDefault, NULL, 0, [nal length], kCMBlockBufferAssureMemoryNowFlag, &blockBuffer);
        if(result != noErr)
        {
            NSLog(@"Error creating CMBlockBuffer");
            return;
        }
        result = CMBlockBufferReplaceDataBytes([nal bytes], blockBuffer, 0, [nal length]);
        if(result != noErr)
        {
            NSLog(@"Error filling CMBlockBuffer");
            return;
        }
        const size_t sampleSizes = [nal length];
        CMSampleTimingInfo timing = { 0 };
        result = CMSampleBufferCreate(kCFAllocatorDefault, blockBuffer, YES, NULL, NULL, formatDescription, numberOfSamples, numberOfSampleTimeEntries, &timing, 1, &sampleSizes, &sampleBuffer);

        if(result != noErr)
        {
            NSLog(@"Error creating CMSampleBuffer");
        }
        [self writeSampleBuffer:sampleBuffer ofType:AVMediaTypeVideo];
    });
}

请注意,我在writeSampleBuffer 方法内的样本缓冲区上调用CMSampleBufferSetOutputPresentationTimeStamp,我认为这是在我实际尝试附加它之前的有效时间。

感谢任何帮助。

【问题讨论】:

  • 我的问题至少有一部分是我如何处理 CMSampleTimingInfo。我提到我正在使用setOutputPresentationTimeStamp 填写一个真实的时间戳。我现在意识到我还需要填写 CMSampleTimingInfo 的其他字段。我将decodeTimeStamp 设置为kCMTimeInvalid,将duration 设置为CMTimeMake(1, 30)。我现在得到一个总时间合适的可搜索视频容器,但没有视频(在 VLC 中测试)。

标签: iphone ios h.264 avassetwriter cmsamplebufferref


【解决方案1】:

我设法在 VLC 但不是 QuickTime 中播放视频。我使用类似于我上面发布的代码将 H.264 NAL 放入 CMSampleBuffers。

我有两个主要问题:

  1. 我没有正确设置 CMSampleTimingInfo(正如我上面的评论所述)。
  2. 我没有正确打包原始 NAL 数据(不确定记录在哪里,如果有的话)。

为了解决 #1,我设置了 timing.duration = CMTimeMake(1, fps);,其中 fps 是预期的帧速率。然后我设置timing.decodeTimeStamp = kCMTimeInvalid; 表示样本将按解码顺序给出。最后,我通过计算绝对时间来设置timing.presentationTimeStamp,我也将其与startSessionAtSourceTime一起使用。

为了解决 #2,通过反复试验,我发现以以下形式给出我的 NAL 单元是有效的:

[7 8 5] [1] [1] [1]..... [7 8 5] [1] [1] [1]..... (repeating)

每个 NAL 单元都以等于 0x00000001 的 32 位起始码为前缀。

大概出于同样的原因,它没有在 QuickTime 中播放,我仍然无法将生成的 .mov 文件移动到相册(ALAssetLibrary 方法 videoAtPathIsCompatibleWithSavedPhotosAlbum 未能说明“无法播放电影。”希望对发生的事情有想法的人可以发表评论。谢谢!

【讨论】:

  • 鉴于这仅适用于 VLC,我有可能需要更改将 NAL 打包到缓冲区中的方式,以便 QuickTime 工作。有人有什么想法吗?
  • 我对生成的 mov 文件进行了十六进制转储,看起来文件顶部存在一个 mdat 原子,底部存在一对 avc1 原子,但没有 avcC 原子。在 mdat 原子内部似乎是我在这个答案中描述的 NAL 流(由 0x00000001 前缀分隔)。我假设 QuickTime 拒绝播放该文件,因为 mdat 下的数据是 Annex-B 格式,并且不存在 avcC atom。我需要弄清楚如何正确地将数据提供给 AVAssetWriterInput。
  • 我设法让一切正常。我将 NAL 长度添加到每个 NAL(而不是 0x00000001),并且我还设法将 avcC 数据正确传递到 mov 容器中。在这里查看我的问题和答案:stackoverflow.com/questions/15673379/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-16
  • 2012-10-16
  • 2011-08-29
  • 2019-09-24
  • 2016-06-28
  • 1970-01-01
相关资源
最近更新 更多