Offline Playback
This tutorial will walk you through the facilities that the Bitmovin Player iOS SDK offers for working with offline content and offline playback.
Manage Offline Content For Playback
One of the challenges of enabling offline playback is downloading and managing assets. The Bitmovin SDK provides the ability to download content, as well as to pause, resume and cancel downloads. It also enables downloaded content to be deleted. These operations are provided via the OfflineContentManager
API, which is responsible for managing the state of the assets. Application lifecycle events such as the application going into background, being terminated by the user, or crashing are handled by the OfflineManager
.
Initialize the OfflineManager
You first need to initialize the singleton class OfflineManager
in your AppDelegate
class. To do so, you add the following code snippet in the application(_:didFinishLaunchingWithOptions:)
method:
OfflineManager.initializeOfflineManager()
App Lifecycle Support
In order to appropriately handle application lifecycle events, such as the application going into background, add the following code into application(_:handleEventsForBackgroundURLSession:completionHandler:)
in your AppDelegate
:
OfflineManager.sharedInstance().add(completionHandler: completionHandler, for: identifier)
Download protected or unprotected content
Before you can download the content, you first need to create an instance of a SourceConfig
that represents the asset to be downloaded.
let sourceConfig = SourceConfig(url: URL(string: "https://www.example.com/stream.m3u8")!)
sourceConfig.title = "Sample Content"
sourceConfig.sourceDescription = "Description of this content"
In case the source is protected by Digital Rights Management (DRM), you just need to provide its DRM configuration like you would do for online playback:
let fpsConfig = FairplayConfig(license: licenseUrl, certificateURL: certificateUrl)
sourceConfig.drmConfig = fpsConfig
You will also need a reference to the OfflineManager
using the sharedInstance()
method, which you can then use to get access to an OfflineContentManager
for your SourceConfig
. The OfflineContentManager
instance manages offline content and offline DRM related tasks for this single SourceConfig
:
let offlineManager = OfflineManager.sharedInstance()
do {
let offlineContentManager = try offlineManager.offlineContentManager(for: sourceConfig)
} catch {
print("error creating offline content manager \(error)")
}
Now that you have both the offline content manager and source config for the asset, you can already kick off the download operation, by calling the download()
method.
offlineContentManager.download()
OfflineState
You can check the state of the download operation with the offlineState
property, which is very useful for checking on the state of an asset before performing further actions. For example, checking the asset's offline state before updating the UI or allowing the user to cancel a download:
switch offlineContentManager.offlineState {
case .downloading:
print("[MyViewController] asset download is in progress")
case .suspended:
print("[MyViewController] asset download is suspended")
case .downloaded:
print("[MyViewController] asset download is finished")
case .canceling:
print("[MyViewController] canceling asset download")
case .notDownloaded:
print("[MyViewController] asset is not downloaded")
@unknown default:
break
}
Events
Additionally, the SDK allows you to add event listeners for tracking all the offline states mentioned above.
offlineContentManager.add(listener: self)
And then you can track the events for further action:
extension MyViewController: OfflineContentManagerListener {
func onOfflineError(
_ event: OfflineErrorEvent,
offlineContentManager: OfflineContentManager
) {
let errorMessage = event.message
print("[MyViewController] Download resulted in error: \(errorMessage)")
}
func onContentDownloadFinished(
_ event: ContentDownloadFinishedEvent,
offlineContentManager: OfflineContentManager
) {
print("[MyViewController] Download Finished")
}
func onContentDownloadProgressChanged(
_ event: ContentDownloadProgressChangedEvent,
offlineContentManager: OfflineContentManager
) {
print("[MyViewController] Progress")
}
func onContentDownloadSuspended(
_ event: ContentDownloadSuspendedEvent,
offlineContentManager: OfflineContentManager
) {
print("[MyViewController] Suspended")
}
func onContentDownloadResumed(
_ event: ContentDownloadResumedEvent,
offlineContentManager: OfflineContentManager
) {
print("[MyViewController] Resumed")
}
func onContentDownloadCanceled(
_ event: ContentDownloadCanceledEvent,
offlineContentManager: OfflineContentManager
) {
print("[MyViewController] Canceled")
}
}
You can find more information about each of the above events in our documentation:
onOfflineError
onContentDownloadFinished
onContentDownloadProgressChanged
onContentDownloadSuspended
onContentDownloadResumed
onContentDownloadCanceled
Cancel an ongoing download
The OfflineContentManager
offers the cancelDownload()
method that cancels a running or suspended download operation. When a download was canceled successfully, also all partially downloaded data is deleted automatically.
guard offlineContentManager.offlineState == .downloading ||
offlineContentManager.offlineState == .suspended else {
return
}
offlineContentManager.cancelDownload()
Suspend an ongoing download
The suspendDownload()
method can be used to suspend (pause) an ongoing download operation.
guard offlineContentManager.offlineState == .downloading else {
return
}
offlineContentManager.suspendDownload()
Resume a suspended download
If the download of a given source config has .suspended state
, it can be resumed at any time with the resumeDownload()
method:
guard offlineContentManager.offlineState == .suspended else {
return
}
offlineContentManager.resumeDownload()
Delete all downloaded data
You can force to delete all cached data for a given source config with the deleteOfflineData()
method:
guard offlineContentManager.offlineState == .downloaded else {
return
}
offlineContentManager.deleteOfflineData()
Overview of states and allowed method calls
The following table outlines all allowed actions in all possible offline states a source config can have. It also shows which listener methods are called on the OfflineContentManagerListener
:
Current State | Method call triggering transition | OfflineContentManagerListener Event | Following State |
---|---|---|---|
NotDownloaded | download | Downloading | |
Downloading | cancelDownload | Canceling | |
Downloading | suspendDownload | onContentDownloadSuspended | Suspended |
Downloading | onContentDownloadFinished | Downloaded | |
Downloading | onContentDownloadProgressChanged | Downloading | |
Downloaded | deleteOfflineData | NotDownloaded | |
Suspended | resumeDownload | onContentDownloadResumed | Downloading |
Suspended | cancelDownload | Canceling | |
Canceling | onContentDownloadCanceled | NotDownloaded | |
Downloaded | renewOfflineLicense | onOfflineContentLicenseRenewed | Downloaded |
Play online/offline content seamlessly
Now that you know how to manage assets for offline playback, you can focus on the what is needed to play back the asset. Assuming the offline state of of the offline content manager for the source config is .downloaded
, .downloading
or .suspended
, before passing the source config to the player, you should check whether the device has access to the internet or not. If the device does not have internet access, the method offlineContentManager.createOfflineSourceConfig(restrictedToAssetCache:)
can be used to create an offline source config from the initial downloaded source config:
guard offlineContentManager.offlineState == .downloaded,
let offlineSourceConfig = offlineContentManager.createOfflineSourceConfig(restrictedToAssetCache: true) else {
finishWithError(title: "Error", message: "The device seems to be offline, but no offline content for the selected source available.")
return
}
sourceConfig = offlineSourceConfig
This method gives you the option to restrictToAssetCache
, which will restrict the playback to the audio and subtitle tracks which are cached on disk. As a result, tracks which are not cached, do not show up as selectable in the player UI. If the device has access to the internet, you should probably set false
to this parameter as it would allow the user to select tracks that are not cached yet. In the case that the offline state for the offline content manager for the source config is .notDownloaded
or .canceling
, and the device does not have access to the internet, then the playback will not be possible and you should notify the user. Once these checks are done and playback is possible, you can load the source config into the player:
let player = PlayerFactory.createPlayer()
player.add(listener: self)
player.load(sourceConfig: sourceConfig) // this can be an instance of `SourceConfig` or `OfflineSourceConfig`
Sample App
Here you will find a set of iOS sample applications. There is also a dedicated app that demonstrates the offline playback capabilities.
Updated 11 months ago