168 lines
6.6 KiB
Swift
168 lines
6.6 KiB
Swift
|
import AVFoundation
|
||
|
import Promises
|
||
|
|
||
|
class RCTResourceLoaderDelegate: NSObject, AVAssetResourceLoaderDelegate, URLSessionDelegate {
|
||
|
|
||
|
private var _loadingRequest:AVAssetResourceLoadingRequest?
|
||
|
private var _requestingCertificate:Bool = false
|
||
|
private var _requestingCertificateErrored:Bool = false
|
||
|
private var _drm: DRMParams?
|
||
|
private var _localSourceEncryptionKeyScheme: String?
|
||
|
private var _reactTag: NSNumber?
|
||
|
private var _onVideoError: RCTDirectEventBlock?
|
||
|
private var _onGetLicense: RCTDirectEventBlock?
|
||
|
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
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")
|
||
|
}
|
||
|
|
||
|
func setLicenseResult(_ license:String!) {
|
||
|
guard let respondData = RCTVideoUtils.base64DataFromBase64String(base64String: license),
|
||
|
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))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func finishLoadingWithError(error:Error!) -> Bool {
|
||
|
if let _loadingRequest = _loadingRequest, let error = error {
|
||
|
_loadingRequest.finishLoading(with: error as! NSError)
|
||
|
|
||
|
_onVideoError?([
|
||
|
"error": [
|
||
|
"code": NSNumber(value: (error as NSError).code),
|
||
|
"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 {
|
||
|
if handleEmbeddedKey(loadingRequest) {
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
if _drm != nil {
|
||
|
return handleDrm(loadingRequest)
|
||
|
}
|
||
|
|
||
|
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
|
||
|
}
|
||
|
|
||
|
loadingRequest.contentInformationRequest?.contentType = AVStreamingKeyDeliveryPersistentContentKeyType
|
||
|
loadingRequest.contentInformationRequest?.isByteRangeAccessSupported = true
|
||
|
loadingRequest.contentInformationRequest?.contentLength = Int64(persistentKeyData.count)
|
||
|
loadingRequest.dataRequest?.respond(with: persistentKeyData)
|
||
|
loadingRequest.finishLoading()
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
|
||
|
func handleDrm(_ loadingRequest:AVAssetResourceLoadingRequest!) -> Bool {
|
||
|
if _requestingCertificate {
|
||
|
return true
|
||
|
} else if _requestingCertificateErrored {
|
||
|
return false
|
||
|
}
|
||
|
_loadingRequest = loadingRequest
|
||
|
|
||
|
guard let _drm = _drm, let drmType = _drm.type, drmType == "fairplay" else {
|
||
|
return finishLoadingWithError(error: RCTVideoErrorHandler.noDRMData)
|
||
|
}
|
||
|
|
||
|
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])
|
||
|
}
|
||
|
} else {
|
||
|
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()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
promise.catch{ error in
|
||
|
self.finishLoadingWithError(error:error)
|
||
|
self._requestingCertificateErrored = true
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|
||
|
}
|