如何在不流所有视频的情况下准备好视频?

时间:2019-08-30 04:46:57

标签: swift avplayer avplayeritem

我正在研究类似于TikTok的“视频逻辑”,我正在寻找的是在观看当前视频的同时准备下一部视频。

我能够通过创建播放器的新临时对象并等待用户向下滑动并播放它来做到这一点。

主要问题是下一个视频准备就绪,但一直缓冲到最后,这使得网络下载了大量数据

视频播放器类:

 @objc protocol VideoPlayerDelegate {
    @objc optional func didCompleteVideo()
    @objc optional func didSeenVideo()
    @objc optional func shakeVideoView()
}

class VideoPlayerLayer: AbstractNibView {

    @IBOutlet weak var loader: UIActivityIndicatorView!
    @IBOutlet weak var slideBar: UISlider!

    fileprivate var playerLayer: AVPlayerLayer?
    fileprivate var player: AVPlayer?
    fileprivate var asset: AVAsset?
    fileprivate var playerItem: AVPlayerItem?
    fileprivate var url: URL?
    fileprivate var playButton: TransitionButton?
    fileprivate var delegate: VideoPlayerDelegate?

    public var isAutoPlay: Bool = false

    func configure(url: String, isAutoPlay: Bool = false, customButton: TransitionButton, delegate: VideoPlayerDelegate) {
        if let videoURL = URL(string: url) {

            self.isAutoPlay = isAutoPlay
            self.url = videoURL
            self.playButton = customButton
            self.delegate = delegate

            asset = AVAsset(url: videoURL)
            playerItem = AVPlayerItem(asset: asset!)
            player = AVPlayer(playerItem: playerItem)
            playerLayer = AVPlayerLayer(player: player)

            playerLayer?.frame = bounds
            playerLayer?.videoGravity = AVLayerVideoGravityResize

            loader.hidesWhenStopped = true

            if let playerLayer = self.playerLayer {
                layer.addSublayer(playerLayer)
            }

            bringSubview(toFront: loader)

            addObservers()

            setupTapGesture()

            // To make sure there is no other sound is playing
            do {
                if #available(iOS 10.0, *) {
                    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, mode: AVAudioSessionModeDefault, options: .mixWithOthers)
                } else {
                    // Fallback on earlier versions
                }
            } catch {
                print(error.localizedDescription)
            }

            // Notify when the video is played to the end
            NotificationCenter.default.addObserver(self, selector: #selector(reachTheEndOfTheVideo(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem)


        }else {
            print("Faild to load")
        }
    }

    func addObservers(){
        // This observer for seek bar
        player?.addPeriodicTimeObserver(forInterval: CMTime(seconds: 1, preferredTimescale: 2), queue: DispatchQueue.main) {[weak self] (progressTime) in
            if let duration = self?.player?.currentItem?.duration {

                let durationSeconds = CMTimeGetSeconds(duration)
                let seconds = CMTimeGetSeconds(progressTime)
                let progress = Float(seconds/durationSeconds)

                DispatchQueue.main.async {
                    self?.slideBar.value = progress
                    if progress >= 1.0 {
                        self?.slideBar.value = 0.0
                    }

                    if seconds == 3 {
                        self?.delegate?.didSeenVideo!()
                    }
                }
            }
        }

        playerItem?.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil)
        player?.addObserver(self, forKeyPath: "timeControlStatus", options: [.old, .new], context: nil)
    }

    func setupTapGesture(){
        let tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTapGestureRecognizer(_:)))
        tapGestureRecognizer.numberOfTapsRequired = 1
        self.addGestureRecognizer(tapGestureRecognizer)
    }

    func reachTheEndOfTheVideo(_ notification: Notification) {
        stop()
        delegate?.didCompleteVideo!()
    }

    @objc
    func handleTapGestureRecognizer(_ gestureRecognizer: UITapGestureRecognizer) {
        playButtonPressed()

    }

    override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        if keyPath == "timeControlStatus", let change = change, let newValue = change[NSKeyValueChangeKey.newKey] as? Int, let oldValue = change[NSKeyValueChangeKey.oldKey] as? Int {

            if #available(iOS 10.0, *) {
                let oldStatus = AVPlayer.TimeControlStatus(rawValue: oldValue)
                let newStatus = AVPlayer.TimeControlStatus(rawValue: newValue)

                if newStatus != oldStatus {
                    DispatchQueue.main.async {[weak self] in

                        if self?.isAutoPlay ?? false {
                            if newStatus == .playing {

                                self?.playButton?.stopAnimation(animationStyle: .normal, revertAfterDelay: 0 , completion: {
                                    self?.playButton?.setImage(UIImage(named: "pause"), for: .normal)
                                })
                            }else if newStatus == .paused {
                                //self?.playButton?.setImage(UIImage(named: "ic_play"), for: .normal)
                                self?.playButton?.stopAnimation(animationStyle: .normal, revertAfterDelay: 0 , completion: {
                                    self?.playButton?.setImage(UIImage(named: "ic_play"), for: .normal)
                                })
                            }else {
                                self?.playButton?.setImage(UIImage(named: "pause"), for: .normal)
                                self?.playButton?.startAnimation()
                            }
                        }
                    }
                }

            } else {
                // Fallback on earlier versions
            }

        }else if keyPath == "status" {
            let status: AVPlayerItemStatus
            if let statusNumber = change?[.newKey] as? NSNumber {
                status = AVPlayerItemStatus(rawValue: statusNumber.intValue)!
            } else {
                status = .unknown
            }

            // Switch over status value
            switch status {
            case .readyToPlay:
                if self.isAutoPlay {
                    play()
                }else{
                    pause()

                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                        if let player = self.player {
                            player.cancelPendingPrerolls()
                        }
                    }
                }
            case .failed:
                return
            case .unknown:
                return
            }
        }
    }

}

// MARK:- Player Control Functions
extension VideoPlayerLayer {
    func play() {
        player?.play()
    }

    func pause() {
        player?.pause()
    }

    func stop() {
        player?.pause()
        player?.seek(to: kCMTimeZero)
    }

    func isPlaying() -> Bool {
        if let player = self.player {
            if #available(iOS 10.0, *) {
                if player.timeControlStatus == AVPlayer.TimeControlStatus.playing {
                    return true
                }
            } else {
                // Fallback on earlier versions
            }
        }
        return false
    }

    func isVideoAvailable() -> Bool{
        if let _ = self.url {
            return true
        }
        return false
    }

    func isVideoReady() -> Bool {
        if let playerItem = self.playerItem {
            if playerItem.status == AVPlayerItemStatus.readyToPlay {
                return true
            }
        }
        return false
    }

    func cancelBuffring() {
        if let playerItem = self.playerItem, let asset = self.asset, let player = self.player {
            asset.cancelLoading()
            playerItem.cancelPendingSeeks()
            playerItem.removeObserver(self, forKeyPath: "status", context: nil)
            player.removeObserver(self, forKeyPath: "timeControlStatus", context: nil)
            player.cancelPendingPrerolls()
            player.replaceCurrentItem(with: nil)

            stop()
        }
    }

    func reloadVideoPrerolls(){
        if let player = player {
            player.preroll(atRate: 0, completionHandler: nil)
            play()
        }
    }

    func playButtonPressed() {
        if isPlaying() {
            pause()
        }else {
            play()
        }
    }
} ```

0 个答案:

没有答案