# Apple FairPlay DRM

ByteArk Player SDK สำหรับ iOS รองรับ Apple FairPlay Streaming (FPS) (opens new window) ซึ่งช่วยให้สามารถเล่นเนื้อหาที่เข้ารหัสได้ ในการเปิดใช้งานฟีเจอร์นี้ คุณต้องทำการ implement protocol ByteArkPlayerDrmDataSource และเมื่อ SDK ตรวจพบว่าเนื้อหามีการเข้ารหัสด้วย FairPlay ระบบจะร้องขอให้แอปของคุณจัดเตรียมข้อมูลและคีย์ที่จำเป็นสำหรับการถอดรหัสเนื้อหานั้น

ByteArkPlayerDrmDataSource Protocol

// A dataSource requests the necessary data to decrypt Apple FairPlay Drm content
@objc public protocol ByteArkPlayerDrmDataSource: AnyObject {
    /// Called when player needs to decrypt the current protected content and requires a content identifier (content id)  from the application to start the decryption process
    ///
    /// - Parameters:
    ///     - url: The url of the resource being loaded.
    ///     - completionHandler: The handler block used to provide the content identifier to the ByteArk Player.
    ///
    func contentIdentifier(for url: URL, completionHandler handler: @escaping (_ contentIdentifier: Data?) -> Void)
    /// Called when player needs to decrypt the current protected content and requires an application certificate from the application to start the decryption process
    ///
    /// - Parameters:
    ///     - url: The url of the resource being loaded.
    ///     - completionHandler: The handler block used to provide the application certificate to the ByteArk Player, the Application Certificate data you receive after registering an FPS playback app.
    ///
    func applicationCertificate(for url: URL, completionHandler handler: @escaping (_ certificate: Data?) -> Void)
    /// Called when player  needs to decrypt the current protected content and requires a content key from the application to start the decryption process
    ///
    /// - Parameters:
    ///     - spcData: The SPC (Server Playback Context) message from the operating system which must be sent to the Key Server in order to obtain the CKC (Content Key Context) message.
    ///     - completionHandler: The completion block used to provide CKC (Content Key Context) that received from  the Server Response to the 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
28

# Implementation

WARNING

การถอดรหัส FairPlay สามารถใช้งานได้เฉพาะบนอุปกรณ์จริงเท่านั้น และจะไม่ทำงานบนตัวจำลอง (simulator)

  1. Implement the contentIdentifier(for url: URL, completionHandler handler: @escaping (_ contentIdentifier: Data?) -> Void) delegate method in your class

This delegate method requests the identifier for the protected content to be passed through the delegate method's completion block.

func contentIdentifier(for url: URL, completionHandler handler: @escaping (Data?) -> Void) {
    // extract content identifier from url
    if #available(iOS 16.0, *) {
        handler(url.host()?.data(using: .utf8))
    } else {
        // Fallback on earlier versions
        handler(url.host?.data(using: .utf8))
    }
}
1
2
3
4
5
6
7
8
9
  1. Implement the applicationCertificate(for url: URL, completionHandler handler: @escaping (_ certificate: Data?) -> Void) delegate method in your class

This delegate method requests an Application Certificate binary which must be passed through the completion block.

func applicationCertificate(for url: URL, completionHandler handler: @escaping (Data?) -> Void) {
    // request an application certificate from 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 the contentKey(with spcData: Data, completionHandler handler: @escaping (_ ckcData: Data?) -> Void) delegate method in your class

This delegate method provides the SPC (Server Playback Context) message from the operating system which must be sent to the Key Server in order to obtain the CKC (Content Key Context) message. The CKC message must be returned via the completion block under the response parameter.

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")
    // convert spc to base64 and set 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
26
27
  1. Set your ByteArkPlayerDrmDataSource implementation to 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