This how-to guide describes how to implement playback of downloadable content-protected HLS using the THEOplayer iOS SDK. Currently, FairPlay is supported together with the Irdeto and DRMToday integrations.
In order to do offline DRM on iOS, your license key should be downloadable, and cacheable. The SDK will not be able to decrypt your content if your key expired.
This feature only works on iOS 10.0 and above.
Known limitations
The snippet below downloads a FairPlay HLS asset.
//example source
public static var sourceToBeCached: SourceDescription {
let src = "https://source.m3u8"
let merchant = "merchant"
let userId = "userId"
let sessionId = "sessionId"
let streamType = "application/x-mpegurl"
let drmConfig = DRMTodayDRMConfiguration(
licenseAcquisitionURL: "https://lic.staging.drmtoday.com/license-server-fairplay/",
certificateURL: "https://lic.staging.drmtoday.com/license-server-fairplay/cert/",
userId: userId,
sessionId: sessionId,
merchant: merchant
)
return SourceDescription(source: TypedSource(src: src, type: streamType, drm: drmConfig))
}
//example cache call
var cachingTask: CachingTask?
func cacheSource(SourceDescription source, Date expirationDate) {
cachingTask = THEOplayer.cache.createTask(source: source, parameters: CachingParameters(expirationDate: expirationDate))
if cachingTask != nil {
_ = cachingTask!.addEventListener(type: CachingTaskEventTypes.STATE_CHANGE) { event in
print("Received state change on caching task \(self.cachingTask!.source.sources[0].src) Status: \(self.cachingTask!.status)")
}
_ = cachingTask!.addEventListener(type: CachingTaskEventTypes.PROGRESS) { event in
print("Received progress on caching task \(self.cachingTask!.source.sources[0].src) Cached: ")
for timeRange in self.cachingTask!.cached {
print(timeRange.start, timeRange.end)
}
}
cachingTask.start()
}
}
func playSourceFromCache(SourceDescription source) {
theoplayer.source = source
theoplayer.play();
}
The snippet below deletes all cached assets
func cleanCache() {
for cachingTask in THEOplayer.cache.tasks {
print("Will remove caching task \(cachingTask.source.sources[0].src)")
cachingTask.remove()
}
}
Renew a DRM license with specific DRM configuration:
func renewLicense() {
let newDrmConfig = DRMTodayDRMConfiguration(
licenseAcquisitionURL: "https://lic.staging.drmtoday.com/license-server-fairplay/",
certificateURL: "https://lic.staging.drmtoday.com/license-server-fairplay/cert/",
userId: userId,
sessionId: sessionId,
merchant: merchant
)
cachingTask.license.renew(newDrmConfig) // or we can renew it with the old drmConfig too: cachingTask.license.renew()
}
Handle redirected manifest:
// cache source
let url = URL(string: "MASTER_MANIFEST_URL_THAT_GETS_REDIRECTED")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
Streams.SAVED_REDIRECTED_URL = (response?.url)!
cachingTaskSource.sources[0].src = Streams.SAVED_REDIRECTED_URL!
self.cachingTask = THEOplayer.cache.createTask(source: cachingTaskSource, parameters: CachingParameters(expirationDate: Date.distantFuture, bandwidth: cachingTaskBandwidth))
if self.cachingTask != nil {
_ = self.cachingTask!.addEventListener(type: CachingTaskEventTypes.STATE_CHANGE) { event in
if let cachingTask = self.cachingTask {
print("Received state change on caching task \(cachingTask.source.sources[0].src) Status: \(cachingTask.status)")
}
}
_ = self.cachingTask!.addEventListener(type: CachingTaskEventTypes.PROGRESS) { event in
print("Received progress on caching task \(self.cachingTask!.source.sources[0].src) Cached: ")
for timeRange in self.cachingTask!.cached {
print(timeRange.start, timeRange.end)
}
}
print("Did create caching task \(self.cachingTask!.source.sources[0].src)")
}
}
task.resume()
// playback of cached source
var source = cachingTaskSource
source.sources[0].src = Streams.SAVED_REDIRECTED_URL!
theoplayer.source = source
theoplayer.play()