Download To Go
The Player library supports download of both clear and DRM protected contents.
Custom License Request Handling
Similar to playing DRM Protected contents, license request can be handled while downloading DRM protected contents as well as follows.
dwldManager.drmDelegate = dwldManager.drmDelegate = object : DRMDelegate {
override fun onKeyResponseRequired(
assetURL: String,
request: DRMKeyRequest,
callback: Callback<ByteArray, Error>
) {
// Add custom logic
}
}
- The license will have a specific validity and can be renewed in background using the
requestLicenseInfo
andrenewLicense
APIs in theDownloadManager
. - To ensure that there are no disruptions in Offline Playback , it is recommended to configure a
DRMDelegate
with the Player created for offline playback and handle the license request on demand. (This would work only if the device is connected to the internet).
The Platform Player library provides aDefaultDRMDelegate
which can be created as below:
val drmDelegate = DefaultDRMDelegate(coroutineScope)
Download Manager
Download Requirements
DownloadRequirements
is an enum of all possible requirements available to configure downloads. The only default requirement is DeviceRequirements.NETWORK
.
Name | Description |
---|---|
NETWORK | Represents the requirement that the device should have a network connection. |
NETWORK_UNMETERED | Represents the requirement that the device should have an unmetered network connection. |
DEVICE_IDLE | Represents the requirement that the device should be idle. |
DEVICE_CHARGING | Represents the requirement that the device should be charging. |
DEVICE_STORAGE_NOT_LOW | Represents the requirement that the device's available storage should not be low. This is generally the point where the user is given a "low storage" warning by the Android OS. |
val downloadProperties = listOf(
DownloadRequirements.NETWORK //Pass all preferred requirements
)
When the DEVICE_STORAGE_NOT_LOW
requirement is not fulfilled i.e. the device does not have enough storage, ongoing / new downloads are put in DownloadState.QUEUED
(Refer Download States) and resume only when the available storage is not low.
Download Properties
DownloadProperties
is a collection of configurations that dictate how downloads should be queued and processed.
Name | Type | Default value | Description |
---|---|---|---|
maxDownloadTasks | Int | 50 | The maximum number of tasks queued for download. (Must be greater than 0) |
maxParallelDownloads | Int | 3 | The maximum number of tasks parallel downloads. (Must be greater than 0) |
minRetryCount | Int | 5 | The minimum number of times that a download will be retried. A download will fail if the specified number of retries is exceeded without any progress being made. |
requirements | List<DownloadRequirements> | DownloadRequirements.NETWORK | The list of DownloadRequirements to be fulfilled for every download task. |
All the above-mentioned properties are optional, with the defaults being the best suited for most cases. The application can override the defaults by passing the preferred values to the constructor.
Creating a Download Manager
The DownloadManager can be initialized by passing app context on Application
's onCreate() lifecycle method.
DownloadManager.create(
this,
logger,
drmDelegate, // Refer Custom License Request handling section
downloadProperties
).resumeDownloads()
Creating a Download Request
// The preferred video bitrate (quality) to be downloaded
val preferredVideoBitrate = 100_000_000
// Creating a Clear Content Download Request
val clearContentDownloadRequest = DownloadRequest.Builder()
.mediaURL(contentURL)
.mediaType(MediaType.DASH)
.drmLicenseURL("")
.drmScheme(DRMScheme.NONE)
.preferredVideoBitrate(preferredVideoBitrate)
.addPreferredAudioLanguage("en")
.addPreferredTextLanguage("en")
.metadata(contentName)
.id(contentURL)
.build()
// Creating a DRM Content Download Request
val drmContentDownloadRequest = DownloadRequest.Builder()
.mediaURL(contentURL)
.mediaType(MediaType.DASH)
.drmLicenseURL(license)
.drmScheme(DRMScheme.WIDEVINE)
.preferredVideoBitrate(preferredVideoBitrate)
.addPreferredAudioLanguage("en")
.addPreferredTextLanguage("en")
.id(contentURL)
.metadata(contentName)
.build()
- Application developers must ensure that the Download ID passed during
DownloadRequest
creation is unique. - Application developers may also pass any metadata that they want to preserve as part of downloads.
- Application developers may pass any desired
preferredVideoBitrate
to download. The matching algorithm will automatically choose the closest available variant.
Enqueueing a Download Request
Before Enqueueing a download, it's a good practice to verify if there is already an existing download corresponding to the passed ID.
//It's assumed that DownloadManager.create is called beforehand
val downloadManager = DownloadManager.instance
val downloads = downloadManager.getAllDownloads()
val existingDownload = downloads.find { request.id == it.id }
if (existingDownload == null) {
val result = downloadManager.enqueueDownload(request)
}
The enqueueDownload API, returns a result which can either be a Success (or) Failure. Application developers can obtain the result and update the UI accordingly.
Pausing and Resuming Downloads
Any ongoing download can be paused and resumed.
fun toggleDownload(downloadID: String) {
val downloads = downloadManager.getAllDownloads()
val chosenDownload = downloads.find { it.id == downloadID } ?: return
if (chosenDownload.state == DownloadState.DOWNLOADING) {
downloadManager.pauseDownload(chosenDownload)
} else {
downloadManager.resumeDownload(chosenDownload)
}
}
Purging Downloads
Any ongoing (or) completed Download can be purged.
val downloads = downloadManager.getAllDownloads()
val chosenDownload = downloads.find { it.id == downloadID }
downloadManager.purgeDownload(chosenDownload)
Listening to Download Events
A DownloadManager.Listener
object can be implemented and attached to the DownloadManager
to get notified about the different changes in download tasks.
val downloadListener = object : DownloadManager.Listener {
override fun onDownloadStateChanged(download: Download) {
logger.info { "download state changed to :${download.state}" }
}
override fun onDownloadProgress(download: Download) {
logger.info { "download progressed percent :${download.progressPercent}" }
}
override fun onDownloadRemoved(download: Download) {
logger.info { "download removed :${download.id}" }
}
override fun onWaitingForRequirementsChanged() {
logger.error { "Waiting for requirements to be met." }
}
}
downloadManager.addListener(downloadListener)
Download States
The DownloadState represents the state of a particular Download
- QUEUED - Download request is enqueued but has not started yet due to policy restrictions such as maximum parallel download limit, specified requirements not met, etc.,
- DOWNLOADING - corresponding download task is active and the corresponding Media resources are downloaded.
- PAUSED - corresponding download task is suspended and will not be made active until explicitly resumed.
- FAILED - download has failed because of chunk unavailability / device storage unavailability / other server failures.
- COMPLETED - Download is complete and ready for offline playback.
- REMOVING - a transient state which indicates that the download is getting removed from the system.
- STALE - Indicates the download went into a stale state. This currently has no significance in android and is present only to be inline with iOS states.
Offline Playback
To play a downloaded content , one needs to configure the download ID in the PlayerRequest builder.
val downloads = downloadManager.getAllDownloads()
val completedDownload = downloads.find {
it.id == downloadID && it.state == DownloadState.COMPLETED
} ?: return
val player = PlayerBuilder()
.mediaURL(completedDownload.mediaURL)
.mediaType(MediaType.DASH)
.drmScheme(DRMScheme.WIDEVINE)
.drmLicenseURL("") //can be empty
.downloadID(completedDownload.id)
.build(applicationContext)
player.drmDelegate = DefaultDRMDelegate(coroutineScope)