AVPlayer Archives — Now Playing Apps Let's build it together Sat, 21 Mar 2020 15:33:12 +0000 en-US hourly 1 https://wordpress.org/?v=6.6.1 151952093 Handling audio interruptions in your iOS app https://nowplayingapps.com/handling-audio-interruptions-in-your-audio-app/ Mon, 10 Feb 2020 02:46:57 +0000 https://nowplayingapps.com/?p=304 Handling audio interruptions is key to improving user experience of your app. This article describes how to handle such interruptions gracefully

The post Handling audio interruptions in your iOS app appeared first on Now Playing Apps.

]]>
Interruptions are common in any type of software system. It is even more important in the case of mobile apps, starting with network interruptions to other common ones such as receiving calls or alarm notifications (also called audio interruption). Handling these audio interruptions will enhance the user experience gracefully.

Common types of audio interruptions

  1. Receiving incoming or making outgoing calls
  2. Alarm or timer notifications
  3. Other app takes over the iOS audio session (for example user opens a podcast app and starts playing it while your music app was playing)

How does the operating system handles interruption?

  1. Operating System deactivates your apps audio session
  2. Sends a notification through NSNotificationCenter saying an interruption has occurred. This will allow your app to gracefully respond to the interruption, for example, by pausing/stopping your playback and updating the UI states – such as display paused icon
  3. Once the interruption has ended, the operating system sends another notification confirming the interruption has ended. This will allow your app can respond to it (by resuming the playback and updating the UI state – display playing icon)

So basically, in order to handle these interruptions, all you need to do is listen to interruption notifications, and respond to it accordingly. Let’s see how.

Listening to audio interruption notifications

This can be done by adding an observer for AVAudioSession.interruptionNotification event.

private func addInterruptionsObserver() {
    NotificationCenter.default.addObserver(self,
                   selector: #selector(handleInterruption),
                   name: AVAudioSession.interruptionNotification,
                   object: nil)
}

Make sure that your app’s AVAudioSession is set before this observer is added.

How to gracefully handle audio interruptions

Once the observer is set, handleAudioInterruption will now receive notifications when an interruption begins and ends.

@objc private func handleInterruption(notification: Notification) {
    guard let userInfo = notification.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSession.InterruptionType(rawValue: typeValue) else {
            return
    }
    
    switch type {
    case .began:
        // pause audio
        // update player UI state to paused
        
    case .ended:
        //  resume audio playback
        // update player UI state to playing
        // set audio session
        
    default: break
    }
}

If the AVAudioSession.InterruptionType == .ended, there may also be extra info inside the userInfo dictionary called AVAudioSession.InterruptionOptions which has a static property called shouldResume. This tells you whether your app is ready to be resumed.

guard let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt else {
    return
}

let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
if options.contains(.shouldResume) {
    // Interruption Ended - playback should resume
}

And that’s it. Your app is now configured to handle audio interruptions gracefully.

Conclusion

Handling audio interruption is very essential to good user experience, and by listening to the events sent by iOS, it is an easy win for any media application.

The post Handling audio interruptions in your iOS app appeared first on Now Playing Apps.

]]>
304
How to get media duration from your iOS video player (AVPlayer) https://nowplayingapps.com/how-to-get-media-duration-from-your-ios-video-player-avplayer/ Fri, 28 Sep 2018 19:20:33 +0000 https://nowplayingapps.com/?p=60 Building a video player to stream content is absolutely amazing, however your customer experience is only as good as how accurate the information that is presented to them. I have had challenges putting accurate data in front of the user, and here is how overcame this challenge when using AVPlayer in iOS apps.

The post How to get media duration from your iOS video player (AVPlayer) appeared first on Now Playing Apps.

]]>
Building a video player to stream content is absolutely amazing, however, your customer experience is only as good as how accurate the information that is presented to them. I have had challenges putting accurate data in front of the user, and here is how I overcame this challenge when using AVPlayer in iOS apps.

The problem

If you are developing a custom video player skin, chances you have to somehow show the overall duration of the current playing media. And using AVPlayerItem’s duration is the best way of doing this. And this is how duration is accessed.

(lldb) po playerItem!.duration.seconds
nan

However, when done this way after we start playing, we get this “nan” 🙁

What the… NaN

The problem here is we are trying to access the duration before AVPlayer gets a chance to download the content (or manifest if using a streaming protocol like HLS). And the solution is to wait for the right time.

So, when’s the right time?

Simple answer. When the content is ready. Or otherwise when AVPlayerItem.Status.readyToPlay. Let’s see it in code

let streamURL = "http://184.72.239.149/vod/smil:BigBuckBunny.smil/playlist.m3u8"
playerItem = AVPlayerItem(url: URL(string: streamURL)!)
player = AVPlayer(playerItem: playerItem)
        
playerItem?.addObserver(self, forKeyPath: #keyPath(AVPlayerItem.status), options: [.old, .new], context: nil)

and then observing like this:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    
    if keyPath == #keyPath(AVPlayerItem.status), let statusNumber = change?[.newKey] as? NSNumber {
        
        switch statusNumber.intValue {
        case AVPlayerItem.Status.readyToPlay.rawValue:
            let durationInSeconds = playerItem?.asset.duration.seconds ?? 0
            print("Ready to play. Duration (in seconds): \(durationInSeconds)")
        default: break
        }
    }
}

When you take this approach, make sure to remove the observer when you close the player or change playerItem inside the player

playerItem?.removeObserver(self, forKeyPath: #keyPath(AVPlayerItem.status))

It’s easier with Swift 4

With Swift 4, listening to changes like these are even easier. This is how:

observation = playerItem?.observe(\AVPlayerItem.status, changeHandler: { observedPlayerItem, change in
    if (observedPlayerItem.status == AVPlayerItem.Status.readyToPlay) {
        print("Current stream duration \(observedPlayerItem.duration.seconds)")
    }
})

It is easy to remove this observer, just assign it to nil. Here’s the link to the WWDC video

And that’s it! Happy streaming.

The post How to get media duration from your iOS video player (AVPlayer) appeared first on Now Playing Apps.

]]>
60