2017-01-25 12 views
2

Вот ссылка на GIF проблем:Искомого AVComposition с CMTimeMapping вызывает AVPlayerLayer заморозить

https://gifyu.com/images/ScreenRecording2017-01-25at02.20PM.gif

Я беру PHAsset из рулона камеры, добавив его в изменяемую композицию, добавляя другой видео трек, манипулируя добавленным треком, а затем экспортируя его через AVAssetExportSession. Результатом является QuickTime файл с расширением .mov файла, сохраненного в NSTemporaryDirectory():

guard let exporter = AVAssetExportSession(asset: mergedComposition, presetName: AVAssetExportPresetHighestQuality) else { 
     fatalError() 
} 

exporter.outputURL = temporaryUrl 
exporter.outputFileType = AVFileTypeQuickTimeMovie 
exporter.shouldOptimizeForNetworkUse = true 
exporter.videoComposition = videoContainer 

// Export the new video 
delegate?.mergeDidStartExport(session: exporter) 
exporter.exportAsynchronously() { [weak self] in 
    DispatchQueue.main.async { 
     self?.exportDidFinish(session: exporter) 
    } 
} 

Я тогда взять этот экспортированный файл и загрузить его в объект картографа, который применяет «медленное движение» к клипу на основе некоторых временных отображений данный ему. В результате здесь является AVComposition:

func compose() -> AVComposition { 
    let composition = AVMutableComposition(urlAssetInitializationOptions: [AVURLAssetPreferPreciseDurationAndTimingKey: true]) 

    let emptyTrack = composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid) 
    let audioTrack = composition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: kCMPersistentTrackID_Invalid) 

    let asset = AVAsset(url: url) 
    guard let videoAssetTrack = asset.tracks(withMediaType: AVMediaTypeVideo).first else { return composition } 

    var segments: [AVCompositionTrackSegment] = [] 
    for map in timeMappings { 

     let segment = AVCompositionTrackSegment(url: url, trackID: kCMPersistentTrackID_Invalid, sourceTimeRange: map.source, targetTimeRange: map.target) 
     segments.append(segment) 
    } 

    emptyTrack.preferredTransform = videoAssetTrack.preferredTransform 
    emptyTrack.segments = segments 

    if let _ = asset.tracks(withMediaType: AVMediaTypeVideo).first { 
     audioTrack.segments = segments 
    } 

    return composition.copy() as! AVComposition 
} 

Затем я загрузить этот файл, а также исходный файл, который также отображается в slowmo в AVPlayerItem с сняться в AVPlayer с которой подключен к AVPlayerLayer с в моей Приложение:

let firstItem = AVPlayerItem(asset: originalAsset) 
let player1 = AVPlayer(playerItem: firstItem) 
firstItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed 
player1.actionAtItemEnd = .none 
firstPlayer.player = player1 

// set up player 2 
let secondItem = AVPlayerItem(asset: renderedVideo) 
secondItem.seekingWaitsForVideoCompositionRendering = true //tried false as well 
secondItem.audioTimePitchAlgorithm = AVAudioTimePitchAlgorithmVarispeed 
secondItem.videoComposition = nil // tried AVComposition(propertiesOf: renderedVideo) as well 

let player2 = AVPlayer(playerItem: secondItem) 
player2.actionAtItemEnd = .none 
secondPlayer.player = player2 

У меня есть время начала и окончания, чтобы циклически перебирать эти видео снова и снова. Я не использую PlayerItemDidReachEnd, потому что я не заинтересован в конце, меня интересует введенное пользователем время. Я даже использовать dispatchGroup для ОБЕСПЕЧИТЬ что оба игрока закончили поиск, прежде чем пытаться воспроизвести видео:

func playAllPlayersFromStart() { 

    let dispatchGroup = DispatchGroup() 

    dispatchGroup.enter() 

    firstPlayer.player?.currentItem?.seek(to: startTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero, completionHandler: { _ in 
     dispatchGroup.leave() 
    }) 

    DispatchQueue.global().async { [weak self] in 
     guard let startTime = self?.startTime else { return } 
     dispatchGroup.wait() 

     dispatchGroup.enter() 

     self?.secondPlayer.player?.currentItem?.seek(to: startTime, toleranceBefore: kCMTimeZero, toleranceAfter: kCMTimeZero, completionHandler: { _ in 
      dispatchGroup.leave() 
     }) 


     dispatchGroup.wait() 

     DispatchQueue.main.async { [weak self] in 
      self?.firstPlayer.player?.play() 
      self?.secondPlayer.player?.play() 
     } 
    } 

} 

Странная часть здесь является то, что первоначальный актив, который также отображается с помощью моих Compose() петли функции прекрасно. Однако рендеринг видео, который также запускался через функцию compose(), иногда зависает при поиске во время одного из сегментов CMTimeMapping. Единственная разница между файлом, который зависает, и файлом, который не замерзает, является то, что он был экспортирован в NSTemporaryDirectory через AVAssetExportSession, чтобы объединить два видеодорожки в один. У них одинаковая продолжительность. Я также уверен, что это только уровень видео, который замерзает, а не звук, потому что, если я добавлю BoundaryTimeObservers игроку, который замораживает его, он все равно бьет их и петли. Также звуковые петли должным образом.

Для меня самая странная часть заключается в том, что видео «возобновляется», если оно пробивается мимо места, где оно приостановлено, чтобы начать поиск после «замораживания». Я застрял на этом в течение нескольких дней, и мне действительно понравилось бы руководство.

Другие примечательные примечания: - Несмотря на то, что CMTimeMapping оригинала и экспортируемого актива - это то же самое время, вы заметите, что рампа замедленного движения рендеринга более «изменчивая», чем оригинал. - Аудио продолжает, когда видео зависает. - видео почти всегда зависает во время секций медленного движения (вызвано CMTimeMapping объектами на основе сегментов - визуализированное видео, похоже, должно играть «догнать» в начале. Хотя я и называю игру после того, как оба закончили поиск, это мне кажется, что правая сторона играет быстрее в начале, как догонять. Странная часть состоит в том, что сегменты являются то же самое, просто ссылаясь на два отдельных исходных файла: один находится в библиотеке активов, другой в NSTemporaryDirectory - кажется для меня, что AVPlayer и AVPlayerItemStatus - это «readyToPlay» до того, как я звоню в игру. - Кажется, «размораживает», если игрок продолжает PAST, что он заблокирован. - Я попытался добавить наблюдателей для «AVPlayerItemPlaybackDidStall», но он никогда не был

Приветствия!

ответ

2

Проблема была в AVAssetExportSession. К моему удивлению, изменение exporter.canPerformMultiplePassesOverSourceMediaData = true устранило проблему. Хотя документация довольно разрежена, и даже претензии, устанавливающие это свойство в true, могут не иметь эффекта », похоже, это исправить проблему. Очень, очень, очень странно! Я считаю это ошибкой и будет подавать радар. Вот документы об этом свойстве: canPerformMultiplePassesOverSourceMediaData

0

Возможно, что в вашем методе playAllPlayersFromStart() переменная startTime может быть изменена между двумя отправленными задачами (это было бы особенно вероятно, если это значение будет обновляться на основе очистки).

Если вы сделаете локальную копию startTime в начале функции, а затем используйте ее в обоих блоках, вам может быть повезло больше.

+0

Действительно оцените ответ. К сожалению, никаких кубиков, так как второй игрок все еще замерзает. Я могу сказать, что сам игрок все еще играет, потому что наблюдатель граничного времени попадает в соответствующее место, которое сигнализирует игроку 1 перезапустить. Также звук перезапускается с самого начала, пока только видео блокируется до тех пор, пока не пройдет «замороженный» кадр. –

 Смежные вопросы

  • Нет связанных вопросов^_^