# 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
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
- 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
2
3
4
5
6
7
8
- 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
2
3
4
5
6
7
8
9
10
11
- 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
- Set your
ByteArkPlayerDrmDataSourceimplementation onByteArkPlayerItemBuilder.
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16