Documentation

Offline Storage & Playback

Scope of support

Radiant Media Player supports download, storage and playback of offline content in MPEG-DASH & HLS. Our implementation is based on Shaka player offline capabilities which use IndexedDB for storage.

This feature should also help developers building media applications with Radiant Media Player to comply with Google Progressive Web Apps requirements.

While the player should provide a straight-forward approach to adding offline support to your application or site, we recommend developers looking to implement this feature to first familiarise themselves with general offline concepts and limitations. This storage for the web article should help.

Offline storage and playback support through hls.js or progressive download is not provided.

Offline support is only provided for on-demand video streaming. Live and DVR streaming are not supported for offline storage and playback due to their nature.

Supported features

Supported streaming protocols:

  • CMAF MPEG-DASH
  • CMAF HLS
  • Offline DRM (persistent license)
  • Audio-only streaming

Supported environments

Supported:

  • Latest Chrome, Firefox, Opera and Safari for Desktop
  • Latest Microsoft Edge and Microsoft Edge Legacy for Windows 7+
  • Latest Chrome, Firefox, Opera for Android 6+
  • Latest Safari for iOS 17+ (set forceMseHlsOnAppleDevices to true if using HLS)
  • Latest Safari for iPadOS 13+ (set forceMseHlsOnAppleDevices to true if using HLS)
  • Ionic, Flutter, Apache Cordova, WebView mobile apps for Android 6+
  • Ionic, Flutter, Apache Cordova, WebView mobile apps for iOS 17+ (set forceMseHlsOnAppleDevices to true if using HLS)
  • Ionic, Flutter, Apache Cordova, WebView mobile apps for iPadOS 13+ (set forceMseHlsOnAppleDevices to true if using HLS)

Storage limitations

How much can I store?

Radiant Media Player makes use of IndexedDB to store content locally for offline playback. Data for an origin (e.g. a specific Internet domain) can be stored in two ways: persistent and best-effort. Radiant Media Player always stores content data as best-effort. Therefore content stored must be considered transient with Radiant Media Player offline feature, meaning your app needs to expect that the data will be lost eventually. This is because the OS will reclaim storage from browsers and WebViews if a device is running low on space. Note that research from the Chrome team shows that data is very rarely deleted by the browser. If a user visits a website regularly, there is very little chance that its stored data, even in best-effort mode, will get evicted by the browser. So the word "transient" mentioned above should not be blocking for most use-cases.

  • Chrome for Desktop and Android: up to 60% of the total disk space for an origin - but please note:
    • In incognito mode, Chrome reduces the amount of storage an origin can use to approximately 5% of the total disk space
    • If the user has enabled "Clear cookies and site data when you close all windows" in Chrome, the storage quota is significantly reduced to a maximum of approximately 300MB.
    • Other Chromium-based browsers like Microsoft Edge generally follows Chrome rules but they may also have limitations of their own
  • Ionic, Flutter, Apache Cordova, WebView mobile app for Android: up to 60% of the total disk space
  • Firefox for Desktop and Android: 10% of the total disk size or 10 Go, whichever is smaller for best-effort storage mode
  • Safari for iOS 17+, iPadOS 17+ and macOS Sonoma+: up to 60% of the total disk space - see WebKit note
  • Ionic, Flutter, Apache Cordova, WebView mobile app for iOS 17+, iPadOS 17+ and macOS+ Sonoma: up to 15% of the total disk space - see WebKit note

Persistent DRM licenses

The player automatically downloads and stores persistent licenses when available. Persistent licenses allow for offline playback of DRM protected content without the need for an Internet connection to access content.

Persistent licenses are supported for:

  • Widevine DRM

In the following environments:

  • Chrome 64+, Samsung Internet 12+ for Android 6+
  • Chrome 64+ for macOS 10.12+
  • Chrome 64+, Opera 64+, Microsoft Edge 79+, for Windows 10+
  • Chrome 64+ for Chromebooks (ChromeOS)

Persistent licenses are NOT supported in the following environments:

  • Safari (all platforms)
  • Firefox (all platforms)
  • Mobile apps for Android or iOS (Ionic, Flutter, Apache Cordova, WebView)

For platforms where storage of persistent licenses is not available you will get offline storage of protected content on all DRM-enabled environments, at the cost of needing a network connection at playback time to retrieve licenses.

HLS support

While we generally recommend using MPEG-DASH for offline playback, you may also opt for HLS based on your project requirement. You will need the following:

  • A CMAF / fMP4 HLS stream (offline is not supported with HLS-TS)
  • Set hlsEngine: 'shakaplayer' player setting
  • Set forceHlsJSOnAppleDevices: true player setting to enable support for Apple devices

Offline player settings

shakaOffline: Boolean

Enables or not offline features in Radiant Media Player. Default: false.

shakaOfflinePreferredTrackQuality: Number

Specifies which quality to download for offline storage when multiple choices are available.

  • -4: uses Shaka player default selection algorithm which generally selects the highest non-HD quality
  • -3: pick the highest available quality
  • -2: pick the lowest available quality
  • -1: pick the upper average quality
  • n (where n is an integer >= 0): pick the nth quality

Default: -1.

Offline API events

  • downloadstarted fires when download of content for offline playback has started, after download API method has been called
  • downloadprogress fires each time new data is appended to offline storage for a given running download - this event has progress value (Number) as event.data property
  • downloadcompleted fires when download of content for offline playback has completed, after download API method has been called
  • downloadremoved fires when the targeted download has been removed from storage, after calling removeDownload API method
  • abortdownloadcompleted fires when the targeted download has been aborted, after calling abortDownload API method

Offline API methods

listDownloadedContent() Promise.<DownloadedItem[]|WarningData)

rmp.listDownloadedContent().then(downloadedItems => {
  console.log(`Downloaded items: ${downloadedItems.length}`);
}).catch(warning => {
  console.warn(warning);
});

Returns a Promise that resolves if listDownloadedContent request has completed or rejects otherwise with a WarningData Object. Request the player to query the current list of downloaded items. Each item of the Array of DownloadedItem will have its information stored as a DownloadedItem Object with the following structure:

{
  appMetadata: Object,
  duration: Number,
  expiration: Number,
  offlineUri: String,
  originalManifestUri: String,
  size: Number
}

If no downloaded item is found the following default Array of DownloadedItem Object will be returned.

[{
  appMetadata: {},
  duration: -1,
  expiration: -1,
  offlineUri: '',
  originalManifestUri: '',
  size: -1
}]

download() Promise.<|WarningData>

rmp.download().then(() => {
  console.log('download has completed');
}).catch(warning => {
  console.warn(warning);
});

Returns a Promise that resolves if download request has completed or rejects otherwise with an WarningData Object. Request the player to start downloading content for offline storage. The downloadcompleted API event will fire when download has finished, while the downloadstarted and downloadprogress API events will give you status information about the download.

downloadProgress getter

const downloadProgress = rmp.downloadProgress;

This getter should be queried when downloadprogress API event fire and will return a Number between 0 and 1 representing the progress for the current download. Returns -1 if this value is not available.

loadDownload(storedItem) Promise.<|WarningData>

rmp.loadDownload(storedItem).then(() => {
  console.log('loadDownload has completed');
}).catch(warning => {
  console.warn(warning);
});

Returns a Promise that resolves if loadDownload request was successful or rejects to a WarningData Object. When a downloaded item has been listed with the listDownloadedContent API method, this item can be passed to the loadDownload API method to start playing this item. When playback starts for a stored item the player will fire a playing API event.

removeDownload(storedItem) Promise.<|WarningData>

rmp.removeDownload(storedItem).then(() => {
  console.log('removeDownload has completed');
}).catch(warning => {
  console.warn(warning);
});

Returns a Promise that resolves if removeDownload request was successful or rejects to a WarningData Object. When a downloaded item has been listed with the listDownloadedContent API method, this item can be passed to the removeDownload API method to be removed from storage. When an item has been removed from storage, the downloadremoved API event will fire.

abortDownload() Promise.<|WarningData>

rmp.abortDownload().then(() => {
  console.log('abortDownload has completed');
}).catch(warning => {
  console.warn(warning);
});

Returns a Promise that resolves if abortDownload request was successful or rejects to a WarningData Object. When a download is currently running it is possible to abort it by calling abortDownload. When a download has been aborted, the abortdownloadcompleted API event will fire.

Offline API error management

The player will fire the following warning API event through the offline API. See the error management docs for more information.

  • 3001: storage has not been initialized (shakaOffline setting is set to false or stream is not on-demand)
  • 3002: could not download offline content
  • 3003: could not list offline downloaded content
  • 3004: could not remove offline downloaded content
  • 3005: could not load offline content - invalid input
  • 3006: could not query temporary storage usage and availability
  • 3007: offline storage is not supported in this environment
  • 3008: could not abort download of offline content

Implementation example

To help you get started we have released a full usage example with source code here.

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License.

©2015-2025 Radiant Media Player. All Rights Reserved.