17

我遇到了 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

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

从时间戳中可以看出,前 88% 需要 5 秒,然后我让它再运行 13 分钟(完整的视频处理通常不会超过 10 秒),进度没有变化。

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

有人有想法么?

4

4 回答 4

4

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

具体来说,我在 Facebook 上使用 mp4。似乎当 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 所以我不知道你是否会遇到同样的情况。直到我有更多时间去探索,我才想出了这个 hack 作为解决方案:

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

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

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

于 2014-02-10T03:29:51.457 回答
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];

否则它只是卡住了。

于 2013-02-17T20:47:05.727 回答
2

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

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

准系统示例:

// 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];
}
于 2013-02-23T05:46:06.227 回答
0

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

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

于 2019-09-19T20:49:25.650 回答