How to do offline Playback using Caching API

Introduction

This article describes how to implement playback of downloadable non-protected and DRM content-protected streams using THEOplayer.

For Offline Playback of DRM content-protected - THEOplayer is currently integrated with following:

Support Matrix 

Web iOS Android Android Tv
 No Yes  Yes  Yes

 

Pre-requirements 

In order to do offline DRM on Android, your license key should be downloadable and cacheable. The SDK will not be able to decrypt your content if your key expired.

Before you start, add the following transitive theoplayer dependency to the build.gradle file:

dependencies {

    // other dependencies..

    implementation 'com.google.android.exoplayer:exoplayer:2.6.1'

}

 

In order to do offline DRM on iOS, your license key should be downloadable and cacheable. The SDK will not be able to decrypt the content if the player key has expired.

This feature only works on iOS 10.0 and above.

Known limitations
  • If your manifest gets redirected when you are caching and playing back a resource, you need to save your redirected manifest URL (what is being cached) and provide that (redirected) URL to the player when you would like to play back from the cache.
  • Only completely cached videos can be played offline. (When the CachingTask status is DONE, then the video is playable without any internet connection
  • HLS manifests can have a tag #EXT-X-PLAYLIST-TYPE:VOD (https://developer.apple.com/documentation/http_live_streaming/example_playlists_for_http_live_streaming/video_on_demand_playlist_construction)
    If this tag is not present in the manifest of the cached quality, THEOplayer will make extra request for the manifest event if the video is completely cached. To avoid from these extra request, you need to provide EXT-X-PLAYLIST-TYPE:VOD in your manifests.

 

Code Example 

The example is using DRMtoday Configuration to display downloading and playabck properties/methods of Caching API. For all possible API calls, we refer you to the documentation available in the license that you've received or Additional features

Note: The code will change as per other DRM Vendors stated above.

How to Download:

//For Android SDK

private
 void cacheSource(String sessionId, String src, String amount, Date expirationDate) {

    Cache cache = THEOplayerGlobal.getSharedInstance(this).getCache();

    DRMConfiguration drmToday = createDrmTodayConfiguration(sessionId);

    SourceDescription source = sourceDescription(typedSource(src).drm(drmToday).build()).build();

    CachingParameters cachingParameters = new CachingParameters(amount, expirationDate);

    CachingTask cachingTask = cache.createTask(source, cachingParameters);

    cachingTask.addEventListener(CachingTaskEventTypes.CACHING_TASK_STATE_CHANGE, e -> {

        switch (cachingTask.getStatus()) {

            case DONE:

                // handle success..

                break;

            case ERROR:

                // handle error..

                break;

            default: // do nothing
                break;
        }
    });
    cachingTask.start();
}

private DRMConfiguration createDrmConfiguration(String sessionId) {

    KeySystemConfiguration widevine = KeySystemConfiguration.Builder

        .keySystemConfiguration(C.LICENSE_ACQUISITION_URL)

        .licenseType(LicenseType.PERSISTENT)

        .build();

    return YOUR_DRM_INTEGRATION_WITH_WIVDEVINE(widevine)

}

  

// For iOS SDK

public static
var source: 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://.....",
certificateURL: "https://.....",
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()
}
}

How to Playback:

//For Android SDK

private
 void playSourceFromCache(String sessionId, String src) {

    DRMConfiguration drmConf = createDrmConfiguration(sessionId);

    SourceDescription sourceDescription = sourceDescription(typedSource(src).drm(drmConf).build()).build();

    tpv.getPlayer().setSource(sourceDescription);

    tpv.getPlayer().play();

}
//For iOS SDK

func playSourceFromCache(SourceDescription source) {

    theoplayer.source = source
    theoplayer.play();

}