Downloaders

We developed different downloaders for handling HLS and FPS content. They can all be used behind the common interface DownloaderAPI

DownloaderAPI

The main data type in DownloaderAPI is a Download.

let url = URL(string: "..")!
let config = PlayerConfiguration(with: url, contentType: "...")

let downloader = PRESTOPlay.shared.downloader()
let download = downloader.createDownload(config, headers: [:])
downloader.prepareDownload(download.uuid) { download, error in
    guard let download else { return }
    var res = false;

    if let videoRendition = request.videoTracks[0].renditions[0] {
        res = request.selectVideoRendition(videoRendition)
    }
    if let audioRendition = request.audioTracks[0].renditions[0] {
        res = request.selectAudioRendition(audioRendition)
    }

    let delegate = DownloadDelegate({})
    let error = downloader.startDownload(download, delegate: delegate)
}

To receive events related to a Download we use a delegate:

class DownloadDelegate: DownloadDelegate {
    public let complete: () -> Void

    public init (_ complete: @escaping () -> Void) {
        self.complete = complete
    }

    func didStateChange(_ download: MediaDownload) {
        if (download.state == .success) {
            complete()
        }
    }

    func didProgressChange(_ download: MediaDownload) {
    }
}

License

If you want to prefetch the license without forcing the user to be online during the first playback, use the method PRESTOPlaySDK.shared.prefetcher(for:)

let prefetcher = PRESTOPlaySDK.shared.prefetcher(for: config)
prefetcher?.prefetchKeys() { error in
    if error != nil {
        // Handle error
    }
}

HLS Streams

For downloading HLS and FPS we created a wrapper around the native HLS downloader AVAssetDownloadTask

  • enables querying and selection of stream bitrates
  • enables querying of audio media streams and subtitle media tracks
  • adds support for pre-selecting audio and subtitle tracks before download begins (no multiple audio and subtitle tracks can be selected)
  • greatly simplifies API usage and the monitoring of downloading progress.
  • supports preselection of the preferred media bitrate. If no suitable media bitrate is found, the highest bitrate will be selected. Only one media bitrate can be selected, meaning that only one video rendition will be downloaded
  • background downloads are fully supported
  • if the content is encrypted, it’s possible to prefetch the license before starting the download. It’s also possible to get the license after the download has completed (and without even starting the playback)

Background download tasks are handled by NSURLSession underneath and there are differences between the test environment and the production environment.

For background downloads an instance of HLSDownloader should be kept in AppDelegate. This is the only supported way from Apple to preserve completionHandlers of background sessions in-between Application Lifecycle changes (in our example we use static class field, but it is also possible to use object variable in AppDelegate).

When additional tracks are selected, it’s not possible to know in advance the entire download size due to an API limitation. The download progress will restart from zero for each additional track. You can however differentiate between the main download (which includes the minimum set of tracks that can be played) and the additional tracks using the flag.

When using a master playlist, the CODECS attribute of the EXT-X-STREAM-INF tag may signal CODECs not supported for playback on the downloading device, but download of such streams will be possible. Check Apple FAQ for an updated list of recommended codecs.

There’s a known issue on iOS 11.0.3 where HLS Downloads fail and errors like these appear in the logs.

Download failed with error Error Domain=NSURLErrorDomain Code=-997 
"Lost connection to background transfer service" 
UserInfo={NSLocalizedDescription=Lost connection to  background transfer service} 
NSURLConnection finished with error - code -1002
An error occurred on the xpc connection: Error Domain=NSCocoaErrorDomain Code=4099
"The connection to service named com.apple.nsurlsessiond was invalidated from this  process.
An error occurred on the xpc connection: Error Domain=NSCocoaErrorDomain Code=4097
"connection to service named com.apple.nsurlsessiond"
UserInfo={NSDebugDescription=connection to service named com.apple.nsurlsessiond}

These issues come from the native API and they have been fixed from iOS 11.1.1.