首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >每次迭代后的AVPlayerLooper黑色闪光灯

每次迭代后的AVPlayerLooper黑色闪光灯
EN

Stack Overflow用户
提问于 2017-10-31 01:24:26
回答 1查看 1.4K关注 0票数 7

我使用来自苹果的示例代码在UICollectionViewCell背景上播放视频。

我使用的是AVPlayerLooper,因为它是同一个视频的迭代。

我在这里的问题是,当视频到达尾端时,它会显示一个轻微的黑色的屏幕闪光灯,也许它正在寻找视频到0的时候,我不确定。

下面是代码:

苹果协议

代码语言:javascript
复制
protocol Looper {

    init(videoURL: URL, loopCount: Int)

    func start(in layer: CALayer)

    func stop()
}

苹果提供的播放器活套类

代码语言:javascript
复制
// 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)
        }
    }
}

我的集合视图单元格活套初始化

代码语言:javascript
复制
var looper: Looper? {
    didSet {
        configLooper()
    }
}
func configLooper() {
    looper?.start(in: layer)

}

用于单元格初始化的集合视图委托

代码语言:javascript
复制
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,因此视频播放次数无限。

我尝试使用较小的视频大小文件,但在每次迭代结束时仍然显示一个黑色帧。

有没有人知道是什么原因造成的,还是有更好的方法?苹果的源代码可以找到这里

EN

回答 1

Stack Overflow用户

发布于 2017-10-31 01:29:32

另一种方法是为视频结尾添加一个观察者,如下所示:

代码语言:javascript
复制
NotificationCenter.default.addObserver(forName: AVPlayerItemDidPlayToEndTime, object: self.player.currentItem, queue: nil, using: { (_) in
   DispatchQueue.main.async {
      self.player.seek(to: kCMTimeZero)
      self.player.play()
   }
})
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47026583

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档