Offline Playback: Download to Own (DTO) and Rentals (DTR) for Android

The Ooyala SDK for Android utilizes the capabilities provided by Google's ExoPlayer to support offline playback. ExoPlayer allows parsing of DASH streams. The Ooyala SDK for Android utilizes Widevine as the DRM server to store a DRM license from a Widevine Server. Developers can utilize the Ooyala SDK for Androidin their apps to download DASH assets stored in our CMSs (e.g. Backlot) for offline playback.

Overview

With the Ooyala SDK, you will be able to:

  • Download an asset.
  • Cancel a download in progress.
  • Resume a download of an asset. This one applies when cancelling a download, if you try to download it again it will use the segments already downloaded previously so you will be saving time.
  • Listen to updates on the download process.
  • Delete a downloaded asset.
  • Play a downloaded asset.
  • Check license expiration date.
  • Prevent a downloaded asset from being played after a specific date.

We do not currently support:

  • Closed captions for the downloaded asset.
  • A promo or static image representing the downloaded asset.
  • Multi-audio (e.g. Spanish, English, etc). The SDK will download the default language for the asset.
  • Pausing a download in progress.
  • Estimating the size of the video to download.
  • Auto delete assets that have an expired license.
Note: Note: When uninstalling the app used to download assets, the contents of the downloaded assets will remain on the device. This is expected behavior. You have two options to remove downloaded assets:
  1. Using the DTO APIs, delete the assets before uninstalling the app (recommended), or
  2. Maually searching for the file and remove it (not supported)

Requirements

DTO and DTR using the Ooyala SDK requires:

  • Android 4.1 (API 16) for non protected clear DASH assets.
  • Android 4.3 (API 18) for Widevine DASH assets.
  • Google ExoPlayer.
  • Android Ooyala SDK v4.21.0 and above.
  • Widevine DASH assets for defining rentals entitlements (DTR).
  • Android 5+ only for DTR.

Android supports downloading the following video formats:

  • Non protected clear DASH assets.
  • Widevine DASH assets.

Offline Playback Use-Cases: Buy and Rent

There are several use cases for offline videos. We expose the same client APIs for Download to Own (DTO) and Download to Rent (DTR), but the difference is that we consider assets to be DTO if they are clear DASH or Widevine DASH with an infinite license, meaning the license will never expire, and if you download an asset with these characteristics you will be able to play as long as it remains on your device. On the other hand, Rentals (DTR) requires Widevine DASH assets with a finite license. The access windows are defined by the entitlement settings, setting the expiration date for when a downloaded asset will no longer be playable by the user.

To add rental access period rules for your assets, you must set entitlements for them. For more information, see Rights Locker API Reference.

Caveats

  • We encountered problems with Widevine license expiration in Android 4.x devices, where offline playback was still allowed after the expiration. Since the license has an expiration date it is not being enforced by the system (Android and Widevine), we are not able to support DTR functionality below Android 5. We recommend not deploying DTR to Android v.5 devices until a fix is found. For Android 5 and above, whenever the Widevine license expires, users will not be able to play the particular video linked to that license, as expected.

Using the Ooyala SDK for Android DTO and DTR capabilities

First, you need to set up your app with the Ooyala SDK. If you are not familiar with adding an external library like the Ooyala SDK.jar into an Android app, follow the tutorial we created on the following page: Getting Started: Setting Up Your Environment in Android Studio.

We provide the following classes for DTO:

When creating an instance of a DashDownloader to download an asset to their Android device, you must implement the DashDownloader.Listener to act upon the progress of the download. Supply a DashOptions instance with the configuration of the asset that the user wishes to download. The folder will be used in the future to playback the downloaded asset.

Runtime permissions

Android 6 (API 23), as well as later Android versions, need runtime permission to WRITE_EXTERNAL_STORAGE. This page gives detailed information on runtime permissions. This SampleApp contains sample code illustrating the request of runtime permissions to write external storage.

ExoPlayer classes

The following diagram shows what we're using from the ExoPlayer classes. We use them to download a DASH stream, parse the MPD and segments and prepare a Widevine session to retrieve the license to playback the offline video.

The workflow goes as follows:

  1. OoyalaPlayer creates ExoStreamPlayer via player factory based on the stream type and player configuration.
  2. ExoStreamPlayer selects renderer builder based on stream type (DASH renderer builder for DASH stream, HLS Renderer builder for HLS stream and Extractor renderer builder for MP4.
  3. RendererBuilder fetches stream manifest asynchronously and creates four track renderers: video, audio, text (for closed captions) and metadata (for ID3 tags, HLS only).
  4. Customized track selectors are created, either for bitrate control or for offline playback (only downloaded tracks are enabled in this scenario).
  5. If content is protected, DRM session manager needs to be created for license acquisition. The initial segment of the video stream is decoded, license acquisition request is raised and WidevineDrmCallback method gets called.
  6. In the meantime, ExoStreamPlayer creates the surface view and binds it to the player.
  7. Now we are ready to playback the video.

Sample App

We provide a sample app that you can use as a guide to build your own. It uses the classes we introduced for DTO and shows you a basic implementation to download an asset into your app. Here is the link to the sample app: https://github.com/ooyala/android-sample-apps/tree/stable/PlaybackLab/DownloadToOwnSampleApp.

Starting a download

To start a download, you must use an instance of DashDownloader and call the startDownload method. DashDownloader needs a DashOptions object to know which asset to download. You supply options like the pcode, embed code, domain, and embed token generator. The embedTokenGenerator is required for Widevine assets, as OPT is needed for all DRM assets. Look at the sample app for a more real example on how to start a download. The following snippet is a basic example of starting a download for a new asset.

public void startDownload() {
    File folder = android.os.Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
    DashOptions options = DashOptions.Builder(PCODE, EMBED_CODE, DOMAIN, folder).setEmbedTokenGenerator(this).build();
    DashDownloader dashDownloader = new DashDownloader(this, options, this);
 	
    downloader.startDownload();
}

The video asset will be saved in the given folder supplied to the DashOptions object. We assume the segment URLs are relative to the MPD file location; these terms are related to the DASH specification. All the downloaded DASH segments will be in the same folder as the MPD. If an MPD file already exists in the folder, an error will be thrown. We recommend deleting the contents of this directory when you encounter this error.

Note that we supply a DashDownloader.Listener to the DashDownloader constructor, as it is needed to get updates for the download. This is the only way to supply a listener to DashDownloader. The following code shows the method called in DashDownloader.Listener, when a download has completed successfully.

public void onCompletion() {
    // Download completed, you can ask for the license expiration here if it is a DRM asset
    long expiration = downloader.getLicenseExpirationDate();
}

The download process also downloads a Widevine license if the asset is protected. It is used to playback the content offline. The example above shows you how to retrieve the license expiration date when the download was completed. You cannot ask for expiration date before a download has completed as the files are not there yet.

Checking download progress

DashDownloader.Listener lets you know about the progress of a download through its onPercentage method.

public void onPercentage(int percentCompleted) {
    // percentCompleted is an integer between 0 and 100
}

Canceling a download in progress

During a download, you can cancel it by calling the abort method of the DashDownloader object.

public void abort() {
    downloader.abort();
}

Calling abort will trigger DashDownloader.Listener to call its onAbort method.

public void onAbort() {
    // Download canceled
}

Playing an offline video

Now that you have an offline video stored in your device, you can play it back using the OoyalaPlayer. The following code assumes you already setup an OoyalaPlayer instance in an Android Activity.

public void initializePlayer() {
    PlayerDomain domain = new PlayerDomain(DOMAIN);
    Options options = new Options.Builder().setShowPromoImage(false).setShowNativeLearnMoreButton(false).setUseExoPlayer(true).build();
    OoyalaPlayer player = new OoyalaPlayer(PCODE, domain, options);

    /*
     * add more configuration here, for example, the Skin Player setup
     */ 

    // Use the folder where we downloaded the asset
    File folder = new File(android.os.Environment.getExternalStoragePublicDirectory(android.os.Environment.DIRECTORY_MOVIES), EMBED_CODE);
    OfflineVideo offlineVideo = OfflineVideo.getVideo(folder); // Gets the video we already downloaded

    // Initialize the player with the downloaded video
    player.setUnbundledVideo(offlineVideo);
}

After calling setUnbundledVideo you should be able to playback the offline asset successfully using the OoyalaPlayer. Remember to use the same folder you used when downloading the asset.

Deleting a downloaded asset

Here's a code snippet showing how to delete both the contents and license of an asset.

public void deleteAsset() {
    // Assumes the downloader instance (DashDownloader) was setup with the correct folder.
    downloader.deleteAll();
}

This will delete the contents that were stored in the folder supplied to the DashOptions object when initializing a DashDownloader instance.

Reference

Was this article helpful?