2022-05-19 07:29:25 -06:00
|
|
|
import AVFoundation
|
|
|
|
|
|
|
|
class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate {
|
2023-10-05 13:37:28 -06:00
|
|
|
private var _loadingRequests: [String: AVAssetResourceLoadingRequest?] = [:]
|
2023-12-07 00:47:40 -07:00
|
|
|
private var _requestingCertificate = false
|
|
|
|
private var _requestingCertificateErrored = false
|
2022-05-19 07:29:25 -06:00
|
|
|
private var _drm: DRMParams?
|
|
|
|
private var _localSourceEncryptionKeyScheme: String?
|
|
|
|
private var _reactTag: NSNumber?
|
|
|
|
private var _onVideoError: RCTDirectEventBlock?
|
|
|
|
private var _onGetLicense: RCTDirectEventBlock?
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
init(
|
|
|
|
asset: AVURLAsset,
|
|
|
|
drm: DRMParams?,
|
|
|
|
localSourceEncryptionKeyScheme: String?,
|
|
|
|
onVideoError: RCTDirectEventBlock?,
|
|
|
|
onGetLicense: RCTDirectEventBlock?,
|
|
|
|
reactTag: NSNumber
|
|
|
|
) {
|
|
|
|
super.init()
|
|
|
|
let queue = DispatchQueue(label: "assetQueue")
|
|
|
|
asset.resourceLoader.setDelegate(self, queue: queue)
|
|
|
|
_reactTag = reactTag
|
|
|
|
_onVideoError = onVideoError
|
|
|
|
_onGetLicense = onGetLicense
|
|
|
|
_drm = drm
|
|
|
|
_localSourceEncryptionKeyScheme = localSourceEncryptionKeyScheme
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
deinit {
|
2023-10-05 13:37:28 -06:00
|
|
|
for request in _loadingRequests.values {
|
|
|
|
request?.finishLoading()
|
|
|
|
}
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func resourceLoader(_: AVAssetResourceLoader, shouldWaitForRenewalOfRequestedResource renewalRequest: AVAssetResourceRenewalRequest) -> Bool {
|
2022-05-19 07:29:25 -06:00
|
|
|
return loadingRequestHandling(renewalRequest)
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func resourceLoader(_: AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest: AVAssetResourceLoadingRequest) -> Bool {
|
2022-05-19 07:29:25 -06:00
|
|
|
return loadingRequestHandling(loadingRequest)
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func resourceLoader(_: AVAssetResourceLoader, didCancel _: AVAssetResourceLoadingRequest) {
|
2023-01-28 06:29:00 -07:00
|
|
|
RCTLog("didCancelLoadingRequest")
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
|
|
|
|
2023-12-07 00:47:40 -07:00
|
|
|
func setLicenseResult(_ license: String!, _ licenseUrl: String!) {
|
2023-10-05 13:37:28 -06:00
|
|
|
// Check if the loading request exists in _loadingRequests based on licenseUrl
|
|
|
|
guard let loadingRequest = _loadingRequests[licenseUrl] else {
|
2024-09-13 02:49:43 -06:00
|
|
|
setLicenseResultError("Loading request for licenseUrl \(String(describing: licenseUrl)) not found", licenseUrl)
|
2023-10-05 13:37:28 -06:00
|
|
|
return
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2023-10-05 13:37:28 -06:00
|
|
|
// Check if the license data is valid
|
|
|
|
guard let respondData = RCTVideoUtils.base64DataFromBase64String(base64String: license) else {
|
|
|
|
setLicenseResultError("No data from JS license response", licenseUrl)
|
|
|
|
return
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2023-10-05 13:37:28 -06:00
|
|
|
let dataRequest: AVAssetResourceLoadingDataRequest! = loadingRequest?.dataRequest
|
2022-05-19 07:29:25 -06:00
|
|
|
dataRequest.respond(with: respondData)
|
2023-10-05 13:37:28 -06:00
|
|
|
loadingRequest!.finishLoading()
|
|
|
|
_loadingRequests.removeValue(forKey: licenseUrl)
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func setLicenseResultError(_ error: String!, _ licenseUrl: String!) {
|
2023-10-05 13:37:28 -06:00
|
|
|
// Check if the loading request exists in _loadingRequests based on licenseUrl
|
|
|
|
guard let loadingRequest = _loadingRequests[licenseUrl] else {
|
|
|
|
print("Loading request for licenseUrl \(licenseUrl) not found. Error: \(error)")
|
|
|
|
return
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-10-05 13:37:28 -06:00
|
|
|
|
|
|
|
self.finishLoadingWithError(error: RCTVideoErrorHandler.fromJSPart(error), licenseUrl: licenseUrl)
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2023-10-05 13:37:28 -06:00
|
|
|
func finishLoadingWithError(error: Error!, licenseUrl: String!) -> Bool {
|
|
|
|
// Check if the loading request exists in _loadingRequests based on licenseUrl
|
|
|
|
guard let loadingRequest = _loadingRequests[licenseUrl], let error = error as NSError? else {
|
|
|
|
// Handle the case where the loading request is not found or error is nil
|
|
|
|
return false
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-10-05 13:37:28 -06:00
|
|
|
|
|
|
|
loadingRequest!.finishLoading(with: error)
|
|
|
|
_loadingRequests.removeValue(forKey: licenseUrl)
|
|
|
|
_onVideoError?([
|
|
|
|
"error": [
|
|
|
|
"code": NSNumber(value: error.code),
|
2024-09-13 02:49:43 -06:00
|
|
|
"localizedDescription": error.localizedDescription,
|
2023-10-05 13:37:28 -06:00
|
|
|
"localizedFailureReason": error.localizedFailureReason ?? "",
|
|
|
|
"localizedRecoverySuggestion": error.localizedRecoverySuggestion ?? "",
|
2023-12-07 00:47:40 -07:00
|
|
|
"domain": error.domain,
|
2023-10-05 13:37:28 -06:00
|
|
|
],
|
2024-09-13 02:49:43 -06:00
|
|
|
"target": _reactTag as Any,
|
2023-10-05 13:37:28 -06:00
|
|
|
])
|
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
return false
|
|
|
|
}
|
2023-10-05 13:37:28 -06:00
|
|
|
|
2023-12-07 00:47:40 -07:00
|
|
|
func loadingRequestHandling(_ loadingRequest: AVAssetResourceLoadingRequest!) -> Bool {
|
2022-05-19 07:29:25 -06:00
|
|
|
if handleEmbeddedKey(loadingRequest) {
|
|
|
|
return true
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
if _drm != nil {
|
|
|
|
return handleDrm(loadingRequest)
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
return false
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func handleEmbeddedKey(_ loadingRequest: AVAssetResourceLoadingRequest!) -> Bool {
|
2022-05-19 07:29:25 -06:00
|
|
|
guard let url = loadingRequest.request.url,
|
2024-01-04 12:16:23 -07:00
|
|
|
let _localSourceEncryptionKeyScheme,
|
2022-05-19 07:29:25 -06:00
|
|
|
let persistentKeyData = RCTVideoUtils.extractDataFromCustomSchemeUrl(from: url, scheme: _localSourceEncryptionKeyScheme)
|
|
|
|
else {
|
|
|
|
return false
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType
|
|
|
|
loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
|
|
|
|
loadingRequest.contentInformationRequest?.contentLength = Int64(persistentKeyData.count)
|
|
|
|
loadingRequest.dataRequest?.respond(with: persistentKeyData)
|
|
|
|
loadingRequest.finishLoading()
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
return true
|
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
|
|
|
func handleDrm(_ loadingRequest: AVAssetResourceLoadingRequest!) -> Bool {
|
2022-05-19 07:29:25 -06:00
|
|
|
if _requestingCertificate {
|
|
|
|
return true
|
|
|
|
} else if _requestingCertificateErrored {
|
|
|
|
return false
|
|
|
|
}
|
2023-10-05 13:37:28 -06:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
let requestKey: String = loadingRequest.request.url?.absoluteString ?? ""
|
2023-10-05 13:37:28 -06:00
|
|
|
|
|
|
|
_loadingRequests[requestKey] = loadingRequest
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-01-04 12:16:23 -07:00
|
|
|
guard let _drm, let drmType = _drm.type, drmType == "fairplay" else {
|
2023-10-05 13:37:28 -06:00
|
|
|
return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData, licenseUrl: requestKey)
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2024-04-04 05:23:44 -06:00
|
|
|
Task {
|
|
|
|
do {
|
|
|
|
if _onGetLicense != nil {
|
|
|
|
let contentId = _drm.contentId ?? loadingRequest.request.url?.host
|
|
|
|
let spcData = try await RCTVideoDRM.handleWithOnGetLicense(
|
|
|
|
loadingRequest: loadingRequest,
|
|
|
|
contentId: contentId,
|
|
|
|
certificateUrl: _drm.certificateUrl,
|
|
|
|
base64Certificate: _drm.base64Certificate
|
|
|
|
)
|
|
|
|
|
|
|
|
self._requestingCertificate = true
|
|
|
|
self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? "",
|
|
|
|
"loadedLicenseUrl": loadingRequest.request.url?.absoluteString ?? "",
|
|
|
|
"contentId": contentId ?? "",
|
|
|
|
"spcBase64": spcData.base64EncodedString(options: []),
|
2024-09-13 02:49:43 -06:00
|
|
|
"target": self._reactTag as Any])
|
2024-04-04 05:23:44 -06:00
|
|
|
} else {
|
|
|
|
let data = try await RCTVideoDRM.handleInternalGetLicense(
|
|
|
|
loadingRequest: loadingRequest,
|
|
|
|
contentId: _drm.contentId,
|
|
|
|
licenseServer: _drm.licenseServer,
|
|
|
|
certificateUrl: _drm.certificateUrl,
|
|
|
|
base64Certificate: _drm.base64Certificate,
|
|
|
|
headers: _drm.headers
|
|
|
|
)
|
|
|
|
|
|
|
|
guard let dataRequest = loadingRequest.dataRequest else {
|
|
|
|
throw RCTVideoErrorHandler.noCertificateData
|
|
|
|
}
|
|
|
|
dataRequest.respond(with: data)
|
|
|
|
loadingRequest.finishLoading()
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2024-04-04 05:23:44 -06:00
|
|
|
} catch {
|
|
|
|
self.finishLoadingWithError(error: error, licenseUrl: requestKey)
|
|
|
|
self._requestingCertificateErrored = true
|
2023-12-07 00:47:40 -07:00
|
|
|
}
|
2022-05-19 07:29:25 -06:00
|
|
|
}
|
2023-12-07 00:47:40 -07:00
|
|
|
|
2022-05-19 07:29:25 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|