My project is a TV player app for HLS streams with fairplay encryption. It is made on swiftUI for iPhone and iPad, it is in production.
I have enabled the target "Mac (Designed for iPad)" in the project settings, and It is working perfectly on Mac M1 chips when running the app from the Mac AppStore. The Mac version has never been main main focus, but it is nice to have it working so easily.
However when I run the app from Xcode, by selecting "My Mac (Designed for iPad)", everytime AVPlayer wants to start playback I am ejected from the app and the only thing I get from the logcat is:
Message from debugger: Terminated due to signal 9
Why? And Why does it work when running the app published on the appstore?
I was able to debug a bit and identify which line of code triggers the issue but I am still stuck:
I am using an AVAssetResourceLoaderDelegate
to load the Fairplay Keys instead of the default one (because I need some authentication parameters in the HTTP headers to communicate with the DRM Proxy).
So, in the process I am able to request SPC data and CKC (I have verified the data), and then when the loadingRequest.finishLoading()
is called.. BOOM the app is terminated and it triggers the log Message from debugger: Terminated due to signal 9
.
I am sharing the delegate method from the AVAssetResourceLoaderDelegate
where it happens. This has been written a while ago and is running fine on all devices. If you are not used to this delegate, it is used by AVPlayer
whenever a new mediaItem is set with the method: AVPlayer.replaceCurrentItem(with: mediaItem)
func resourceLoader(_ resourceLoader: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
guard let dataRequest = loadingRequest.dataRequest else { return false }
getFairplaycertificate { data, _ in
// Request Server Playback Context (SPC) data
guard
let certificate = data,
let contentIdData = (loadingRequest.request.url?.host ?? "").data(using: String.Encoding.utf8),
let spcData = try? loadingRequest.streamingContentKeyRequestData(
forApp: certificate,
contentIdentifier: contentIdData,
options: [AVContentKeyRequestProtocolVersionsKey: [1]]
) else {
loadingRequest.finishLoading(with: NSError(domain: "tvplayer", code: -1, userInfo: nil))
print("⚠️", #function, "Unable to get SPC data.")
return false
}
// Now the CKC can be requested
let networkId = loadingRequest.request.url?.host ?? ""
self.requestCKC(spcData: spcData, contentId: networkId) { ckc, error in
if error == nil && ckc != nil {
// The CKC is correctly returned and is sent to AVPlayer. Stream is decrypted
dataRequest.respond(with: ckc!)
loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryContentKeyType
loadingRequest.finishLoading() // <--- THIS LINE IS GUILTY!!!
} else {
print("⚠️", #function, "Unable to get CKC.")
loadingRequest.finishLoading(with: error)
}
}
return true
}
return true
}
If I comment loadingRequest.finishLoading()
or if I replace it by: loadingRequest.finishLoading(with: error)
, the app is not terminated but my decryption keys are not loaded. That's the only clue I have so far.
Any help would be appreciated.
- What should I look for?
- Is there a way to get a detailed error stacktrace?
Thanks.