private void HandleStreamingContentKeyRequest(AVContentKeyRequest keyRequest) { var contentKeyIdentifierString = keyRequest.Identifier.ToString(); if (string.IsNullOrEmpty(contentKeyIdentifierString)) { Debug.WriteLine("Failed to retrieve the assetID from the keyRequest!"); return; } var contentKeyIdentifierUrl = new NSUrl(contentKeyIdentifierString); var assetIdString = contentKeyIdentifierUrl.Host; var assetIdData = NSData.FromString(assetIdString, NSStringEncoding.UTF8); Action provideOnlineKey = () => { var applicationCertificate = RequestApplicationCertificate(); try { var keys = new[] { new NSString(AVContentKeyRequest.ProtocolVersions) }; var numbers = new NSMutableArray <NSNumber>(); numbers.Add(new NSNumber(1)); var objects = new NSObject[] { numbers }; var options = new NSDictionary <NSString, NSObject>(keys, objects); keyRequest.MakeStreamingContentKeyRequestData(applicationCertificate, assetIdData, options, async(data, error) => { var ckcData = await this.RequestContentKeyFromKeySecurityModule(data, assetIdString); if (ckcData == null) { return; } var keyResponse = AVContentKeyResponse.Create(ckcData); keyRequest.Process(keyResponse); }); } catch (Exception ex) { Debug.WriteLine($"Failed to make streaming content key request data: {ex.Message}"); } }; /* * When you receive an AVContentKeyRequest via -contentKeySession:didProvideContentKeyRequest: * and you want the resulting key response to produce a key that can persist across multiple * playback sessions, you must invoke -respondByRequestingPersistableContentKeyRequest on that * AVContentKeyRequest in order to signal that you want to process an AVPersistableContentKeyRequest * instead. If the underlying protocol supports persistable content keys, in response your * delegate will receive an AVPersistableContentKeyRequest via -contentKeySession:didProvidePersistableContentKeyRequest:. */ if (ShouldRequestPersistableContentKey(assetIdString) || PersistableContentKeyExistsOnDisk(assetIdString)) { try { NSError error; keyRequest.RespondByRequestingPersistableContentKeyRequest(out error); if (error != null) { throw new Exception($"Error requesting persistable content key: {error.ToString()}"); } } catch (Exception ex) { Debug.WriteLine(ex.Message); /* * This case will occur when the client gets a key loading request from an AirPlay Session. * You should answer the key request using an online key from your key server. */ provideOnlineKey(); } return; } try { provideOnlineKey(); } catch (Exception ex) { Debug.WriteLine(ex.Message); } }
private void HandlePersistableContentKeyRequest(AVPersistableContentKeyRequest keyRequest) { /* * The key ID is the URI from the EXT-X-KEY tag in the playlist (e.g. "skd://key65") and the * asset ID in this case is "key65". */ var contentKeyIdentifierString = keyRequest.Identifier as NSString; if (contentKeyIdentifierString == null) { Debug.WriteLine("Failed to retrieve the assetID from the keyRequest!"); return; } var contentKeyIdentifierUrl = new NSUrl(contentKeyIdentifierString); var assetIdString = contentKeyIdentifierUrl.Host; var assetIdData = NSData.FromString(assetIdString, NSStringEncoding.UTF8); Action <NSData, NSError> completionHandler = async(data, error) => { if (error != null) { keyRequest.Process(error); pendingPersistableContentKeyIdentifiers.Remove(assetIdString); return; } if (data == null) { return; } try { var ckcData = await RequestContentKeyFromKeySecurityModule(data, assetIdString); NSData persistentKey = keyRequest.GetPersistableContentKey(ckcData, null, out error); WritePersistableContentKey(persistentKey, new NSString(assetIdString)); /* * AVContentKeyResponse is used to represent the data returned from the key server when requesting a key for * decrypting content. */ var keyResponse = AVContentKeyResponse.Create(persistentKey); /* * Provide the content key response to make protected content available for processing. */ keyRequest.Process(keyResponse); string assetName = string.Empty; bool assetRemoved = false; if (contentKeyToStreamNameMap.TryGetValue(assetIdString, out assetName)) { assetRemoved = contentKeyToStreamNameMap.Remove(assetIdString); } if (!string.IsNullOrWhiteSpace(assetName) && assetRemoved && !contentKeyToStreamNameMap.ContainsKey(assetIdString)) { var userInfo = new Dictionary <string, object>(); userInfo["name"] = assetName; var userInfoDictionary = NSDictionary.FromObjectsAndKeys(userInfo.Values.ToArray(), userInfo.Keys.ToArray()); NSNotificationCenter.DefaultCenter.PostNotificationName(ContentKeyDelegate.DidSaveAllPersistableContentKey, null, userInfoDictionary); } pendingPersistableContentKeyIdentifiers.Remove(assetIdString); } catch (Exception ex) { pendingPersistableContentKeyIdentifiers.Remove(assetIdString); Debug.WriteLine(ex.Message); } }; try { var applicationCertificate = RequestApplicationCertificate(); var keys = new[] { new NSString(AVContentKeyRequest.ProtocolVersions) }; var numbers = new NSMutableArray <NSNumber>(); numbers.Add(new NSNumber(1)); var objects = new NSObject[] { numbers }; var options = new NSDictionary <NSString, NSObject>(keys, objects); if (PersistableContentKeyExistsOnDisk(assetIdString)) { var urlToPersistableKey = CreateUrlForPersistableContentKey(assetIdString); var contentKey = NSFileManager.DefaultManager.Contents(urlToPersistableKey.Path); if (contentKey == null) { pendingPersistableContentKeyIdentifiers.Remove(assetIdString); /* * Key requests should never be left dangling. * Attempt to create a new persistable key. */ keyRequest.MakeStreamingContentKeyRequestData(applicationCertificate, assetIdData, options, completionHandler); return; } /* * Create an AVContentKeyResponse from the persistent key data to use for requesting a key for * decrypting content. */ var keyResponse = AVContentKeyResponse.Create(contentKey); keyRequest.Process(keyResponse); return; } keyRequest.MakeStreamingContentKeyRequestData(applicationCertificate, assetIdData, options, completionHandler); } catch (Exception ex) { Debug.WriteLine($"Failure responding to an AVPersistableContentKeyRequest when attemping to determine if key is already available for use on disk. {ex.Message}"); } }