Converts iOS implementation from Objective-c to Swift # During the conversion process some updates to the code structure were also made - Modularize codebase from single file to smaller focused files - Untangled large nested IF statements - Added more null checks, since Swift is more strict with null pointers - Added property to allow for decoding of local video sources with self contained key for offline playback - Updates example apps to test react-native 0.63.4 and uses auto native dependency imports for android and ios
		
			
				
	
	
		
			169 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
| import AVFoundation
 | |
| import Promises
 | |
| 
 | |
| struct RCTVideoDRM {
 | |
|     @available(*, unavailable) private init() {}
 | |
| 
 | |
|     static func fetchLicense(
 | |
|         licenseServer: String,
 | |
|         spcData: Data?,
 | |
|         contentId: String,
 | |
|         headers: [String:Any]?
 | |
|     ) -> Promise<Data> {
 | |
|         let request = createLicenseRequest(licenseServer:licenseServer, spcData:spcData, contentId:contentId, headers:headers)
 | |
|         
 | |
|         return Promise<Data>(on: .global()) { fulfill, reject in
 | |
|             let postDataTask = URLSession.shared.dataTask(with: request as URLRequest, completionHandler:{ (data:Data!,response:URLResponse!,error:Error!) in
 | |
|                 
 | |
|                 let httpResponse:HTTPURLResponse! = (response as! HTTPURLResponse)
 | |
| 
 | |
|                 guard error == nil else {
 | |
|                     print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)")
 | |
|                     reject(error)
 | |
|                     return
 | |
|                 }
 | |
|                 guard httpResponse.statusCode == 200 else {
 | |
|                     print("Error getting license from \(licenseServer), HTTP status code \(httpResponse.statusCode)")
 | |
|                     reject(RCTVideoErrorHandler.licenseRequestNotOk(httpResponse.statusCode))
 | |
|                     return
 | |
|                 }
 | |
|                 
 | |
|                 guard data != nil, let decodedData = Data(base64Encoded: data, options: []) else {
 | |
|                     reject(RCTVideoErrorHandler.noDataFromLicenseRequest)
 | |
|                     return
 | |
|                 }
 | |
|                 
 | |
|                 fulfill(decodedData)
 | |
|             })
 | |
|             postDataTask.resume()
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     static func createLicenseRequest(
 | |
|         licenseServer: String,
 | |
|         spcData: Data?,
 | |
|         contentId: String,
 | |
|         headers: [String:Any]?
 | |
|     ) -> URLRequest {
 | |
|         var request = URLRequest(url: URL(string: licenseServer)!)
 | |
|         request.httpMethod = "POST"
 | |
|         
 | |
|         if let headers = headers {
 | |
|             for item in headers {
 | |
|                 guard let key = item.key as? String, let value = item.value as? String else {
 | |
|                     continue
 | |
|                 }
 | |
|                 request.setValue(value, forHTTPHeaderField: key)
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         let spcEncoded = spcData?.base64EncodedString(options: [])
 | |
|         let spcUrlEncoded = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, spcEncoded as? CFString? as! CFString, nil, "?=&+" as CFString, CFStringBuiltInEncodings.UTF8.rawValue) as? String
 | |
|         let post = String(format:"spc=%@&%@", spcUrlEncoded as! CVarArg, contentId)
 | |
|         let postData = post.data(using: String.Encoding.utf8, allowLossyConversion:true)
 | |
|         request.httpBody = postData
 | |
|         
 | |
|         return request
 | |
|     }
 | |
|     
 | |
|     static func fetchSpcData(
 | |
|         loadingRequest: AVAssetResourceLoadingRequest,
 | |
|         certificateData: Data,
 | |
|         contentIdData: Data
 | |
|     ) -> Promise<Data> {
 | |
|         return Promise<Data>(on: .global()) { fulfill, reject in
 | |
|             var spcError:NSError!
 | |
|             var spcData: Data?
 | |
|             do {
 | |
|                 spcData = try loadingRequest.streamingContentKeyRequestData(forApp: certificateData, contentIdentifier: contentIdData as Data, options: nil)
 | |
|             } catch _ {
 | |
|                 print("SPC error")
 | |
|             }
 | |
|             
 | |
|             if spcError != nil {
 | |
|                 reject(spcError)
 | |
|             }
 | |
|             
 | |
|             guard let spcData = spcData else {
 | |
|                 reject(RCTVideoErrorHandler.noSPC)
 | |
|                 return
 | |
|             }
 | |
|             
 | |
|             fulfill(spcData)
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     static func createCertificateData(certificateStringUrl:String?, base64Certificate:Bool?) -> Promise<Data> {
 | |
|         return Promise<Data>(on: .global()) { fulfill, reject in
 | |
| 
 | |
|             guard let certificateStringUrl = certificateStringUrl,
 | |
|                   let certificateURL = URL(string: certificateStringUrl.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed) ?? "") else {
 | |
|                       reject(RCTVideoErrorHandler.noCertificateURL)
 | |
|                 return
 | |
|             }
 | |
| 
 | |
|             var certificateData:Data?
 | |
|             do {
 | |
|                certificateData = try Data(contentsOf: certificateURL)
 | |
|                 if (base64Certificate != nil) {
 | |
|                     certificateData = Data(base64Encoded: certificateData! as Data, options: .ignoreUnknownCharacters)
 | |
|                 }
 | |
|             } catch {}
 | |
|             
 | |
|             guard let certificateData = certificateData else {
 | |
|                 reject(RCTVideoErrorHandler.noCertificateData)
 | |
|                 return
 | |
|             }
 | |
|             
 | |
|             fulfill(certificateData)
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     static func handleWithOnGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, certificateUrl:String?, base64Certificate:Bool?) -> Promise<Data> {
 | |
|         let contentIdData = contentId?.data(using: .utf8)
 | |
|         
 | |
|         return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate)
 | |
|             .then{ certificateData -> Promise<Data> in
 | |
|                 guard let contentIdData = contentIdData else {
 | |
|                     throw RCTVideoError.invalidContentId as! Error
 | |
|                 }
 | |
|                 
 | |
|                 return RCTVideoDRM.fetchSpcData(
 | |
|                     loadingRequest:loadingRequest,
 | |
|                     certificateData:certificateData,
 | |
|                     contentIdData:contentIdData
 | |
|                 )
 | |
|             }
 | |
|     }
 | |
|     
 | |
|     static func handleInternalGetLicense(loadingRequest: AVAssetResourceLoadingRequest, contentId:String?, licenseServer:String?, certificateUrl:String?, base64Certificate:Bool?, headers: [String:Any]?) -> Promise<Data> {
 | |
|         let url = loadingRequest.request.url
 | |
|         
 | |
|         guard let contentId = contentId ?? url?.absoluteString.replacingOccurrences(of: "skd://", with:"") else {
 | |
|             return Promise(RCTVideoError.invalidContentId as! Error)
 | |
|         }
 | |
|         
 | |
|         let contentIdData = NSData(bytes: contentId.cString(using: String.Encoding.utf8), length:contentId.lengthOfBytes(using: String.Encoding.utf8)) as Data
 | |
|         
 | |
|         return RCTVideoDRM.createCertificateData(certificateStringUrl:certificateUrl, base64Certificate:base64Certificate)
 | |
|             .then{ certificateData in
 | |
|                 return RCTVideoDRM.fetchSpcData(
 | |
|                     loadingRequest:loadingRequest,
 | |
|                     certificateData:certificateData,
 | |
|                     contentIdData:contentIdData
 | |
|                 )
 | |
|             }
 | |
|             .then{ spcData -> Promise<Data> in
 | |
|                 guard let licenseServer = licenseServer else {
 | |
|                     throw RCTVideoError.noLicenseServerURL as! Error
 | |
|                 }
 | |
|                 return RCTVideoDRM.fetchLicense(
 | |
|                     licenseServer: licenseServer,
 | |
|                     spcData: spcData,
 | |
|                     contentId: contentId,
 | |
|                     headers: headers
 | |
|                 )
 | |
|             }
 | |
|     }
 | |
| }
 |