CMSampleBufferRef kCMSampleBufferAttachmentKey_TrimDurationAtStart 崩溃

6

这个问题困扰了我一段时间。我有一个视频转换器,用于将视频转换为“.mp4”格式。但是在某些视频上会发生崩溃,而不是所有视频。

以下是崩溃日志:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVAssetWriterInput appendSampleBuffer:] 
Cannot append sample buffer: First input buffer must have an appropriate kCMSampleBufferAttachmentKey_TrimDurationAtStart since the codec has encoder delay'

这是我的代码:

NSURL *uploadURL = [NSURL fileURLWithPath:[[NSTemporaryDirectory() stringByAppendingPathComponent:[self getVideoName]] stringByAppendingString:@".mp4"]];

AVAssetTrack *videoTrack = [[self.avAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
CGSize videoSize = videoTrack.naturalSize;
NSDictionary *videoWriterCompressionSettings =  [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1250000], AVVideoAverageBitRateKey, nil];
NSDictionary *videoWriterSettings = [NSDictionary dictionaryWithObjectsAndKeys:AVVideoCodecH264, AVVideoCodecKey, videoWriterCompressionSettings, AVVideoCompressionPropertiesKey, [NSNumber numberWithFloat:videoSize.width], AVVideoWidthKey, [NSNumber numberWithFloat:videoSize.height], AVVideoHeightKey, nil];
AVAssetWriterInput* videoWriterInput = [AVAssetWriterInput
                                        assetWriterInputWithMediaType:AVMediaTypeVideo
                                        outputSettings:videoWriterSettings];
videoWriterInput.expectsMediaDataInRealTime = YES;
videoWriterInput.transform = videoTrack.preferredTransform;

self.assetWriter = [[AVAssetWriter alloc] initWithURL:uploadURL fileType:AVFileTypeQuickTimeMovie error:nil];
[self.assetWriter addInput:videoWriterInput];

//setup video reader
NSDictionary *videoReaderSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey];
AVAssetReaderTrackOutput *videoReaderOutput = [[AVAssetReaderTrackOutput alloc] initWithTrack:videoTrack outputSettings:videoReaderSettings];
self.assetReader = [[AVAssetReader alloc] initWithAsset:self.avAsset error:nil];
[self.assetReader addOutput:videoReaderOutput];

//setup audio writer
AVAssetWriterInput* audioWriterInput = [AVAssetWriterInput
                                        assetWriterInputWithMediaType:AVMediaTypeAudio
                                        outputSettings:nil];

audioWriterInput.expectsMediaDataInRealTime = NO;
[self.assetWriter addInput:audioWriterInput];

//setup audio reader
AVAssetTrack* audioTrack = [[self.avAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
AVAssetReaderOutput *audioReaderOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil];
AVAssetReader *audioReader = [AVAssetReader assetReaderWithAsset:self.avAsset error:nil];
[audioReader addOutput:audioReaderOutput];

[self.assetWriter startWriting];
[self.assetReader startReading];
[self.assetWriter startSessionAtSourceTime:kCMTimeZero];

dispatch_queue_t processingQueue = dispatch_queue_create("processingQueue1", NULL);
[videoWriterInput requestMediaDataWhenReadyOnQueue:processingQueue
                                        usingBlock:^{
     while ([videoWriterInput isReadyForMoreMediaData])
     {
         CMSampleBufferRef sampleBuffer = NULL;
         if ([self.assetReader status] == AVAssetReaderStatusReading &&
             (sampleBuffer = [videoReaderOutput copyNextSampleBuffer])) {
             [videoWriterInput appendSampleBuffer:sampleBuffer];
             CFRelease(sampleBuffer);
         }
         else
         {
             [videoWriterInput markAsFinished];
             if ([self.assetReader status] == AVAssetReaderStatusCompleted)
             {
                 //start writing from audio reader
                 [audioReader startReading];
                 [self.assetWriter startSessionAtSourceTime:kCMTimeZero];
                 dispatch_queue_t processingQueue = dispatch_queue_create("processingQueue2", NULL);
                 [audioWriterInput requestMediaDataWhenReadyOnQueue:processingQueue usingBlock:^{
                     while (audioWriterInput.readyForMoreMediaData)
                     {
                         CMSampleBufferRef sampleBuffer;
                         if ([audioReader status] == AVAssetReaderStatusReading &&
                             (sampleBuffer = [audioReaderOutput copyNextSampleBuffer]))
                         {
                             if (sampleBuffer) {
                                 [audioWriterInput appendSampleBuffer:sampleBuffer];
                             }
                             CFRelease(sampleBuffer);
                         }
                         else
                         {
                             [audioWriterInput markAsFinished];
                             if ([audioReader status] == AVAssetReaderStatusCompleted) {
                                 [self.assetWriter finishWritingWithCompletionHandler:^(){
                                     [self createLiveTrailerApiForVideoId:video.dbId];
                                 }];
                             }
                         }
                     }

                 }];
             }
         }
     }
 }];

这是导致崩溃的部分。

CMSampleBufferRef sampleBuffer;
if ([audioReader status] == AVAssetReaderStatusReading &&
    (sampleBuffer = [audioReaderOutput copyNextSampleBuffer]))
{
    if (sampleBuffer) {
       [audioWriterInput appendSampleBuffer:sampleBuffer];
    }
    CFRelease(sampleBuffer);
}

我一直在搜索,似乎我需要将“kCMSampleBufferAttachmentKey_TrimDurationAtStart”设置为第一个缓冲区,但是找不到任何有关如何设置此值的示例。

请给予建议。谢谢!


徐音,你有找到这个问题的解决方法吗?我也遇到了同样的问题,还没有找到合适的解决方案。 - Tiến Nguyễn Hữu
2个回答

2
就像这样:
CFDictionaryRef dict = NULL;
if (firstBuffer) {
   firstBuffer = NO;
   dict = CMTimeCopyAsDictionary(CMTimeMake(1024, 44100), kCFAllocatorDefault);
   CMSetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_TrimDurationAtStart, dict, kCMAttachmentMode_ShouldNotPropagate);
            }

你为什么使用这些数字? - PIRATE FIFI

0
  • 根据Apple邮件列表上的线程,我建议你检查一下CMAttachment参考
  • 为了清晰起见,你应该在内部while循环中重命名你的`sampleBuffer`变量
  • 你尝试过在初始化音频读取器输出时传递一个包含kCMSampleBufferAttachmentKey_TrimDurationAtStart的字典吗?(不确定应该如何生成)

你好,AnderCover,我确实找到了那个邮件列表线程,但不幸的是,我仍然找不到解决我的问题的好方法... - Xu Yin
我认为我已经初始化了这个AVAssetReaderOutput,如果你在我粘贴的代码中搜索AVAssetReaderOutput,就会发现将代码放在注释中有点困难。但是我没有传递任何关于kCMSampleBufferAttachmentKey_TrimDurationAtStart的信息。你知道我该怎么做吗?谢谢! - Xu Yin
是的,您可以进行初始化,但如果没有添加任何输出设置,那么添加一个带有缺失的 kCMSampleBufferAttachmentKey_TrimDurationAtStart 键和其值的字典应该就可以解决问题了。 - AnderCover
我的问题是如何生成那个字典.. 你能给我一些示例代码吗? - Xu Yin
@XuYin,你找到解决方法了吗?我也遇到了同样的问题。 - user3500462

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接