AVPlayerLooper черная вспышка после каждой итерации

Я использую пример кода от Apple, чтобы воспроизвести видео на UICollectionViewCell фон.

я использую AVPlayerLooper, так как это итерация того же видео.

Моя проблема здесь в том, что когда видео подходит к концу, на нем появляется небольшая вспышка на экране, возможно, он ищет видео до 0 раз, я не уверен.

Вот код:

Протокол от Apple

protocol Looper {

    init(videoURL: URL, loopCount: Int)

    func start(in layer: CALayer)

    func stop()
}

Player Looper Class предоставлен Apple

// Code from Apple
class PlayerLooper: NSObject, Looper {
    // MARK: Types

    private struct ObserverContexts {
        static var isLooping = 0

        static var isLoopingKey = "isLooping"

        static var loopCount = 0

        static var loopCountKey = "loopCount"

        static var playerItemDurationKey = "duration"
    }

    // MARK: Properties

    private var player: AVQueuePlayer?

    private var playerLayer: AVPlayerLayer?

    private var playerLooper: AVPlayerLooper?

    private var isObserving = false

    private let numberOfTimesToPlay: Int

    private let videoURL: URL

    // MARK: Looper

    required init(videoURL: URL, loopCount: Int) {
        self.videoURL = videoURL
        self.numberOfTimesToPlay = loopCount

        super.init()
    }

    func start(in parentLayer: CALayer) {
        player = AVQueuePlayer()
        player?.isMuted = true
        playerLayer = AVPlayerLayer(player: player)

        guard let playerLayer = playerLayer else { fatalError("Error creating player layer") }
        playerLayer.frame = parentLayer.bounds
        parentLayer.addSublayer(playerLayer)

        let playerItem = AVPlayerItem(url: videoURL)
        playerItem.asset.loadValuesAsynchronously(forKeys: [ObserverContexts.playerItemDurationKey], completionHandler: {()->Void in
            /*
                The asset invokes its completion handler on an arbitrary queue when
                loading is complete. Because we want to access our AVPlayerLooper
                in our ensuing set-up, we must dispatch our handler to the main queue.
            */
            DispatchQueue.main.async(execute: {
                guard let player = self.player else { return }

                var durationError: NSError? = nil
                let durationStatus = playerItem.asset.statusOfValue(forKey: ObserverContexts.playerItemDurationKey, error: &durationError)
                guard durationStatus == .loaded else { fatalError("Failed to load duration property with error: \(String(describing: durationError))") }

                self.playerLooper = AVPlayerLooper(player: player, templateItem: playerItem)
                self.startObserving()
                player.play()
            })
        })
    }

    func stop() {
        player?.pause()
        stopObserving()

        playerLooper?.disableLooping()
        playerLooper = nil

        playerLayer?.removeFromSuperlayer()
        playerLayer = nil

        player = nil
    }

    // MARK: Convenience

    private func startObserving() {
        guard let playerLooper = playerLooper, !isObserving else { return }

        playerLooper.addObserver(self, forKeyPath: ObserverContexts.isLoopingKey, options: .new, context: &ObserverContexts.isLooping)
        playerLooper.addObserver(self, forKeyPath: ObserverContexts.loopCountKey, options: .new, context: &ObserverContexts.loopCount)

        isObserving = true
    }

    private func stopObserving() {
        guard let playerLooper = playerLooper, isObserving else { return }

        playerLooper.removeObserver(self, forKeyPath: ObserverContexts.isLoopingKey, context: &ObserverContexts.isLooping)
        playerLooper.removeObserver(self, forKeyPath: ObserverContexts.loopCountKey, context: &ObserverContexts.loopCount)

        isObserving = false
    }

    // MARK: KVO

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if context == &ObserverContexts.isLooping {
            if let loopingStatus = change?[.newKey] as? Bool, !loopingStatus {
                print("Looping ended due to an error")
            }
        }
        else if context == &ObserverContexts.loopCount {
            guard let playerLooper = playerLooper else { return }

            if numberOfTimesToPlay > 0 && playerLooper.loopCount >= numberOfTimesToPlay - 1 {
                print("Exceeded loop limit of \(numberOfTimesToPlay) and disabling looping");
                stopObserving()
                playerLooper.disableLooping()
            }
        }
        else {
            super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
        }
    }
}

Моя коллекция просмотра инициализации петли ячейки

var looper: Looper? {
    didSet {
        configLooper()
    }
}
func configLooper() {
    looper?.start(in: layer)

}

Моя коллекция просмотра делегата для инициализации ячейки

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! FirstLaunchCollectionViewCell
    let videoURL = Bundle.main.url(forResource: "video3", withExtension: "MOV")
    cell.looper = PlayerLooper(videoURL: videoURL!, loopCount: -1)
    return cell
}

loopCount установлен в -1 поэтому воспроизведение видео бесконечное количество раз.

Я пытался использовать файлы меньшего размера, но в конце каждой итерации он все еще показывает черную рамку.

Кто-нибудь знает, что может быть причиной, или есть лучший подход? Исходный код Apple можно найти здесь

1 ответ

Еще один способ сделать это - добавить наблюдателя для завершения видео следующим образом:

NotificationCenter.default.addObserver(forName: AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: nil, using: { (_) in
   DispatchQueue.main.async {
      self.player.seek(to: kCMTimeZero)
      self.player.play()
   }
})
Другие вопросы по тегам