# Apple FairPlay

Note

This feature is available only to ByteArk Video Cloud for Business customers. Please contact sales@byteark.com if you would like to use it.

ByteArk Player SDK for iOS supports Apple FairPlay Streaming (FPS) (opens new window) for DRM-protected playback. To enable it, implement the ByteArkPlayerDrmDataSource protocol — when the SDK detects FairPlay-encrypted content, it asks your app for the data and keys needed to decrypt.

Caution

FairPlay decryption works only on physical devices, not on the Simulator.

ByteArkPlayerDrmDataSource Protocol

// A dataSource that provides the data required to decrypt Apple FairPlay DRM content
@objc public protocol ByteArkPlayerDrmDataSource: AnyObject {
  /// Called when the player needs a content identifier (content id) to decrypt protected content.
  ///
  /// - Parameters:
  ///   - url: The URL of the resource being loaded.
  ///   - completionHandler: A block used to deliver the content identifier back to ByteArk Player.
  func contentIdentifier(for url: URL, completionHandler handler: @escaping (_ contentIdentifier: Data?) -> Void)
  /// Called when the player needs an application certificate to decrypt protected content.
  ///
  /// - Parameters:
  ///   - url: The URL of the resource being loaded.
  ///   - completionHandler: A block used to deliver the Application Certificate data
  ///     (received after registering an FPS playback app) back to ByteArk Player.
  func applicationCertificate(for url: URL, completionHandler handler: @escaping (_ certificate: Data?) -> Void)
  /// Called when the player needs a content key to decrypt protected content.
  ///
  /// - Parameters:
  ///   - spcData: SPC (Server Playback Context) data which must be sent to the Key Server
  ///     to obtain the CKC (Content Key Context).
  ///   - completionHandler: A block used to deliver the CKC back to ByteArk Player.
  func contentKey(with spcData: Data, completionHandler handler: @escaping (_ ckcData: Data?) -> Void)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# Implementation

  1. Implement contentIdentifier(for url: URL, completionHandler:) in your class. This method asks for the identifier of the protected content.
func contentIdentifier(for url: URL, completionHandler handler: @escaping (Data?) -> Void) {
  // Extract the content identifier from the URL
  if #available(iOS 16.0, *) {
    handler(url.host()?.data(using: .utf8))
  } else {
    handler(url.host?.data(using: .utf8))
  }
}
1
2
3
4
5
6
7
8
  1. Implement applicationCertificate(for url: URL, completionHandler:). This method requests the Application Certificate that must be passed back through the completion block.
func applicationCertificate(for url: URL, completionHandler handler: @escaping (Data?) -> Void) {
  // Request the application certificate from your server
  var request = URLRequest(url: URL(string: "APPLICATION_CERTIFICATE_URL")!)
  request.httpMethod = "GET"
  let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
    handler(data)
  }
  task.resume()
}
1
2
3
4
5
6
7
8
9
10
11
  1. Implement contentKey(with spcData: Data, completionHandler:). The SPC data must be sent to your Key Server to obtain the CKC, which is returned through the completion block.
func contentKey(with spcData: Data, completionHandler handler: @escaping (Data?) -> Void) {
  var request = URLRequest(url: URL(string: "APPLE_FAIRPLAY_KEY_SERVER_URL")!)
  request.httpMethod = "POST"
  request.setValue("application/json", forHTTPHeaderField: "Content-Type")
  // Encode SPC as base64 and set the JSON body
  let json: [String: Any] = [ "spc": spcData.base64EncodedString() ]
  let jsonData = try? JSONSerialization.data(withJSONObject: json)
  request.httpBody = jsonData
  let task = URLSession.shared.dataTask(with: request) {  (data, response, error) in
    guard let data = data,
          let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
          let ckcString = json["ckc"] as? String,
          let ckcData = Data(base64Encoded: ckcString)
    else {
      handler(nil)
      return
    }
    handler(ckcData)
  }
  task.resume()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
  1. Set your ByteArkPlayerDrmDataSource implementation on ByteArkPlayerItemBuilder.
do {
  let item = try ByteArkPlayerItemBuilder()
    .title("FairPlay Source")
    .media(URL(string: "FAIRPLAY_MEDIA_URL")!)
    .drmDataSource(YOUR_DRM_DATA_SOURCE_IMPLEMENTATION)
    .build()
  let config = try ByteArkPlayerConfigBuilder()
    .autoplay(true)
    .item(item)
    .build()
  bytearkPlayerViewController.player.configure(with: config)
} catch {
  print(error.localizedDescription)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16