2021-10-26 19:35:07 -06:00
|
|
|
import AVFoundation
|
2021-10-27 19:34:05 -06:00
|
|
|
import Promises
|
2021-10-26 19:35:07 -06:00
|
|
|
|
|
|
|
class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate {
|
|
|
|
|
|
|
|
private var _loadingRequest:AVAssetResourceLoadingRequest?
|
|
|
|
private var _requestingCertificate:Bool = false
|
|
|
|
private var _requestingCertificateErrored:Bool = false
|
|
|
|
private var _drm: DRMParams?
|
2021-10-27 19:34:05 -06:00
|
|
|
private var _localSourceEncryptionKeyScheme: String?
|
2021-10-26 19:35:07 -06:00
|
|
|
private var _reactTag: NSNumber?
|
|
|
|
private var _onVideoError: RCTDirectEventBlock?
|
|
|
|
private var _onGetLicense: RCTDirectEventBlock?
|
|
|
|
|
|
|
|
|
|
|
|
init(
|
|
|
|
asset: AVURLAsset,
|
|
|
|
drm: DRMParams?,
|
2021-10-27 19:34:05 -06:00
|
|
|
localSourceEncryptionKeyScheme: String?,
|
2021-10-26 19:35:07 -06:00
|
|
|
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
|
2021-10-27 19:34:05 -06:00
|
|
|
_localSourceEncryptionKeyScheme = localSourceEncryptionKeyScheme
|
2021-10-26 19:35:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
deinit {
|
|
|
|
_loadingRequest?.finishLoading()
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForRenewalOfRequestedResource renewalRequest:AVAssetResourceRenewalRequest) -> Bool {
|
|
|
|
return loadingRequestHandling(renewalRequest)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceLoader(_ resourceLoader:AVAssetResourceLoader, shouldWaitForLoadingOfRequestedResource loadingRequest:AVAssetResourceLoadingRequest) -> Bool {
|
|
|
|
return loadingRequestHandling(loadingRequest)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceLoader(_ resourceLoader:AVAssetResourceLoader, didCancel loadingRequest:AVAssetResourceLoadingRequest) {
|
|
|
|
NSLog("didCancelLoadingRequest")
|
|
|
|
}
|
2021-10-27 19:34:05 -06:00
|
|
|
|
2021-10-26 19:35:07 -06:00
|
|
|
func setLicenseResult(_ license:String!) {
|
2021-10-27 19:34:05 -06:00
|
|
|
guard let respondData = RCTVideoUtils.base64DataFromBase64String(base64String: license),
|
2021-10-26 19:35:07 -06:00
|
|
|
let _loadingRequest = _loadingRequest else {
|
|
|
|
setLicenseResultError("No data from JS license response")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
let dataRequest:AVAssetResourceLoadingDataRequest! = _loadingRequest.dataRequest
|
|
|
|
dataRequest.respond(with: respondData)
|
|
|
|
_loadingRequest.finishLoading()
|
|
|
|
}
|
|
|
|
|
|
|
|
func setLicenseResultError(_ error:String!) {
|
|
|
|
if _loadingRequest != nil {
|
|
|
|
self.finishLoadingWithError(error: RCTVideoErrorHandler.fromJSPart(error))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
func finishLoadingWithError(error:Error!) -> Bool {
|
2021-10-26 19:35:07 -06:00
|
|
|
if let _loadingRequest = _loadingRequest, let error = error {
|
2021-10-27 19:34:05 -06:00
|
|
|
_loadingRequest.finishLoading(with: error as! NSError)
|
2021-10-26 19:35:07 -06:00
|
|
|
|
|
|
|
_onVideoError?([
|
|
|
|
"error": [
|
2021-10-27 19:34:05 -06:00
|
|
|
"code": NSNumber(value: (error as NSError).code),
|
2021-10-26 19:35:07 -06:00
|
|
|
"localizedDescription": error.localizedDescription == nil ? "" : error.localizedDescription,
|
|
|
|
"localizedFailureReason": ((error as NSError).localizedFailureReason == nil ? "" : (error as NSError).localizedFailureReason) ?? "",
|
|
|
|
"localizedRecoverySuggestion": ((error as NSError).localizedRecoverySuggestion == nil ? "" : (error as NSError).localizedRecoverySuggestion) ?? "",
|
|
|
|
"domain": (error as NSError).domain
|
|
|
|
],
|
|
|
|
"target": _reactTag
|
|
|
|
])
|
|
|
|
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadingRequestHandling(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
|
2021-10-27 19:34:05 -06:00
|
|
|
if handleEmbeddedKey(loadingRequest) {
|
2021-10-26 19:35:07 -06:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
if _drm != nil {
|
|
|
|
return handleDrm(loadingRequest)
|
2021-10-26 19:35:07 -06:00
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
func handleEmbeddedKey(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
|
|
|
|
guard let url = loadingRequest.request.url,
|
|
|
|
let _localSourceEncryptionKeyScheme = _localSourceEncryptionKeyScheme,
|
|
|
|
let persistentKeyData = RCTVideoUtils.extractDataFromCustomSchemeUrl(from: url, scheme: _localSourceEncryptionKeyScheme)
|
|
|
|
else {
|
|
|
|
return false
|
2021-10-26 19:35:07 -06:00
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType
|
|
|
|
loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
|
|
|
|
loadingRequest.contentInformationRequest?.contentLength = Int64(persistentKeyData.count)
|
|
|
|
loadingRequest.dataRequest?.respond(with: persistentKeyData)
|
|
|
|
loadingRequest.finishLoading()
|
2021-10-26 19:35:07 -06:00
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
func handleDrm(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
|
|
|
|
if _requestingCertificate {
|
|
|
|
return true
|
|
|
|
} else if _requestingCertificateErrored {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
_loadingRequest = loadingRequest
|
2021-10-26 19:35:07 -06:00
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
guard let _drm = _drm, let drmType = _drm.type, drmType == "fairplay" else {
|
|
|
|
return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData)
|
2021-10-26 19:35:07 -06:00
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
var promise: Promise<Data>
|
|
|
|
if _onGetLicense != nil {
|
|
|
|
let contentId = _drm.contentId ?? loadingRequest.request.url?.host
|
|
|
|
promise = RCTVideoDRM.handleWithOnGetLicense(
|
|
|
|
loadingRequest:loadingRequest,
|
|
|
|
contentId:contentId,
|
|
|
|
certificateUrl:_drm.certificateUrl,
|
|
|
|
base64Certificate:_drm.base64Certificate
|
|
|
|
) .then{ spcData -> Void in
|
|
|
|
self._requestingCertificate = true
|
|
|
|
self._onGetLicense?(["licenseUrl": self._drm?.licenseServer ?? "",
|
|
|
|
"contentId": contentId,
|
|
|
|
"spcBase64": spcData.base64EncodedString(options: []),
|
|
|
|
"target": self._reactTag])
|
|
|
|
}
|
2021-10-26 19:35:07 -06:00
|
|
|
} else {
|
2021-10-27 19:34:05 -06:00
|
|
|
promise = RCTVideoDRM.handleInternalGetLicense(
|
|
|
|
loadingRequest:loadingRequest,
|
|
|
|
contentId:_drm.contentId,
|
|
|
|
licenseServer:_drm.licenseServer,
|
|
|
|
certificateUrl:_drm.certificateUrl,
|
|
|
|
base64Certificate:_drm.base64Certificate,
|
|
|
|
headers:_drm.headers
|
|
|
|
) .then{ data -> Void in
|
|
|
|
guard let dataRequest = loadingRequest.dataRequest else {
|
|
|
|
throw RCTVideoErrorHandler.noCertificateData
|
|
|
|
}
|
|
|
|
dataRequest.respond(with:data)
|
|
|
|
loadingRequest.finishLoading()
|
|
|
|
}
|
2021-10-26 19:35:07 -06:00
|
|
|
}
|
|
|
|
|
2021-10-27 19:34:05 -06:00
|
|
|
|
|
|
|
promise.catch{ error in
|
|
|
|
self.finishLoadingWithError(error:error)
|
|
|
|
self._requestingCertificateErrored = true
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
2021-10-26 19:35:07 -06:00
|
|
|
}
|
|
|
|
}
|