每次迭代后AVPlayerLooper黑色闪烁 [英] AVPlayerLooper black flash after each iteration

查看:75
本文介绍了每次迭代后AVPlayerLooper黑色闪烁的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Apple的示例代码在UICollectionViewCell背景上播放视频.

I am using a sample code from Apple to play a video on a UICollectionViewCell background.

我正在使用AVPlayerLooper,因为它是同一视频的迭代.

I'm using AVPlayerLooper, since it is a iteration of the same video.

我的问题是,当视频播放完时,它会显示出轻微的黑点闪烁,也许我正在寻找0倍的视频,我不确定.

My problem here is that when the video reaches the end, it shows a slight black-bip screen flash, maybe it is seeking the video to 0 time, I'm not sure.

代码如下:

Apple协议

protocol Looper {

    init(videoURL: URL, loopCount: Int)

    func start(in layer: CALayer)

    func stop()
}

Apple提供的"Player Looper类"

// 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,因此视频播放了无数次.

The loopCount set to -1 so the video playback infinite number of times.

我尝试使用较小的视频文件,但在每次迭代结束时仍显示黑框.

I've tried using smaller video size files but it still shows a black frame at the end of each iteration.

有人知道这可能是什么原因吗,还是有更好的方法?可以在此处

Does anyone have any clue what might be causing this, or there's any better approach ? The Apple source code can be found here

推荐答案

您可以采用的另一种方法是为视频完成添加观看者,如下所示:

Another way you could go about it is to add an observer for the video finishing like so:

NotificationCenter.default.addObserver(forName: AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: nil, using: { (_) in
   DispatchQueue.main.async {
      self.player.seek(to: kCMTimeZero)
      self.player.play()
   }
})

这篇关于每次迭代后AVPlayerLooper黑色闪烁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆