-
-
Save fousa/5709fb7c84e5b53dbdae508c9cb4fadc to your computer and use it in GitHub Desktop.
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 | |
} | |
} |
I found apple has changed implementation in their sample project by using AVContentKey (*iOS 10.3.3) before AVAssetResourceLoaderDelegate was being used. But I would stick with AVAssetResourceLoader implementation.
I have been trying to implement the FairPlay streaming DRM in my project. I'm struck in getting the Content from the CKC data. I'm getting the below errors.
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could not be completed" UserInfo={NSLocalizedFailureReason=An unknown error occurred (-16152), NSLocalizedDescription=The operation could not be completed, NSUnderlyingError=0x10bc54770 {Error Domain=NSOSStatusErrorDomain Code=-16152 "(null)"}}
Error Domain=AVFoundationErrorDomain Code=-11835 "Cannot Open" UserInfo={NSUnderlyingError=0x17025c860 {Error Domain=NSOSStatusErrorDomain Code=-42681 "(null)"}, NSLocalizedFailureReason=This content is not authorized., NSLocalizedDescription=Cannot Open}
How to validate the implementation step by step?
Thanks
Same here. It works on ios11 but fails on ios10. any solution?
can u provide any demo url asset so that i can pass it as a argument to the FairPlayer.play() method
So setting delegate using asset.resourceLoader.setDelegate(self, queue: queue)
automatically invokes the resourceLoader method? I am new to swift and I am still trying to wrap my head around how that works.
Have you had any luck doing something like this with AirPlay?
It's seems that this demo is not working now. I found out, that Apple's demo is better showing how to make this works:
https://developer.apple.com/services-account/download?path=/Developer_Tools/FairPlay_Streaming_Server_SDK/FairPlay_Streaming_Server_SDK_v4.2.0.zip
For me it was an issue with line #49, where streamingContentKeyRequestData appeared. I dont know exactly, but it seems that calling this function took too many time, so task fails before it could resolve.
In apple's demo they wrap all this async stuff in async block on separate queue, and it works well for me. I wrapped everything inside resourceLoader(...) in async block except return true
and now it works
after implementing the above methods, I got data with empty size then I called finishedLoading() method by implementing dataRequest.respond(with: data) and got below error from AVPlayer.
Error Domain=AVFoundationErrorDomain Code=-11835 "Cannot Open" UserInfo={NSLocalizedFailureReason=This content is not authorized., NSLocalizedDescription=Cannot Open, NSUnderlyingError=0x106f2afa0 {Error Domain=NSOSStatusErrorDomain Code=-42681 "(null)"} from the AVPlayer.
I am confuse on how to get contentId, which is used to create content Identifier. Can anybody help me?
@mangaloreglitz Have you got the solution?
What is specifically "contentId " here?
Can anybody please throw some light?
What is specifically "contentId " here?
Can anybody please throw some light?
Any luck here?
@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"
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.
content id is host url "contentId = url.host"
@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
Thanks :)