Вот ссылка на 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», но он никогда не был
Приветствия!
Действительно оцените ответ. К сожалению, никаких кубиков, так как второй игрок все еще замерзает. Я могу сказать, что сам игрок все еще играет, потому что наблюдатель граничного времени попадает в соответствующее место, которое сигнализирует игроку 1 перезапустить. Также звук перезапускается с самого начала, пока только видео блокируется до тех пор, пока не пройдет «замороженный» кадр. –