chrisl08
chrisl08

Reputation: 1658

AVPlayer: delay when status is ReadyToPlay and first frame appearing

We load an MP4 video from a URL into an AVPlayer. The AVPlayer is behind a skeleton image which we hide when the AVPlayer gets to status "Ready To Play".

We expect to see the first frame of the video as soon as we hide the skeleton image. However, that first frame of the video appears after a slight delay. What is the status that indicates that the video in the AVPlayer is loaded and ready?

func prepareVideo(videoUrl: URL?, owner: UIView, autoStart: Bool = false) {

    self.player = AVPlayer(url: videoUrl!)
    let playerController = AVPlayerViewController()
    playerController.player = player
    playerController.view.layer.shouldRasterize = true
    playerController.view.frame = owner.frame
    playerController.view.isOpaque = true
    playerController.view.backgroundColor = UIColor.clear
    playerController.view.layer.borderColor = UIColor.clear.cgColor
    playerController.view.layer.borderWidth = 0
    playerController.showsPlaybackControls = false
    playerController.updatesNowPlayingInfoCenter = false

    owner.addSubview(playerController.view)
    owner.sendSubview(toBack: playerController.view)
    timerStart() // this starts a timer that checks the AVPlayer status

    if autoStart {
        playVideo()
    }

}

@objc func timerStatusCheck() {
// function called by a Timer and checks the status of AVPlayer 
    if player!.status == AVPlayerStatus.readyToPlay  {
        print("ready to play")
        timerStop()
        if (readyToPlayHandler != nil) {
            self.readyToPlayHandler!() // here we hide the skeleton image that shows while video is loading
        }
    } else if player!.status == AVPlayerStatus.failed {
        timerStop()
        MessageBox.showError("Video Failed to start")
    }

}

Upvotes: 2

Views: 2766

Answers (1)

ozool
ozool

Reputation: 114

When the AVPlayer reports rate = 1, it's playing the video. However, that doesn't mean the video is visible in the AVPlayerViewController. For that, you need the AVPlayerViewController's property "isReadyForDisplay" to be true.

https://developer.apple.com/documentation/avkit/avplayerviewcontroller/1615830-isreadyfordisplay

Note both AVPlayerViewController.isReadyForDisplay and AVPlayer.status are both KVO observable, which will be more responsive than using a Timer.

Also note if you use an AVPlayerLayer to display the video (instead of AVPlayerViewController), you need to observe the AVPlayerLayer's "isReadyForDisplay" for the same reason.

@objc func timerStatusCheck() {
// function called by a Timer and checks the status of AVPlayer 
    if player!.status == AVPlayerStatus.readyToPlay  {
        print("ready to play")

        // check that controller's view is showing video frames from player

        if playerController.isReadyForDisplay == false {
            print("view not yet showing video")
            return 
        }

        timerStop()
        if (readyToPlayHandler != nil) {
            self.readyToPlayHandler!() // here we hide the skeleton image that shows while video is loading
        }
    } else if player!.status == AVPlayerStatus.failed {
        timerStop()
        MessageBox.showError("Video Failed to start")
    }

}

Upvotes: 1

Related Questions