Skip to content

Instantly share code, notes, and snippets.

@fousa
Last active June 1, 2023 12:28
Show Gist options
  • Save fousa/5709fb7c84e5b53dbdae508c9cb4fadc to your computer and use it in GitHub Desktop.
Save fousa/5709fb7c84e5b53dbdae508c9cb4fadc to your computer and use it in GitHub Desktop.
Integrate HLS with FairPlay.
class FairPlayer: AVPlayer {
private let queue = DispatchQueue(label: "com.icapps.fairplay.queue")
func play(asset: AVURLAsset) {
// Set the resource loader delegate to this class. The `resourceLoader`'s delegate will be
// triggered when FairPlay handling is required.
asset.resourceLoader.setDelegate(self, queue: queue)
// Load the asset in the player.
let item = AVPlayerItem(asset: asset)
// Set the current item in this player instance.
replaceCurrentItem(with: item)
// Start playing the item. From the moment the `play` is triggered the `resourceLoader` will
// do the rest of the work.
play()
}
}
extension FairPlayer: AVAssetResourceLoaderDelegate {
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
// We first check if a url is set in the manifest.
guard let url = loadingRequest.request.url else {
print("๐Ÿ”‘", #function, "Unable to read the url/host data.")
loadingRequest.finishLoading(with: NSError(domain: "com.icapps.error", code: -1, userInfo: nil))
return false
}
print("๐Ÿ”‘", #function, url)
// When the url is correctly found we try to load the certificate date. Watch out! For this
// example the certificate resides inside the bundle. But it should be preferably fetched from
// the server.
guard
let certificateURL = Bundle.main.url(forResource: "certificate", withExtension: "der"),
let certificateData = try? Data(contentsOf: certificateURL) else {
print("๐Ÿ”‘", #function, "Unable to read the certificate data.")
loadingRequest.finishLoading(with: NSError(domain: "com.icapps.error", code: -2, userInfo: nil))
return false
}
// Request the Server Playback Context.
let contentId = "hls.icapps.com"
guard
let contentIdData = contentId.data(using: String.Encoding.utf8),
let spcData = try? loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData, options: nil),
let dataRequest = loadingRequest.dataRequest else {
loadingRequest.finishLoading(with: NSError(domain: "com.icapps.error", code: -3, userInfo: nil))
print("๐Ÿ”‘", #function, "Unable to read the SPC data.")
return false
}
// Request the Content Key Context from the Key Server Module.
let ckcURL = URL(string: "https://hls.icapps.com/ckc")!
var request = URLRequest(url: ckcURL)
request.httpMethod = "POST"
request.httpBody = spcData
let session = URLSession(configuration: URLSessionConfiguration.default)
let task = session.dataTask(with: request) { data, response, error in
if let data = data {
// The CKC is correctly returned and is now send to the `AVPlayer` instance so we
// can continue to play the stream.
dataRequest.respond(with: data)
loadingRequest.finishLoading()
} else {
print("๐Ÿ”‘", #function, "Unable to fetch the CKC.")
loadingRequest.finishLoading(with: NSError(domain: "com.icapps.error", code: -4, userInfo: nil))
}
}
task.resume()
return true
}
}
@linkmanishgupta
Copy link

What is specifically "contentId " here?
Can anybody please throw some light?

@yksingh12
Copy link

What is specifically "contentId " here?
Can anybody please throw some light?

Any luck here?

@lkuraer
Copy link

lkuraer commented Mar 15, 2021

@yksingh12 @linkmanishgupta for me it was URI that is provided in m3u8 manifest, for example:

#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://contentId",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1"

@rafaelreis-hotmart
Copy link

rafaelreis-hotmart commented Jun 2, 2021

What is specifically "contentId " here?

contentId or AssetId depends on your DRM supplier. I saw some implementations that uses some Identifier for the media. It could be the media code, for example. I saw some implementations using a key inside this URL skd://whatever. Found the Docs about how to integrate with your supplier.

@amitsingh12ap
Copy link

content id is host url "contentId = url.host"

@15kiortiz
Copy link

@yksingh12 @linkmanishgupta for me it was URI that is provided in m3u8 manifest, for example:

#EXT-X-KEY:METHOD=SAMPLE-AES,URI="skd://contentId",KEYFORMAT="com.apple.streamingkeydelivery",KEYFORMATVERSIONS="1"

where could I edit this? I want to be able to test the fairplay. Thank you very much

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment