与原始NAL单元一起使用AVAssetWriter
我注意到在AVAssetWriterInput
的iOS文档中,你可以传递nil
给outputSettings
字典来指定输入数据不应该被重新编码。
用于编码附加到输出的媒体的设置。 通过零以指定附加采样不应重新编码。
我想利用这个特性来传入原始H.264 NAL流,但是我无法将我的原始字节流转换为可以传入AVAssetWriterInput的appendSampleBuffer
方法的CMSampleBuffer
。 我的NAL流只包含SPS / PPS / IDR / P NAL(1,5,7,8)。 我一直无法找到有关如何在AVAssetWriter中使用预编码的H264数据的文档或结论性答案。 生成的视频文件无法播放。
我如何正确地将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
,并在我实际尝试追加它之前认为这是有效的时间。
任何帮助表示赞赏。
我设法让视频播放在VLC中工作,但不是QuickTime。 我使用的代码类似于我在上面发布的将H.264 NAL转换为CMSampleBuffers的代码。
我有两个主要问题:
为了解决#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单元的前缀是一个32位的起始码,等于0x00000001
。
大概出于同样的原因,它不是在QuickTime中播放,我仍然无法将生成的.mov文件移动到相册( ALAssetLibrary
方法videoAtPathIsCompatibleWithSavedPhotosAlbum
失败,指出“电影无法播放”。希望有人有想法关于发生了什么事可以发表评论。谢谢!