简体   繁体   English

将AVAssetWriter与原始NAL单元一起使用

[英]Using AVAssetWriter with raw NAL Units

I noticed in the iOS documentation for AVAssetWriterInput you can pass nil for the outputSettings dictionary to specify that the input data should not be re-encoded. 我在iOS文档的AVAssetWriterInput注意到,可以为outputSettings字典传递nil ,以指定不应重新编码输入数据。

The settings used for encoding the media appended to the output. 用于编码附加到输出的媒体的设置。 Pass nil to specify that appended samples should not be re-encoded. 传递nil来指定不应重新编码附加的样本。

I want to take advantage of this feature to pass in a stream of raw H.264 NALs, but I am having trouble adapting my raw byte streams into a CMSampleBuffer that I can pass into AVAssetWriterInput's appendSampleBuffer method. 我想利用此功能来传递原始H.264 NAL流,但是我无法将原始字节流适配到CMSampleBuffer ,而我可以将其传递给AVAssetWriterInput的appendSampleBuffer方法。 My stream of NALs contains only SPS/PPS/IDR/P NALs (1, 5, 7, 8). 我的NAL流仅包含SPS / PPS / IDR / P NAL(1、5、7、8)。 I haven't been able to find documentation or a conclusive answer on how to use pre-encoded H264 data with AVAssetWriter. 我还没有找到有关如何将预编码的H264数据与AVAssetWriter一起使用的文档或最终答案。 The resulting video file is not able to be played. 生成的视频文件无法播放。

How can I properly package the NAL units into CMSampleBuffers ? 如何将NAL单元正确打包到CMSampleBuffers Do I need to use a start code prefix? 我需要使用起始码前缀吗? A length prefix? 长度前缀? Do I need to ensure I only put one NAL per CMSampleBuffer ? 我是否需要确保每个CMSampleBuffer仅放置一个NAL? My end goal is to create an MP4 or MOV container with H264/AAC. 我的最终目标是用H264 / AAC创建一个MP4或MOV容器。

Here's the code I've been playing with: 这是我一直在玩的代码:

-(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];
    });
}

Note that I'm calling CMSampleBufferSetOutputPresentationTimeStamp on the sample buffer inside of the writeSampleBuffer method with what I think is a valid time before I'm actually trying to append it. 请注意,我要在writeSampleBuffer方法内部的样本缓冲区上调用CMSampleBufferSetOutputPresentationTimeStamp ,在我实际尝试附加它之前,我认为这是一个有效时间。

Any help is appreciated. 任何帮助表示赞赏。

I managed to get video playback working in VLC but not QuickTime. 我设法在VLC中播放视频,但不能在QuickTime中播放。 I used code similar to what I posted above to get H.264 NALs into CMSampleBuffers. 我使用类似于上面发布的代码将H.264 NAL放入CMSampleBuffers。

I had two main issues: 我有两个主要问题:

  1. I was not setting CMSampleTimingInfo correctly (as my comment above states). 我没有正确设置CMSampleTimingInfo(如我上面的评论所述)。
  2. I was not packing the raw NAL data correctly (not sure where this is documented, if anywhere). 我没有正确打包原始NAL数据(不确定记录在哪里,如果有的话)。

To solve #1, I set timing.duration = CMTimeMake(1, fps); 为了解决#1,我设置了timing.duration = CMTimeMake(1, fps); where fps is the expected frame rate. 其中fps是预期的帧速率。 I then set timing.decodeTimeStamp = kCMTimeInvalid; 然后,我设置timing.decodeTimeStamp = kCMTimeInvalid; to mean that the samples will be given in decoding order. 表示将按照解码顺序给出样本。 Lastly, I set timing.presentationTimeStamp by calculating the absolute time, which I also used with startSessionAtSourceTime . 最后,我通过计算绝对时间来设置timing.presentationTimeStamp ,我还将其与startSessionAtSourceTime一起startSessionAtSourceTime

To solve #2, through trial and error I found that giving my NAL units in the following form worked: 为了解决#2,通过反复试验,我发现以以下形式提供NAL单元是可行的:

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

Where each NAL unit is prefixed by a 32-bit start code equaling 0x00000001 . 其中每个NAL单元均以等于0x00000001的32位起始码作为前缀。

Presumably for the same reason it's not playing in QuickTime, I'm still having trouble moving the resulting .mov file to the photo album (the ALAssetLibrary method videoAtPathIsCompatibleWithSavedPhotosAlbum is failing stating that the "Movie could not be played." Hopefully someone with an idea about what's going on can comment. Thanks! 大概是出于与不在QuickTime中播放相同的原因,我仍然无法将生成的.mov文件移动到相册中( ALAssetLibrary方法videoAtPathIsCompatibleWithSavedPhotosAlbum无法说明“无法播放电影”。希望有人提出了这一想法。关于正在发生的事情可以发表评论。谢谢!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM