-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add DRM protected content playback support (#148)
- Loading branch information
Showing
14 changed files
with
574 additions
and
41 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
// | ||
// Copyright (c) SRG SSR. All rights reserved. | ||
// | ||
// License information is available from the LICENSE file. | ||
// | ||
|
||
import AVFoundation | ||
import Combine | ||
import Player | ||
|
||
final class ContentKeySessionDelegate: NSObject, AVContentKeySessionDelegate { | ||
private let certificateUrl: URL | ||
private let session = URLSession(configuration: .default) | ||
private var cancellable: AnyCancellable? | ||
|
||
init(certificateUrl: URL) { | ||
self.certificateUrl = certificateUrl | ||
} | ||
|
||
private static func contentKeyRequestDataPublisher( | ||
for request: AVContentKeyRequest, | ||
certificateData: Data | ||
) -> AnyPublisher<Data, Error> { | ||
Future { promise in | ||
// Use a dummy content identifier (otherwise the request will fail). | ||
request.makeStreamingContentKeyRequestData( | ||
forApp: certificateData, | ||
contentIdentifier: "content_id".data(using: .utf8) | ||
) { data, error in | ||
if let data { | ||
promise(.success(data)) | ||
} | ||
else if let error { | ||
promise(.failure(error)) | ||
} | ||
} | ||
} | ||
.eraseToAnyPublisher() | ||
} | ||
|
||
private static func contentKeyContextRequest(from identifier: Any?, httpBody: Data) -> URLRequest? { | ||
guard let skdUrlString = identifier as? String, | ||
var components = URLComponents(string: skdUrlString) else { | ||
return nil | ||
} | ||
|
||
components.scheme = "https" | ||
guard let url = components.url else { return nil } | ||
|
||
var request = URLRequest(url: url) | ||
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type") | ||
request.httpMethod = "POST" | ||
request.httpBody = httpBody | ||
return request | ||
} | ||
|
||
private static func contentKeyContextDataPublisher( | ||
fromKeyRequestData keyRequestData: Data, | ||
identifier: Any?, | ||
session: URLSession | ||
) -> AnyPublisher<Data, Error> { | ||
guard let request = contentKeyContextRequest(from: identifier, httpBody: keyRequestData) else { | ||
return Fail(error: DRMError.missingContentKeyContext) | ||
.eraseToAnyPublisher() | ||
} | ||
return session.dataTaskPublisher(for: request) | ||
.mapError { $0 } | ||
.map(\.data) | ||
.eraseToAnyPublisher() | ||
} | ||
|
||
func contentKeySession(_ session: AVContentKeySession, didProvide keyRequest: AVContentKeyRequest) { | ||
cancellable = self.session.dataTaskPublisher(for: certificateUrl) | ||
.mapError { $0 } | ||
.map { Self.contentKeyRequestDataPublisher(for: keyRequest, certificateData: $0.data) } | ||
.switchToLatest() | ||
.map { [session = self.session] data in | ||
Self.contentKeyContextDataPublisher(fromKeyRequestData: data, identifier: keyRequest.identifier, session: session) | ||
} | ||
.switchToLatest() | ||
.sink { completion in | ||
switch completion { | ||
case .finished: | ||
break | ||
case let .failure(error): | ||
keyRequest.processContentKeyResponseErrorReliably(error) | ||
} | ||
} receiveValue: { data in | ||
let response = AVContentKeyResponse(fairPlayStreamingKeyResponseData: data) | ||
keyRequest.processContentKeyResponse(response) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// | ||
// Copyright (c) SRG SSR. All rights reserved. | ||
// | ||
// License information is available from the LICENSE file. | ||
// | ||
|
||
import Foundation | ||
|
||
struct DRM: Decodable { | ||
enum `Type`: String, Decodable { | ||
case fairPlay = "FAIRPLAY" | ||
case playReady = "PLAYREADY" | ||
case widevine = "WIDEVINE" | ||
} | ||
|
||
let type: `Type` | ||
let certificateUrl: URL? | ||
let licenseUrl: URL | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
// | ||
// Copyright (c) SRG SSR. All rights reserved. | ||
// | ||
// License information is available from the LICENSE file. | ||
// | ||
|
||
import AVFoundation | ||
|
||
public extension AVContentKeyRequest { | ||
/// Informs the receiver that obtaining a content key response has failed, resulting in failure handling. Unlike | ||
/// `processContentKeyResponseError(_:)` this method ensures error information can be reliably forwarded to the | ||
/// player item being loaded in case of failure. | ||
/// - Parameter error: An error object indicating the reason for the failure. | ||
func processContentKeyResponseErrorReliably(_ error: Error) { | ||
if let nsError = NSError.error(from: error) { | ||
processContentKeyResponseError(nsError) | ||
} | ||
else { | ||
processContentKeyResponseError(error) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ enum Mock { | |
} | ||
|
||
enum MediaCompositionKind: String { | ||
case drm | ||
case onDemand | ||
case live | ||
} | ||
|
12 changes: 12 additions & 0 deletions
12
Tests/CoreBusinessTests/Resources.xcassets/MediaComposition_drm.dataset/Contents.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
{ | ||
"data" : [ | ||
{ | ||
"filename" : "MediaComposition_drm.json", | ||
"idiom" : "universal" | ||
} | ||
], | ||
"info" : { | ||
"author" : "xcode", | ||
"version" : 1 | ||
} | ||
} |
Oops, something went wrong.