0

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.

  • What do you actually need `automaticallyWaitsToMinimizeStalling = true` for? – Mr.SwiftOak Mar 10 '22 at 09:54
  • Yes I need the automaticallyWaitsToMinimizeStalling = true, I know that default value of this thing is true, but minimise the waiting or loading...! Its buffer the things but too too much wait for it. – Muhammad Danish Qureshi Mar 10 '22 at 13:46

1 Answers1

1

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 (e.g. 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 (e.g. hide loading indicator, start player)
             } else {
                 //  player is not ready to play yet
             }
         }
     })
    
Mr.SwiftOak
  • 1,469
  • 3
  • 8
  • 19
  • Thanks for you answer...! I will let you know after try it..! – Muhammad Danish Qureshi Mar 10 '22 at 13:37
  • I also noticed that you are using `AVPlayerViewController` and modifying its frame , background etc.. I do not thing it is good idea, but I would rather suggest using custom View with own layer and setting it as `AVPlayerLayer` in your `AVPlayer` . You can see sample in this video: [ video link](https://www.letsbuildthatapp.com/course_video?id=252) – Mr.SwiftOak Mar 10 '22 at 14:54