7
votes

I am writing an iOS app in Swift for HLS live streaming. I want my app logic to be notified when each HLS segment request is initiated during playback (and what the respective URL is). I have tried to observe changes to various properties of AVPlayer and AVPlayerItem using KVO. Though, it only informs me of when the playback is initiated. For example, adding the following observer triggers a invocation of observeValue method when playback starts, but I have not found a way to be continuously notified of each segment request.

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

Is there a method with KVO that should allow me to be notified of each segment request? Are there other objects/API:s not related to AVFoundation that I should consider?

/George

2
Hi @George_T , did you able to do this some how ? It's so frustrating that AVPlayer or AVPlayerItem item don't have an event like onFragmentChange in hls.jsUdaya Sri

2 Answers

2
votes

I follow Fabian's approach when I need to debug HLS streams. It shows useful info every time that there is an update related to the current stream being played. Here's the code I use, hope it helps anyone facing a similar issue!

func trackPlayerLogs() {
    NotificationCenter.default.addObserver(self,
                                           selector: #selector(handleAVPlayerAccess),
                                           name: NSNotification.Name.AVPlayerItemNewAccessLogEntry,
                                           object: nil)
}

func handleAVPlayerAccess(notification: Notification) {

    guard let playerItem = notification.object as? AVPlayerItem,
        let lastEvent = playerItem.accessLog()?.events.last else {
        return
    }

    let indicatedBitrate = lastEvent.indicatedBitrate

    print("--------------PLAYER LOG--------------")
    print("EVENT: \(lastEvent)")
    print("INDICATED BITRATE: \(indicatedBitrate)")
    print("PLAYBACK RELATED LOG EVENTS")
    print("PLAYBACK START DATE: \(lastEvent.playbackStartDate)")
    print("PLAYBACK SESSION ID: \(lastEvent.playbackSessionID)")
    print("PLAYBACK START OFFSET: \(lastEvent.playbackStartOffset)")
    print("PLAYBACK TYPE: \(lastEvent.playbackType)")
    print("STARTUP TIME: \(lastEvent.startupTime)")
    print("DURATION WATCHED: \(lastEvent.durationWatched)")
    print("NUMBER OF DROPPED VIDEO FRAMES: \(lastEvent.numberOfDroppedVideoFrames)")
    print("NUMBER OF STALLS: \(lastEvent.numberOfStalls)")
    print("SEGMENTS DOWNLOADED DURATION: \(lastEvent.segmentsDownloadedDuration)")
    print("DOWNLOAD OVERDUE: \(lastEvent.downloadOverdue)")
    print("--------------------------------------")
}
1
votes

I don't know of an easy way to be notified of each segment request while it's happening. You should look at AVPlayerItem's accessLog property and look at the AVPlayerItemAccessLogEvents in the log. These will describe both network and playback events. I highly recommend this approach if it fits your needs.

Another way is to set up your app as an HTTP server and point an AVURLAsset/AVPLayerItem at the local server which will then have to translate those requests to an external server. This is much more complex, difficult, error-prone, and is nearly guaranteed to have bad performance. Please do not do this.


addendum:

You may be tempted to look at AVAssetResourceLoaderDelegate as it says you can handle resource requests on behalf of an AVURLAsset. Unfortunately it does not go through the loader for segments. It seems to be for playlists, decryption keys and other such assets.