{"id":25939330,"url":"https://github.com/axinom/drm-sample-player-ios","last_synced_at":"2025-07-26T20:05:40.631Z","repository":{"id":152972222,"uuid":"319974262","full_name":"Axinom/drm-sample-player-ios","owner":"Axinom","description":null,"archived":false,"fork":false,"pushed_at":"2023-05-10T10:17:23.000Z","size":781,"stargazers_count":32,"open_issues_count":0,"forks_count":11,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-18T02:06:19.680Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Axinom.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-12-09T14:02:40.000Z","updated_at":"2025-07-10T05:05:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"eea8cfca-149c-41ae-889e-d542490fe61f","html_url":"https://github.com/Axinom/drm-sample-player-ios","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Axinom/drm-sample-player-ios","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-sample-player-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-sample-player-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-sample-player-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-sample-player-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Axinom","download_url":"https://codeload.github.com/Axinom/drm-sample-player-ios/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Axinom%2Fdrm-sample-player-ios/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267223631,"owners_count":24055715,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-26T02:00:08.937Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-03-04T04:16:40.465Z","updated_at":"2025-07-26T20:05:40.618Z","avatar_url":"https://github.com/Axinom.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Axinom DRM Sample Player\r\n \r\nThe purpose of this sample application is to provide a reference code that can help using Axinom DRM with AVFoundation framework to play FairPlay protected HTTP Live Streams (HLS) hosted on remote servers as well as giving an example of persisting FairPlay protected and non-protected HLS streams on disk for offline playback.\r\n \r\nYou can use the example code provided in this sample to build your own application that integrates the Axinom DRM with AVFoundation.\r\n \r\nAnother major usage of this sample raises from its capability of tracing the steps performed during the playback of protected and non-protected assets such as FairPlay content protection related activity, DRM license acquisition from Axinom DRM licensing server, as well as ```AVPlayerItem``` and ```AVPlayer``` statuses, buffer events, and Access log and Error log events associated with ```AVPlayerItem```.\r\n \r\nSample application's Player View has a togglable Console overlay, that allows user to observe verbose logging of these steps. Console output can be cleared and copied to the device clipboard. Player buttons behind the Console overlay can be clicked only when the Console overlay is hidden.\r\n \r\n## Using the Sample application\r\n \r\nBuild and run the sample on an actual device running iOS 13.1 or later using Xcode.  The APIs demonstrated in this sample do not work on the iOS Simulator.\r\n \r\nThis sample provides a list of HLS Streams that you can playback by tapping on the UITableViewCell corresponding to the stream.  If you wish to cancel an already running `AVAggregateAssetDownloadTask` or delete an already downloaded HLS stream from disk, you can accomplish this by tapping on the accessory button on the `UITableViewCell` corresponding to the stream you wish to manage.\r\nIf you wish to download an HLS stream initiating an `AVAggregateAssetDownloadTask`, you can accomplish this by tapping the multifunction button (Save/Delete/Cancel) button on Player View Controller. Canceling and deleting downloaded stream actions are can also be performed on Player View Controller by tapping the multifunction button (Save/Delete/Cancel).\r\n \r\nWhen the sample creates and initializes an `AVAggregateAssetDownloadTask` for the download of an HLS stream, only the default selections for each of the media selection groups will be used (these are indicated in the HLS playlist `EXT-X-MEDIA` tags by a DEFAULT attribute of YES).\r\n \r\n### Adding Streams to the Sample\r\n \r\nIf you wish to add your own HLS streams to test with using this sample, you can do this by adding an entry into the Streams.json that is part of the Xcode Project.  There are two important keys you need to provide values for:\r\n \r\n__title__: What the display name of the HLS stream should be in the sample, also used as a file name for the downloaded asset and for storage of the persistent key.\r\n \r\n__videoUrl__: The URL of the HLS stream's master playlist.\r\n \r\n__licenseServer__:  Axinom DRM License Server URL.\r\n \r\n__fpsCertificateUrl__: FairPlay Streaming Certificate URL.\r\n \r\n__licenseToken__:  License Token for Content Key Request.\r\n \r\n### Application Transport Security\r\n \r\nIf any of the streams you add are not hosted securely, you will need to add an Application Transport Security (ATS) exception in the Info.plist.  More information on ATS and the relevant plist keys can be found in Apple documentation:\r\n \r\nInformation Property List Key Reference - NSAppTransportSecurity: \u003chttps://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33\u003e\r\n \r\n## Important Notes\r\n \r\nSaving HLS streams for offline playback is only supported for VOD streams.  If you try to save a live HLS stream, the system will throw an exception.\r\n \r\n## Main Files\r\n \r\n__AssetDownloader.swift__:\r\n \r\n- `AssetDownloader` demonstrates how to manage the downloading of HLS streams. It includes APIs for starting and canceling downloads, deleting existing assets of the user's device, and monitoring the download progress and status.\r\n \r\n__ContentKeyManager.swift__:\r\n \r\n- `ContentKeyManager` class configures the instance of AVContentKeySession to use for requesting Content Keys securely for playback or offline use.\r\n \r\n__PlayerViewController.swift__:\r\n \r\n- The `PlayerViewController` uses a native AVPlayer as a base and provides a Video Player user interface together with capabilities of managing the downloading process, deleting downloaded media together with the Content Key associated with an asset. Togglable Console view allows user to see verbose logging of the steps performed during the playback of protected and non-protected assets, Fairplay content protection related activity, as well as AVPlayerItem and AVPlayer statuses, buffer events, and Access log and Error log events associated with AVPlayerItem. Console output can be cleared and copied to the device clipboard.\r\n \r\n__Asset.swift__:\r\n \r\n- `Asset` is a class that holds information about an Asset and adds its AVURLAsset as a recipient to the Playback Content Key Session in a protected playback/download use case. DownloadState extension is used to track the download states of Assets, Keys extension is used to define a number of values to use as keys in dictionary lookups.\r\n \r\n\u003cbr\u003e\r\n \r\n## FairPlay Streaming Overview\r\n \r\nThis sample application allows to play protected and non-protected HTTP Live Streams and preserve them on disk for offline playback.\r\n \r\nAfter the user requests playback of the protected HLS asset from AVFoundation m3u8 playlist will be downloaded from the Internet and parsed by AVFoundation.\r\n \r\n \r\n### Online playback and key delivery scenario\r\n \r\nThe following image demonstrates further steps performed to playback FairPlay protected media in an online scenario.\r\n \r\n![OnlineScenario](OnlineScenario.png \"OnlineScenario\")\r\n \r\n\u003cbr\u003e\r\n \r\n### 1. App receives key loading request from AVFoundation\r\n \r\nDuring parsing of the m3u8 playlist provided by the client, AVFoundation determines that the content is encrypted (m3u8 playlist contains KEY tag).\r\nAs a result ```AVContentKeySessionDelegate``` will provide ```AVContentKeyRequest``` object by invoking the following delegate callback.\r\n```swift\r\nfunc contentKeySession(_ session: AVContentKeySession, didProvide keyRequest: AVContentKeyRequest) {\r\n handleOnlineContentKeyRequest(keyRequest: keyRequest)\r\n}\r\n```\r\n \r\nReceived ```AVContentKeyRequest``` object will allow performing FairPlay streaming specific operations like creating SPC - Server Playback Context (Content Key Request) and then sending it to a Key Server. For sake of simplicity we can name SPC a Content Key Request.\r\n \r\n### 2. App requests for a Content Key Request (SPC) from AVFoundation.\r\n \r\nIn ```handleOnlineContentKeyRequest``` method we check whether the Application Certificate is available, and if not, it will be requested from ```fpsCertificateUrl``` url, defined in Streams.json.\r\n \r\n### 3. AVFoundation creates Content Key Request (SPC)\r\nAs a next step, in the ```provideOnlineKey(withKeyRequest: keyRequest, contentIdentifier: contentIdentifierData)``` method we ask AVFoundation to prepare a Content Key Request (SPC) for a specific combination of application and content (Content Identifier previously parsed from m3u8 playlist).\r\n \r\n```swift\r\nkeyRequest.makeStreamingContentKeyRequestData(forApp: self.fpsCertificate,\r\n                                  contentIdentifier: contentIdentifierData,\r\n                                            options: [AVContentKeyRequestProtocolVersionsKey: [1]],\r\n                                  completionHandler: completionHandler)\r\n```\r\n \r\n### 4. App sends Content Key Request (SPC) to a Key Server\r\n \r\nNow in ```completionHandler``` Content Key Request (SPC) returned by ```makeStreamingContentKeyRequestData``` method will be sent to a Key Server.\r\n \r\n```swift\r\nlet ckcData = try strongSelf.requestContentKeyFromKeySecurityModule(spcData: spcData)\r\n```\r\n \r\n### 5. Key Server responds back with Content Key Response (CKC)\r\n \r\nKey Server responds back with CKC - Content Key Context, for sake of simplicity we can name it Content Key Response. ```AVContentKeyResponse``` class object will be used to represent the data returned from the Key Server.\r\n```swift\r\nlet keyResponse = AVContentKeyResponse(fairPlayStreamingKeyResponseData: ckcData)\r\n```\r\n \r\n### 6. App provides Content Key Response (CKC) to AVFoundation\r\n \r\nNow the app will provide the Content Key Response (CKC) to AVFoundation to make protected content available for processing.\r\n \r\n```swift\r\nkeyRequest.processContentKeyResponse(keyResponse)\r\n```\r\nFinally, AVFoundation can start decryption and playback.\r\n \r\n\u003cbr\u003e\r\n\r\n### License renewal\r\n\r\n```AVContentKeyRequest``` provided by ```AVContentKeySessionDelegate``` (step 1 in the online playback and key delivery scenario) is saved and used for renewing the license using\r\n```renewExpiringResponseData(for contentKeyRequest: AVContentKeyRequest)``` function. For renewing the license in the application, \"Renew\" button on Player View Controller has to be clicked.\r\n\r\n\u003cbr\u003e\r\n \r\n### Key delivery for offline use\r\n \r\nThe following image demonstrates steps performed to deliver the persistable key that will be used to playback Fairplay protected content in an offline scenario.\r\n \r\n![PersistableScenario](PersistableScenario.png \"PersistableScenario\")\r\n \r\n \r\n### 1. The app initiates online key loading request from AVFoundation\r\n \r\nKey loading process in initiated by calling ```processContentKeyRequest``` method on ```AVContentKeySession``` instance which in current sample app is wrapped into ```requestPersistableContentKeys``` method.\r\n \r\n```swift\r\nfunc requestPersistableContentKeys(forAsset asset: Asset) {   \r\n contentKeySession.processContentKeyRequest(withIdentifier: asset.contentKeyId,\r\n                                          initializationData: nil,\r\n                                                     options: nil)\r\n}\r\n```\r\n \r\n### 2. AVFoundation responses with an online key loading request\r\n \r\nOnce this method is called ```AVContentKeySession``` will initiate online key loading first by invoking the following delegate callback\r\n \r\n```swift\r\nfunc contentKeySession(_ session: AVContentKeySession, didProvide keyRequest: AVContentKeyRequest) {       \r\n       handleOnlineContentKeyRequest(keyRequest: keyRequest)\r\n}\r\n```\r\n \r\n### 3. The app initiates a persistable key loading request\r\n \r\nOnce ```handleOnlineContentKeyRequest``` method is called, ```downloadRequestedByUser``` parameter previously set to true, will determine that initial intention was to initiate Persistable Content Key loading process. As a result Persistable Content Key will be initiated by ```respondByRequestingPersistableContentKeyRequestAndReturnError``` method:\r\n \r\n```swift\r\nkeyRequest.respondByRequestingPersistableContentKeyRequestAndReturnError()\r\n```\r\n \r\n### 4. AVFoundation responses with a persistable key loading request\r\n \r\nOnce this method is called ```AVContentKeySession``` will send ```AVPersistableContentKeyRequest``` object by invoking the following delegate callback:\r\n \r\n```swift\r\nfunc contentKeySession(_ session: AVContentKeySession, didProvide keyRequest: AVPersistableContentKeyRequest) {\r\n handlePersistableContentKeyRequest(keyRequest: keyRequest)\r\n}\r\n```\r\n \r\nReceived AVPersistableContentKeyRequest object will allow performing FairPlay streaming specific operations like creating SPC - Server Playback Context (Content Key Request) and sending it to a Key Server.\r\n \r\n### 5-6. AVFoundation creates Persistable Content Key Request (SPC)\r\n \r\nIn ```handlePersistableContentKeyRequest``` method we check whether the Application Certificate is available, and if not, it will be requested from ```fpsCertificateUrl``` URL, defined in Streams.json.\r\n \r\nAs a next step, we ask AVFoundation to prepare a Content Key Request (SPC) for a specific combination of application and content (Content Identifier parsed from m3u8 playlist).\r\n \r\n```swift\r\nkeyRequest.makeStreamingContentKeyRequestData(forApp: self.fpsCertificate,\r\n                                  contentIdentifier: contentIdentifierData,\r\n                                            options: [AVContentKeyRequestProtocolVersionsKey: [1]],\r\n                                  completionHandler: completionHandler)\r\n```\r\n \r\n### 7. App sends Content Key Request (SPC) to a Key Server\r\n \r\nNow in ```completionHandler``` Content Key Request (SPC) returned by ```makeStreamingContentKeyRequestData``` method will be sent to a Key Server.\r\n \r\n```swift\r\nlet ckcData = try strongSelf.requestContentKeyFromKeySecurityModule(spcData: spcData)\r\n```\r\n \r\n### 8. Key Server responses back with Content Key Response (CKC)\r\n \r\nUpon receiving the Content Key Response - CKC is passed to AVFoundation to create a persistable content key, that will be used to decrypt Fairplay protected content in offline usage.\r\n \r\n```swift\r\nlet persistentKey = try keyRequest.persistableContentKey(fromKeyVendorResponse: ckcData, options: nil)\r\n```\r\n \r\nNow Persistable Content Key is delivered and will be saved to the device.\r\n \r\n```swift\r\ntry strongSelf.writePersistableContentKey(contentKey: persistentKey, withAssetName: strongSelf.asset.name)\r\n```\r\n \r\n### 9. App provides Content Key Response (CKC) to AVFoundation\r\n \r\n```AVContentKeyResponse``` class object will be used to represent the data returned from the Key Server.\r\n \r\n```swift\r\nlet keyResponse = AVContentKeyResponse(fairPlayStreamingKeyResponseData: ckcData)\r\n```\r\n \r\nNow the app will provide the Content Key Response (CKC) to AVFoundation to make protected content available for processing.\r\n \r\n```swift\r\nkeyRequest.processContentKeyResponse(keyResponse)\r\n```\r\n \r\nThe Persistable Content Key is now ready to be used to decrypt Fairplay protected content and AVFoundation start downloading the stream to the device.\r\n \r\nFor that ```.HasAvailablePersistableContentKey``` notification will be sent.\r\n \r\n```swift\r\nNotificationCenter.default.post(name: .HasAvailablePersistableContentKey, object: nil, userInfo: nil)\r\n```\r\n \r\n### Downloading the stream to the device\r\n \r\nAfter dispatching the ```.HasAvailablePersistableContentKey``` notification corresponding ```handleContentKeyDelegateHasAvailablePersistableContentKey``` handler method will be called.\r\n \r\nAnd if an asset is now already previously saved the downloading process will be initiated by\r\ncalling ```downloadStream()``` method, which is a wrapper to ```Downloader``` class method\r\n \r\n```download(asset: Asset)```\r\n \r\n \r\nAll downloading related work is handled AVFoundation. Following classes are used for this purpose:\r\n \r\n`AVAggregateAssetDownloadTask` - the sample creates and initializes an AVAggregateAssetDownloadTask for the download of an HLS stream. Only the default media selections for each of the asset’s media selection groups are downloaded (these are indicated in the HLS playlist EXT-X-MEDIA tags by a DEFAULT attribute of YES).\r\n \r\n`AVAssetDownloadURLSession` - a URL session that supports the creation and execution of asset download tasks.\r\n \r\nFollowing protocols are implemented to handle the download process:\r\n \r\n\u003cbr\u003e\r\n \r\n### `AVAssetDownloadDelegate`\r\n \r\nThis protocol handles download-related events.\r\n \r\nFollowing methods are implemented to notify the app of download progress, completion events, and download location:\r\n \r\n* `urlSession(_:aggregateAssetDownloadTask:willDownloadTo:)`\r\nThe method asks the delegate for the asset download location.\r\n \r\n**NOTE:** This delegate callback should only be used to save the location URL somewhere in your application. Any additional work should be done in `URLSessionTaskDelegate.urlSession(_:task:didCompleteWithError:)`.\r\n \r\n* `urlSession(_:aggregateAssetDownloadTask:didLoad:totalTimeRangesLoaded:timeRangeExpectedToLoad:for:)`\r\nMethod to adopt to subscribe to progress updates of a download task\r\n \r\n* `urlSession(_:aggregateAssetDownloadTask:didCompleteFor:)`\r\nThe method called when a child AVAssetDownloadTask completes for each media selection.\r\n \r\nFind out more about `AVAssetDownloadDelegate`:\r\nhttps://developer.apple.com/documentation/avfoundation/avassetdownloaddelegate\r\n \r\n\u003cbr\u003e\r\n \r\n### `URLSessionTaskDelegate`\r\nA protocol that defines methods that URL session instance calls on their delegates to handle task-level events.\r\n \r\nThe following method is implemented to notify the app that the task finished transferring data as well as to provide download related error handling:\r\n \r\n* `urlSession(_:task:didCompleteWithError:)`\r\n \r\nFind out more about `URLSessionTaskDelegate`:\r\nhttps://developer.apple.com/documentation/foundation/urlsessiontaskdelegate\r\n\r\n### Downloaded content folder structure\r\n\r\nDownloaded content can be obtained and accessed as follows:\r\n1. Open Devices and Simulators screen in Xcode (Window -\u003e Devices and Simulators).\r\n2. From the list of connected devices, choose the one that has Axinom DRM Sample\r\nPlayer installed.\r\n3. In the opened window under installed apps section, select Axinom DRM Sample\r\nPlayer and then choose Download Container option accessible from the App\r\ncontainer actions menu indicated by three period signs at the bottom).\r\n4. To view the downloaded application container, right click on it and select\r\nShow Package Contents. While doing that, make sure that hidden files are shown\r\nin Finder (shortcut: \"Command + Shift + period\" to toggle hiding/showing hidden\r\nfiles) because some files or folders might not be visible otherwise.\r\n\r\nThe following screenshot shows how the downloaded file structure looks like on\r\nan iOS device. Downloaded Fairplay keys are stored inside .keys folder. Audio\r\nand video content is located under .movpkg folder. Contents of that can also be\r\nviewed by right clicking on it and selecting the Show Package Contents option.\r\nVideo fragments are saved into a subfolder which name starts with \"0\" and the\r\naudio fragments folder name starts with \"1\". The third folder named \"Data\"\r\ncontains the HLS master playlist. Finally, boot.xml describes the .movpkg\r\nfolder content.\r\n\r\n![DownloadedFileStructure](DownloadedFileStructure.png \"DownloadedFileStructure\")\r\n\r\n\r\n## Helpful Resources\r\n\r\nThe following resources available on the Apple Developer website contain helpful information that you may find useful\r\n\r\n* General information regarding HLS on supported Apple devices and platforms:\r\n    * [HTTP Live Streaming (HLS) - Apple Developer](https://developer.apple.com/streaming/)\r\n    * [AV Foundation - Apple Developer](https://developer.apple.com/av-foundation/)\r\n* For information regarding topics specific to FairPlay Streaming as well as the latest version of the FairPlay Streaming Server SDK, please see:\r\n    * [FairPlay Streaming - Apple Developer](http://developer.apple.com/streaming/fps/).\r\n* Information regarding authoring HLS content for devices and platforms:\r\n    * [HLS Authoring Specification for Apple Devices](https://developer.apple.com/library/content/documentation/General/Reference/HLSAuthoringSpec/index.html#//apple_ref/doc/uid/TP40016596-CH4-SW1)\r\n    * [WWDC 2016 - Session 510: Validating HTTP Live Streams](https://developer.apple.com/videos/play/wwdc2016/510/)\r\n    * [WWDC 2017 - Session 515: HLS Authoring Update](https://developer.apple.com/videos/play/wwdc2017/515/)\r\n* Information regarding error handling on the server side and with AVFoundation on supported Apple devices and platforms:\r\n    * [WWDC 2017 - Session 514: Error Handling Best Practices for HTTP Live Streaming](https://developer.apple.com/videos/play/wwdc2017/514/)\r\n \r\n\r\n## Requirements\r\n \r\n### Build\r\n \r\nXcode 11.0 or later; iOS 13.0 SDK or later\r\n \r\n### Runtime\r\n \r\niOS 13.1 or later.\r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faxinom%2Fdrm-sample-player-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faxinom%2Fdrm-sample-player-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faxinom%2Fdrm-sample-player-ios/lists"}