AVAssetExportSession перестает развиваться - программирование
Подтвердить что ты не робот

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

(Примечание: он не останавливается с одинаковым процентом каждый раз, его полностью случайным)

Как вы можете видеть из временных меток, для выполнения первых 88% потребовалось 5 секунд, а затем я пропустил еще 13 минут (полная обработка видео обычно занимает не более 10 секунд) без изменений в прогресс.

В настоящее время мой единственный вариант - проверить, не проделан ли прогресс за последние X секунд, и просто сообщите об этом пользователю и попробуйте еще раз.

У кого-нибудь есть идеи?

4b9b3361

Ответ 1

В моем случае эта проблема возникла из mp4s, которые были закодированы с необычными конфигурациями. Но в конечном итоге ошибка в коде Apple вызывает проблему.

В частности, я работал с mp4s на Facebook. Похоже, что когда 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.

К сожалению, API Apple неправильно сообщают об этом времени запуска для трека. После получения AVAssetTrack из AVAsset и проверки его timeRange, он сообщил о времени начала как 0. Возможно, я неправильно понимаю характер отслеживания дорожек и активов, но это похоже на ошибку.

Конечно, при построении видеороликов для сеанса экспорта я использовал неправильный интервал времени трека. Я подозреваю, что когда сессия экспорта ищет данные в треке в момент времени 0 и не находит ничего путающего, а затем, как мы видели, просто зависает и даже не сообщает об ошибке - еще одна ошибка. Даже создание композиции с помощью videoCompositionWithPropertiesOfAsset: не помогает.

Я еще не пытался использовать AVAssetReader и AVAssetWriter вместо AVAssetExportSession, поэтому я не знаю, встретите ли вы там ту же ситуацию. Пока у меня не будет больше времени, чтобы изучить, что я придумал этот хак в качестве решения:

Reset временной интервал, используемый для вставки дорожки и видеоинструкции для начала в 1 или 2 кадрах:

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

Очевидно, что этот взлом применяется только к конкретной ситуации, когда время начала видео трека не равно 0. Я бы предположил, что в целом эта ошибка возникает из-за необычных кодировок мультимедиа, которые не ожидаются AVAssetExportSession и связанные с ним классы.

Ответ 2

Я нашел это!

Вам нужно установить правильные команды 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];

В противном случае он просто застрял.

Ответ 3

Мне интересно, если это проблема с потоками - если таймер и экспортер попадают в разные потоки. (Руководство 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];
}

Ответ 4

У меня была та же проблема, и ни одно из других решений (например, установка layerInstructions) не помогло мне.

Я изменил порядок работы DispatchQueues, и проблема, похоже, устранилась. В частности, AVAssetExportSessions создавались для нескольких разных (одновременных) DispatchQueues с одним и тем же AVAsset. Исправление состояло в том, чтобы очередь, которая создала AVAsset, также (всегда) была очередью, которая создает AVAssetExportSessions. Я обнаружил, что вызов AVAssetExportSession.exportAsynchronously() из глобального DispatchQueue - это нормально - и необходимо, если вы хотите иметь таймер, который в конечном итоге истекает в случае сбоя.