Offline FairPlay Streaming for iOS with Media Services v3

Media Services logo v3


AMS website | Media Services v2 documentation | Code Samples | Troubleshooting guide

Azure Media Services provides a set of well-designed content protection services that cover:

  • Microsoft PlayReady

  • Google Widevine

    Widevine is a service provided by Google Inc. and subject to the terms of service and Privacy Policy of Google, Inc.

  • Apple FairPlay Streaming - An Apple DRM technology that is only available for video transferred over HTTP Live Streaming (HLS) on iOS devices, in Apple TV, and in Safari on macOS.

  • AES-128 encryption

Digital rights management (DRM)/Advanced Encryption Standard (AES) encryption of content is performed dynamically upon request for various streaming protocols. DRM license/AES decryption key delivery services also are provided by Media Services.

Besides protecting content for online streaming over various streaming protocols, offline mode for protected content is also an often-requested feature. Offline-mode support is needed for the following scenarios:

  • Playback when internet connection isn't available, such as during travel.
  • Some content providers might disallow DRM license delivery beyond a country/region's border. If users want to watch content while traveling outside of the country/region, offline download is needed.
  • In some countries/regions, internet availability and/or bandwidth is still limited. Users might choose to download first to be able to watch content in a resolution that is high enough for a satisfactory viewing experience. In this case, the issue typically isn't network availability but limited network bandwidth. Over-the-top (OTT)/online video platform (OVP) providers request offline-mode support.

This article covers FairPlay Streaming (FPS) offline-mode support that targets devices running iOS 10 or later. FairPlay Streaming is an Apple technology that is only available for video transferred over HTTP Live Streaming (HLS) on iOS devices, in Apple TV, and in Safari on macOS.

Note

Offline DRM is only billed for making a single request for a license when you download the content. Any errors are not billed.

Prerequisites

Before you implement offline DRM for FairPlay on an iOS 10+ device:

Configure content protection in Azure Media Services

In the GetOrCreateContentKeyPolicyAsync method, do the following:

Uncomment the code that configures the FairPlay policy option:

ContentKeyPolicyFairPlayConfiguration fairplayConfig = ConfigureFairPlayPolicyOptions();

Also, uncomment the code that adds CBCS ContentKeyPolicyOption into the list of ContentKeyPolicyOptions

options.Add(
    new ContentKeyPolicyOption()
    {
        Configuration = fairplayConfig,
        Restriction = restriction,
        Name = "ContentKeyPolicyOption_CBCS"
    });

Enable offline mode

To enable offline mode, create a custom StreamingPolicy and use its name when creating a StreamingLocator in CreateStreamingLocatorAsync.

CommonEncryptionCbcs objStreamingPolicyInput= new CommonEncryptionCbcs()
{
    Drm = new CbcsDrmConfiguration()
    {
        FairPlay = new StreamingPolicyFairPlayConfiguration()
        {
            AllowPersistentLicense = true // This enables offline mode
        }
    },
    EnabledProtocols = new EnabledProtocols()
    {
        Hls = true,
        Dash = true // Even though DASH under CBCS is not supported for either CSF or CMAF, HLS-CMAF-CBCS uses DASH-CBCS fragments in its HLS playlist
    },

    ContentKeys = new StreamingPolicyContentKeys()
    {
        // Default key must be specified if keyToTrackMappings is present
        DefaultKey = new DefaultKey()
        {
            Label = "CBCS_DefaultKeyLabel"
        }
    }
}

Now your Media Services account is configured to deliver offline FairPlay licenses.

Sample iOS Player

FPS offline-mode support is available only on iOS 10 and later. The FPS Server SDK (version 3.0 or later) contains the document and sample for FPS offline mode. Specifically, FPS Server SDK (version 3.0 or later) contains the following two items related to offline mode:

  • Document: "Offline Playback with FairPlay Streaming and HTTP Live Streaming." Apple, September 14, 2016. In FPS Server SDK version 4.0, this document is merged into the main FPS document.

  • Sample code: HLSCatalog sample (part of the Apple's FPS Server SDK) for FPS offline mode in the \FairPlay Streaming Server SDK version 3.1\Development\Client\HLSCatalog_With_FPS\HLSCatalog. In the HLSCatalog sample app, the following code files are used to implement offline-mode features:

    • AssetPersistenceManager.swift code file: AssetPersistenceManager is the main class in this sample that demonstrates how to:

      • Manage downloading HLS streams, such as the APIs used to start and cancel downloads and to delete existing assets off devices.
      • Monitor the download progress.
    • AssetListTableViewController.swift and AssetListTableViewCell.swift code files: AssetListTableViewController is the main interface of this sample. It provides a list of assets the sample can use to play, download, delete, or cancel a download.

These steps show how to set up a running iOS player. Assuming you start from the HLSCatalog sample in FPS Server SDK version 4.0.1, make the following code changes:

In HLSCatalog\Shared\Managers\ContentKeyDelegate.swift, implement the method requestContentKeyFromKeySecurityModule(spcData: Data, assetID: String) by using the following code. Let "drmUr" be a variable assigned to the HLS URL.

    var ckcData: Data? = nil

    let semaphore = DispatchSemaphore(value: 0)
    let postString = "spc=\(spcData.base64EncodedString())&assetId=\(assetIDString)"

    if let postData = postString.data(using: .ascii, allowLossyConversion: true), let drmServerUrl = URL(string: self.drmUrl) {
        var request = URLRequest(url: drmServerUrl)
        request.httpMethod = "POST"
        request.setValue(String(postData.count), forHTTPHeaderField: "Content-Length")
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        request.httpBody = postData

        URLSession.shared.dataTask(with: request) { (data, _, error) in
            if let data = data, var responseString = String(data: data, encoding: .utf8) {
                responseString = responseString.replacingOccurrences(of: "<ckc>", with: "").replacingOccurrences(of: "</ckc>", with: "")
                ckcData = Data(base64Encoded: responseString)
            } else {
                print("Error encountered while fetching FairPlay license for URL: \(self.drmUrl), \(error?.localizedDescription ?? "Unknown error")")
            }

            semaphore.signal()
            }.resume()
    } else {
        fatalError("Invalid post data")
    }

    semaphore.wait()
    return ckcData

In HLSCatalog\Shared\Managers\ContentKeyDelegate.swift, implement the method requestApplicationCertificate(). This implementation depends on whether you embed the certificate (public key only - sometimes referred to as the .der or .cer file) with the device or host the certificate on the web. The following implementation uses the hosted application certificate used in the test samples. Let "certUrl" be a variable that contains the URL of the application certificate.

func requestApplicationCertificate() throws -> Data {

        var applicationCertificate: Data? = nil
        do {
            applicationCertificate = try Data(contentsOf: URL(string: certUrl)!)
        } catch {
            print("Error loading FairPlay application certificate: \(error)")
        }

        return applicationCertificate
    }

For the final integrated test, both the video URL and the application certificate URL are provided in the section "Integrated Test."

In HLSCatalog\Shared\Resources\Streams.plist, add your test video URL. For the content key ID, use the FairPlay license acquisition URL with the skd protocol as the unique value.

Offline FairPlay iOS App Streams

Use your own test video URL, FairPlay license acquisition URL, and application certificate URL, if you have them set up. Or you can continue to the next section, which contains test samples.

Integrated test

Three test samples in Media Services cover the following three scenarios:

  • FPS protected, with video, audio, and alternate audio track
  • FPS protected, with video and audio, but no alternate audio track
  • FPS protected, with video only and no audio

You can find these samples at this demo site, with the corresponding application certificate hosted in an Azure web app. With either the version 3 or version 4 sample of the FPS Server SDK, if a master playlist contains alternate audio, during offline mode it plays audio only. Therefore, you need to strip the alternate audio. In other words, the second and third samples listed previously work in online and offline mode. The sample listed first plays audio only during offline mode, while online streaming works properly.

Offline Fairplay questions

See offline fairplay questions in the FAQ.

Storage of the FairPlay Private Key (.pfx) in Azure Key Vault

The private key (.pfx) that you receive from Apple should be treated as a secure certificate and can be stored in the Azure Key Vault.

To store the private key in the Key Vault, you need to first convert it into a base64 encoded text file. The private key should be stored as a "secret object" and not as a "certificate object" in Azure KeyVault due to the size of the cert (1024 bytes) which is not a supported size for Azure Key Vault "certificate objects". You can also optionally store the .pfx file password as a secret in the key vault.

  • The .pfx certificate file should first be converted to base 64 text file by the admin
  • Once converted, this file can be stored in Azure DevOps Services as a secure text file.
  • The string can then be stored in Azure KeyVault manually as a "secret object", or as part of a deployment/build script for your solution. An example of storing the FairPlay private certificate in Azure KeyVault can be seen in the Gridwich project sample code
  • Optionally, store the password for the .pfx file as a secret in the key vault.

Example CLI script to copy base64 encoded private key file to the Azure KeyVault:

set -eu
echo key vault : $SHARED_KV_NAME
echo "Copying FairPlay certificate to key vault as secret"
az keyvault secret set --vault-name $SHARED_KV_NAME -n ams-fairPlay-certificate-b64 -f $(FairPlayCertificate.secureFilePath) --output none