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.

Другие вопросы по тегам