AVAssetExportSession停止进行

时间:2012-05-23 20:43:15

标签: ios avfoundation avassetexportsession

我遇到AVAssetExportSession的问题,其中进度停止增加,但状态仍然表示它正在导出。这实际上是非常罕见的,它在99.99%的时间内完美无缺地工作,但无论如何我想解决这个问题。

所以我开始导出:

    exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];
    exportSession.videoComposition = videoComposition; 
    exportSession.outputFileType = @"com.apple.quicktime-movie";
    exportSession.outputURL = outputURL;
    [exportSession exportAsynchronouslyWithCompletionHandler:^{
        ...
    }];

然后有一个计时器检查进度:

    AVAssetExportSessionStatus status = [exportSession status];
    float progress = 0;
    if (status == AVAssetExportSessionStatusExporting) {
        progress = [exportSession progress];
    } else if (status == AVAssetExportSessionStatusCompleted) {
        progress = 1;
    }
    NSLog(@"%d %f", status, progress);
    [delegate processor:self didProgress:progress];

输出最终看起来像:

2012-05-23 14:28:59.494 **********[1899:707] 2 0.125991
2012-05-23 14:28:59.994 **********[1899:707] 2 0.185280
2012-05-23 14:29:00.494 **********[1899:707] 2 0.259393
2012-05-23 14:29:00.994 **********[1899:707] 2 0.326093
2012-05-23 14:29:01.494 **********[1899:707] 2 0.400206
2012-05-23 14:29:01.995 **********[1899:707] 2 0.481729
2012-05-23 14:29:02.495 **********[1899:707] 2 0.541019
2012-05-23 14:29:02.997 **********[1899:707] 2 0.622542
2012-05-23 14:29:03.493 **********[1899:707] 2 0.681832
2012-05-23 14:29:03.995 **********[1899:707] 2 0.763355
2012-05-23 14:29:04.494 **********[1899:707] 2 0.822645
2012-05-23 14:29:04.994 **********[1899:707] 2 0.880082
2012-05-23 14:29:05.493 **********[1899:707] 2 0.880082
2012-05-23 14:29:05.994 **********[1899:707] 2 0.880082
...
2012-05-23 14:43:22.994 **********[1899:707] 2 0.880082
2012-05-23 14:43:23.493 **********[1899:707] 2 0.880082
2012-05-23 14:43:23.994 **********[1899:707] 2 0.880082
2012-05-23 14:43:24.494 **********[1899:707] 2 0.880082

(注意:每次都不会以相同的百分比停止,它完全随机)

正如您从时间戳中看到的那样,花费5秒钟来完成前88%,然后让它再运行13分钟(完整的视频处理通常不需要超过10秒),而且没有任何变化进展。

目前我唯一的选择是检查过去X秒内的进度是否没有改变,只是告诉用户失败并重试。

有人有什么想法吗?

4 个答案:

答案 0 :(得分:4)

在我的情况下,这个问题源于用异常配置编码的mp4。但最终苹果公司代码中的一个错误导致了这个问题。

具体来说,我在Facebook上使用mp4s。似乎当facebook对视频进行编码时,它会丢弃或移动视频轨道的第一帧。用ffprobe检查视频显示:

        "r_frame_rate": "722/25",
        "avg_frame_rate": "722/25",
        "time_base": "1/28880",
        "start_pts": 1000,
        "start_time": "0.034626",

0.034626正好是第2帧的时间。然而,音轨从0开始。

不幸的是,Apple的API无法正确报告该轨道的开始时间。从AVAsset获取AVAssetTrack并检查其timeRange后,它将开始时间报告为0.我可能误解了跟踪和资产计时的性质,但这似乎是一个错误。

当然,在为导出会话构建视频合成时,我使用的轨道时间范围不正确。我怀疑当导出会话在时间0查找轨道中的数据时发现什么都不会混淆,然后正如我们所见,只是挂起,甚至没有报告错误 - 另一个错误。甚至使用videoCompositionWithPropertiesOfAsset:创建合成也无济于事。

我还没有尝试过使用AVAssetReader和AVAssetWriter而不是AVAssetExportSession,所以我不知道你是否会遇到相同的情况。在我有更多时间去探索之前,我已经提出了这个黑客作为解决方案:

将用于轨道插入的时间范围和视频指令重置为1或2帧:

CMTimeRange videoTimeRange = videoTrack.timeRange;
videoTimeRange.start = CMTimeMake(1, ceil(videoTrack.nominalFrameRate));

显然,这个hack只适用于视频轨道开始时间不为0的特定情况。我猜这个错误通常来自AVAssetExportSession及其相关类不期望的异常媒体编码。

答案 1 :(得分:3)

我发现了!

您需要设置正确的layerInstructions:

AVMutableCompositionTrack *track = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];


AVMutableVideoCompositionLayerInstruction * layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:track];


AVMutableVideoCompositionInstruction * MainInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
MainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, allTime);
MainInstruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];

否则就会卡住。

答案 2 :(得分:2)

我想知道这是否是线程问题 - 如果计时器和导出器最终在不同的线程中。 (AVFoundation指南说导出器不能保证在任何特定线程上运行。)

您是否尝试使用键值观察而不是计时器?

Barebones示例:

// register for the notification
[exportSession addObserver:someProcessor forKeyPath:@"progress" options:NSKeyValueObservingOptionNew context:NULL];

然后在你的处理器中:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
    // add some checks here
    NSNumber *processNumber = [change objectForKey:NSKeyValueChangeNewKey];
    float process = [processNumber floatValue];
    [delegate processor:self didProgress:progress];
}

答案 3 :(得分:0)

我遇到了同样的问题,其他解决方案(例如设置layerInstructions)都没有帮助我。

我重新安排了DispatchQueues的工作方式,看来已经解决了该问题。具体来说,AVAssetExportSessions是在具有相同AVAsset 的多个不同(并行)DispatchQueue上创建的。解决方法是使创建AVAsset的队列也(始终)成为创建AVAssetExportSessions的队列。我发现从全局DispatchQueue调用AVAssetExportSession.exportAsynchronously()很好-并且这是必要的,如果您希望有一个计时器,该计时器最终会在发生故障时超时。