AVPlayerLoop не бесшовно зацикливается - Swift 4
Моя проблема: я пытаюсь сделать бесшовное зацикливание (я намерен сделать мой AVPlayer или AVPlayerQueue зацикливанием без задержки между воспроизведением). Так, например, если я делаю видео и иду к воспроизведению, оно должно бесконечно зацикливаться без каких-либо скачков или задержек в цикле.
Я написал код ниже (прямо из примера кода):
var playerQQ: AVQueuePlayer!
var playerLayur: AVPlayerLayer!
var playerEyetem: AVPlayerItem!
var playerLooper: AVPlayerLooper!
func playRecordedVideo(videoURL: URL) {
playerQQ = AVQueuePlayer()
playerLayur = AVPlayerLayer(player: playerQQ)
playerLayur.frame = (camBaseLayer?.bounds)!
camBaseLayer?.layer.insertSublayer(playerLayur, above: previewLayer)
playerEyetem = AVPlayerItem(url: videoURL)
playerLooper = AVPlayerLooper(player: playerQQ, templateItem: playerEyetem)
playerQQ.play()
}
Код выше не зацикливается; между концом текущего плеера и следующего появляется всплески. Я много пытался найти проблему и искал ее в Интернете, но не нашел решения. Кроме того, я пробовал NSNotifications и другие методы, включая установку Player.seek(в: ноль), когда проигрыватель заканчивает воспроизведение. Но ничего не сработало.
Любая помощь будет оценена:)
3 ответа
При зацикливании ресурсов следует иметь в виду, что аудио- и видеодорожки могут иметь разное смещение и разную длительность, что приводит к "бликам" при зацикливании. Такие небольшие различия довольно часто встречаются в учтенных активах.
Перебор треков и печать временных диапазонов может помочь обнаружить такие ситуации: for track in asset.tracks { print( track.mediaType); CMTimeRangeShow( track.timeRange); }
Чтобы обрезать аудио- и видеодорожки до одинакового времени начала и одинаковой длительности, получите общий временной диапазон дорожек, а затем вставьте этот временной диапазон из исходного ресурса в новый AVMutableComposition
, Как правило, вы также хотите сохранить свойства, такие как ориентация видео дорожки:
let asset: AVAsset = (your asset initialization here)
let videoTrack: AVAssetTrack = asset.tracks(withMediaType: .video).first!
let audioTrack: AVAssetTrack = asset.tracks(withMediaType: .audio).first!
// calculate common time range of audio and video track
let timeRange: CMTimeRange = CMTimeRangeGetIntersection( (videoTrack.timeRange), (audioTrack.timeRange))
let composition: AVMutableComposition = AVMutableComposition()
try composition.insertTimeRange(timeRange, of: asset, at: kCMTimeZero)
// preserve orientation
composition.tracks(withMediaType: .video).first!.preferredTransform = videoTrack.preferredTransform
Поскольку AVMutableComposition является подклассом AVAsset, его можно использовать для AVPlayerLooper
циклическое воспроизведение или экспорт с AVAssetExportSession
,
Я поместил более полную реализацию усечения на github: https://github.com/fluthaus/NHBAVAssetTrimming. Он более надежен, обрабатывает несколько дорожек, сохраняет больше свойств и может быть либо легко интегрирован в проекты, либо может быть собран как отдельная утилита обрезки фильмов из командной строки macOS.
Похоже, проблема с файлами.mp4, конвертируйте файл.mp4 в файл.mov. AVPlayer или AVQueuePlayer с файлом.mp4 работают нормально. вот мой код:
NotificationCenter.default.addObserver(forName: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: nil, queue: nil) { [weak self] (noty) in
self?.player?.seek(to: CMTime.zero)
self?.player?.play() }
или
let asset = AVAsset(url: URL(fileURLWithPath: movPath))
let playerItem = AVPlayerItem(asset: asset)
let player = AVQueuePlayer(playerItem: playerItem)
playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
plyerLayer.frame = CGRect(x: 0, y: 88, width: kWidth, height: kWidth * 0.75)
plyerLayer.videoGravity = .resizeAspectFill
plyerLayer.player = player
view.layer.addSublayer(plyerLayer)
player.play()
Ответ от @NoHalfBits прекрасно работает, но я также нашел другое решение. Я в основном получил диапазон времени пересечения видео и звука mediaTypes из ресурса playerItem. После этого я добавил intersectionTimeRange в качестве timeRange в параметр, когда я вызываю:
playerLooper = AVPlayerLooper(playerQueue:_, playerItem:_, timeRange: intersectionTimeRange)
Это будет работать! Чтобы получить timeRanges каждого из них, установите цикл for для элемента playerItem.
Если играешь в конце, попробуй
NotificationCenter.default.addObserver(self,
selector: #selector(playerItemDidReachEnd(notification:)),
name: Notification.Name.AVPlayerItemDidPlayToEndTime,
object: avPlayer?.currentItem)
@objc func playerItemDidReachEnd(notification: Notification) {
if let playerItem: AVPlayerItem = notification.object as? AVPlayerItem {
playerItem.seek(to: kCMTimeZero, completionHandler: nil)
}
}
Если нет, я бы посоветовал управлять своим собственным с помощью dTime (запустить NSTimer 1/30 секунды или что-то в этом роде) и настроить игру на что-то подобное
player.seekToTime(seekTimeInProgress, toleranceBefore: kCMTimeZero,
toleranceAfter: kCMTimeZero, completionHandler: ...
kCMTimeZero чрезвычайно важны, иначе время не будет точным. И, наконец, я обнаружил, что при перезапуске видео есть время загрузки, в зависимости от типа телефона на iOS и продолжительности видео, а также от того, сколько вы играете, поэтому, если вы по-прежнему испытываете это отставание после устранения проблем с синхронизацией, может быть принужден к учету в вашем UX.