30

Hello I have set my UISliderminimum value to 0.00. Then I set it's max value in this way.

self.viewPlayer.layer.addSublayer(playerLayer)
    let duration : CMTime = avPlayer.avPlayer.currentItem!.asset.duration
    let seconds : Float64 = CMTimeGetSeconds(duration)

    sliderBar.maximumValue=Float(seconds)
    sliderBar!.isContinuous = false
    sliderBar!.tintColor = UIColor.green

But I am getting this exception

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Attempting to set a slider's minimumValue (0.000000) to be larger than the maximumValue (nan)'
enter code here

I know after prepareForPlay() to actual playing it takes some time to really play the video. So how can I detect when the player really started to play the video? Please help me. Thanks

user1960169
  • 3,533
  • 12
  • 39
  • 61

8 Answers8

41

Since iOS 10 you can observe timeControlStatus property of AVPlayer. It can be .playing.

Check the code:

private func setupAVPlayer() {
    avPlayer.addObserver(self, forKeyPath: "status", options: [.old, .new], context: nil)
    if #available(iOS 10.0, *) {
        avPlayer.addObserver(self, forKeyPath: "timeControlStatus", options: [.old, .new], context: nil)
    } else {
        avPlayer.addObserver(self, forKeyPath: "rate", options: [.old, .new], context: nil)
    }
}

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if object as AnyObject? === avPlayer {
        if keyPath == "status" {
            if avPlayer.status == .readyToPlay {
                avPlayer.play()
            }
        } else if keyPath == "timeControlStatus" {
            if #available(iOS 10.0, *) {
                if avPlayer.timeControlStatus == .playing {
                    videoCell?.muteButton.isHidden = false
                } else {
                    videoCell?.muteButton.isHidden = true
                }
            }
        } else if keyPath == "rate" {
            if avPlayer.rate > 0 {
                videoCell?.muteButton.isHidden = false
            } else {
                videoCell?.muteButton.isHidden = true
            }
        }
    }
}
Renatus
  • 1,123
  • 13
  • 20
19

Here is what I did to actually know when video started (not when it's only ready to start).

Swift 4

player = AVPlayer(url: URL(fileURLWithPath: path))
player.addObserver(self, forKeyPath: "rate", options: NSKeyValueObservingOptions.new, context: nil)

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "rate" {
        if player.rate > 0 {
            print("video started")
        }
    }
}
Skoua
  • 3,373
  • 3
  • 38
  • 51
12

You can add an observer on the object of your AVPlayer like this

player.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.new, context: nil)

and you can check the status change with your AVPlayer like this

 func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutableRawPointer) {
    if keyPath == "status" {
        print(player.status)
    }
}
shim
  • 9,289
  • 12
  • 69
  • 108
Rajat
  • 10,977
  • 3
  • 38
  • 55
3

From Apple's docs: You can observe an AVPlayerLayer object’s readyForDisplay property to be notified when the layer has user-visible content. In particular, you might insert the player layer into the layer tree only when there is something for the user to look at and then perform a transition from

Chandler De Angelis
  • 2,646
  • 6
  • 32
  • 45
3

You could create a Combine publisher and observe the rate property:

let isPlayingPublisher: AnyPublisher<Bool, Never> = NotificationCenter.default
        .publisher(for: AVPlayer.rateDidChangeNotification, object: player)
        .compactMap({ $0.object as? AVPlayer })
        .map(\.rate)
        .map({ $0 != 0 })
        .eraseToAnyPublisher()

In SwiftUI you could then receive the value:

.onReceive(isPlayingPublisher) { isPlaying in
    
}
Joony
  • 4,498
  • 2
  • 30
  • 39
-2

Declare AVPlayer Global

var streamPlayer = AVPlayer()

func playStreamAudio( for audioUrl:String)
    {
        guard streamPlayer.timeControlStatus != .playing else {
            streamPlayer.pause()
            return
        }
        let url = audioUrl //"http://192.168.71.11:7891/rec.wav"
        let playerItem = AVPlayerItem(url: URL(string:url)!)
        streamPlayer = AVPlayer(playerItem:playerItem)
        streamPlayer.rate = 1.0;
        streamPlayer.volume = 1.0
        streamPlayer.play()
    }
-3

SUPER EASY SOLUTION SWIFT 4-5:

Just check the timeControlStatus!: (also works with PlayerQueue)

    if avPlayer?.timeControlStatus.rawValue == 2 {
       
    //video is playing (playing)

    } else if avPlayer?.timeControlStatus.rawValue == 0 { 
    
    //video is not playing (paused)
    
    }

the raw value will give you its current state :)

-6

Another, simpler, approach is something like:

if videoPlayer.rate != 0 && videoPlayer.error == nil {
            print("video player is playing.................")
        } else {
            print("video player is NOT playing.")
        } 

Where videoPlayer is of type AVPlayer, obviously.

AdjunctProfessorFalcon
  • 1,790
  • 6
  • 26
  • 62