简体   繁体   中英

AVPlayerViewController took too much time to play video/audio with automaticallyWaitsToMinimizeStalling

I am using AVPlayer, in Player I want to play video from the Server. I am doing like the code below. Problem I am facing is I set the

automaticallyWaitsToMinimizeStalling = true

According to Documentation

A Boolean value that indicates whether the player should automatically delay playback in order to minimize stalling.

but it took too much time to load the video/audio, for 8minutes video it took almost 2 to 3 minutes wait to play. If during this time (wait time of 2 to 3 minutes), user pause the video and play again, then video will play without any delay. This unnecessary wait should not be happened.

Can anyone guide me how to decrease wait of stalling? I can not use this answer

player.automaticallyWaitsToMinimizeStalling = false

because due to set this value false, my player stops again and again, and user have to play this manually, so this thing is very bad.

// MARK: - Outlets
    @IBOutlet weak var audioView: UIView!

// MARK: - Variables
    var player: AVPlayer = AVPlayer()
    let playerController = AVPlayerViewController()
    var obs = Set<NSKeyValueObservation>()


// MARK: - Helper Method
private func settingForAudioPlayer() {
    guard let lesson = viewModal.lesson else {
        print("lesson not found")
        return
    }
    var path = "\(Server.audioVideoBasurl + (lesson.videoPath ?? ""))"
    path = path.replacingOccurrences(of: " ", with: "%20")
    print("path:\(path)")
    guard let url = URL(string: path) else {
        print("Path not converted to url")
        return
    }
    self.player = AVPlayer(url: url)
    self.player.automaticallyWaitsToMinimizeStalling = true
self.player.playImmediately(atRate: 1.0)

    self.playerController.player = self.player
    DispatchQueue.main.async {
        
        self.playerController.view.clipsToBounds = true
        self.playerController.view.removeFromSuperview()
        self.playerController.delegate = self

        self.showSpinner(onView: self.audioView, identifier: "audioView", title: "")
        self.audioView.addSubview(self.playerController.view)
        self.audioView.layoutIfNeeded() // Previously we were playing only audio but after some time we decided to add videos also so thats why that view name is audioView Don’t get confuse with this view name
        self.playerController.view.frame.size.width = self.audioView.frame.width
        self.playerController.view.frame.size.height = self.audioView.frame.height
        self.playerController.view.backgroundColor = .clear
        self.playerController.videoGravity = AVLayerVideoGravity.resizeAspectFill

        var ob : NSKeyValueObservation!
        ob = self.playerController.observe(\.isReadyForDisplay, options: [.initial, .new]) { vc, ch in
            guard let ok = ch.newValue, ok else {return}
            self.obs.remove(ob)
            DispatchQueue.main.async {
                print("finishing")
                self.removeSpinner(identifier: "audioView") // This is my Custom Method
                vc.view.isHidden = false // The Idea of KVO observer add got from the Internet
            }
        }
        self.obs.insert(ob)
        let iv = self.audioBackgroundImageView ?? UIImageView()

        let v = self.playerController.contentOverlayView!
        iv.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            iv.bottomAnchor.constraint(equalTo:v.bottomAnchor),
            iv.topAnchor.constraint(equalTo:v.topAnchor),
            iv.leadingAnchor.constraint(equalTo:v.leadingAnchor),
            iv.trailingAnchor.constraint(equalTo:v.trailingAnchor),
        ])

        NSLayoutConstraint.activate([
            v.bottomAnchor.constraint(equalTo:self.playerController.view.bottomAnchor),
            v.topAnchor.constraint(equalTo:self.playerController.view.topAnchor),
            v.leadingAnchor.constraint(equalTo:self.playerController.view.leadingAnchor),
            v.trailingAnchor.constraint(equalTo:self.playerController.view.trailingAnchor),
        ])

        self.view.layoutIfNeeded()
    }

}

in Above peace of code I have video url (Path), I passed it to the AVPlayer, Player pass to AVPlayerController, add observer to the playerController to check that AVPlayerController is ready for Display, then remove observer. After that I am only settings the constraint.

Kindly guide me how to decrease the waits on swift, on Android side video/Audio plays with in seconds. It can be duplicate of this but my scenario is different and the solution here did not work for me. Kindly let me know in-case you need more information to help me.

If you don't want to use automaticallyWaitsToMinimizeStalling = true you can also observe player buffer and decide whether you start playing video. Here are some steps how to do that:

  1. Create observer variable in the class where you intend to handle player:

     var playbackBufferEmptyObserver: NSKeyValueObservation? var playbackBufferFullObserver: NSKeyValueObservation? var playbackLikelyToKeepUpObserver: NSKeyValueObservation?
  2. Instantiate AVPlayer with AVPlayerItem instead of URL likewise:

     let playerItem = AVPlayerItem(url: url) let player = AVPlayer(playerItem: playerItem)
  3. Create observers and assign them to variables:

     playbackBufferEmptyObserver = self.playerItem.observe(\.isPlaybackBufferEmpty, options: [.new, .initial ], changeHandler: { [weak self] (player, bufferEmpty) in if let self = self { DispatchQueue.main.async { if bufferEmpty.newValue == true { // handle showing loading, player not playing } } } }) playbackBufferFullObserver = self.playerItem.observe(\.isPlaybackBufferFull, options: [.new, .initial], changeHandler: {[weak self] (player, bufferFull) in if let self = self { DispatchQueue.main.async { if bufferFull.newValue == true { //handle when player buffer is full (eg hide loading) start player } } } }) playbackLikelyToKeepUpObserver = self.playerItem.observe(\.isPlaybackLikelyToKeepUp, options: [.new, .initial], changeHandler: { [weak self] (player, _) in if let self = self { if ((self.playerItem?.status?? AVPlayerItem.Status.unknown). ==.readyToPlay) { // handle that player is ready to play (eg, hide loading indicator, start player) } else { // player is not ready to play yet } } })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM