Skip to content

Commit

Permalink
Merge pull request #261 from bitmovin/casting-ios/widevine-drm
Browse files Browse the repository at this point in the history
Widevine DRM support for casting on iOS
  • Loading branch information
rolandkakonyi authored Sep 22, 2023
2 parents 49370d0 + 454fbba commit 4fa39ac
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 26 deletions.
2 changes: 1 addition & 1 deletion ios/DrmModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class DrmModule: NSObject, RCTBridgeModule {
bridge.uiManager.addUIBlock { [weak self] _, _ in
guard
self?.drmConfigs[nativeId] == nil,
let fairplayConfig = RCTConvert.fairplayConfig(config)
let fairplayConfig = RCTConvert.drmConfig(config).fairplay
else {
return
}
Expand Down
56 changes: 44 additions & 12 deletions ios/RCTConvert+BitmovinPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ extension RCTConvert {
- Parameter json: JS object
- Returns: The produced `SourceConfig` object
*/
static func sourceConfig(_ json: Any?, drmConfig: FairplayConfig? = nil) -> SourceConfig? {
static func sourceConfig(_ json: Any?, drmConfig: DrmConfig? = nil) -> SourceConfig? {
guard let json = json as? [String: Any?] else {
return nil
}
Expand Down Expand Up @@ -335,33 +335,62 @@ extension RCTConvert {
}
}

/**
Utility method to get a `DrmConfig` from a JS object.
- Parameter json: JS object
- Returns: The generated `DrmConfig` object
*/
static func drmConfig(_ json: Any?) -> (fairplay: FairplayConfig?, widevine: WidevineConfig?) {
guard let json = json as? [String: Any?] else {
return (nil, nil)
}
return (
fairplay: RCTConvert.fairplayConfig(json["fairplay"]),
widevine: RCTConvert.widevineConfig(json["widevine"])
)
}

/**
Utility method to get a `FairplayConfig` from a JS object.
- Parameter json: JS object
- Returns: The generated `FairplayConfig` object
*/
static func fairplayConfig(_ json: Any?) -> FairplayConfig? {
guard
let json = json as? [String: Any?],
let fairplayJson = json["fairplay"] as? [String: Any?],
let licenseURL = fairplayJson["licenseUrl"] as? String,
let certificateURL = fairplayJson["certificateUrl"] as? String
else {
guard let json = json as? [String: Any?],
let licenseURL = json["licenseUrl"] as? String,
let certificateURL = json["certificateUrl"] as? String else {
return nil
}
let fairplayConfig = FairplayConfig(
license: URL(string: licenseURL),
certificateURL: URL(string: certificateURL)!
)
if let licenseRequestHeaders = fairplayJson["licenseRequestHeaders"] as? [String: String] {
if let licenseRequestHeaders = json["licenseRequestHeaders"] as? [String: String] {
fairplayConfig.licenseRequestHeaders = licenseRequestHeaders
}
if let certificateRequestHeaders = fairplayJson["certificateRequestHeaders"] as? [String: String] {
if let certificateRequestHeaders = json["certificateRequestHeaders"] as? [String: String] {
fairplayConfig.certificateRequestHeaders = certificateRequestHeaders
}
return fairplayConfig
}

/**
Utility method to get a `WidevineConfig` from a JS object.
- Parameter json: JS object
- Returns: The generated `WidevineConfig` object
*/
static func widevineConfig(_ json: Any?) -> WidevineConfig? {
guard let json = json as? [String: Any?],
let licenseURL = json["licenseUrl"] as? String else {
return nil
}
let widevineConfig = WidevineConfig(license: URL(string: licenseURL))
if let licenseRequestHeaders = json["httpHeaders"] as? [String: String] {
widevineConfig.licenseRequestHeaders = licenseRequestHeaders
}
return widevineConfig
}

/**
Utility method to get a `ThumbnailTrack` instance from a JS object.
- Parameter url: String.
Expand Down Expand Up @@ -989,12 +1018,15 @@ extension RCTConvert {
- Returns: The produced `SourceRemoteControlConfig` object
*/
static func sourceRemoteControlConfig(_ json: Any?) -> SourceRemoteControlConfig? {
guard let json = json as? [String: Any?] else {
guard let json = json as? [String: Any?],
let castSourceConfigJson = json["castSourceConfig"] as? [String: Any?] else {
return nil
}

return SourceRemoteControlConfig(
castSourceConfig: RCTConvert.sourceConfig(json["castSourceConfig"])
castSourceConfig: RCTConvert.sourceConfig(
json["castSourceConfig"],
drmConfig: RCTConvert.drmConfig(castSourceConfigJson["drmConfig"]).widevine
)
)
}
}
16 changes: 8 additions & 8 deletions ios/SourceModule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ class SourceModule: NSObject, RCTBridgeModule {
analyticsSourceMetadata: Any?
) {
bridge.uiManager.addUIBlock { [weak self] _, _ in
let fairplayConfig: FairplayConfig?
let drmConfig: DrmConfig?
if let drmNativeId = drmNativeId {
fairplayConfig = self?.getDrmModule()?.retrieve(drmNativeId)
drmConfig = self?.getDrmModule()?.retrieve(drmNativeId)
} else {
fairplayConfig = nil
drmConfig = nil
}

guard
self?.sources[nativeId] == nil,
let sourceConfig = RCTConvert.sourceConfig(config, drmConfig: fairplayConfig),
let sourceConfig = RCTConvert.sourceConfig(config, drmConfig: drmConfig),
let sourceMetadata = RCTConvert.analyticsSourceMetadata(analyticsSourceMetadata)
else {
return
Expand All @@ -100,16 +100,16 @@ class SourceModule: NSObject, RCTBridgeModule {
sourceRemoteControlConfig: Any?
) {
bridge.uiManager.addUIBlock { [weak self] _, _ in
let fairplayConfig: FairplayConfig?
let drmConfig: DrmConfig?
if let drmNativeId = drmNativeId {
fairplayConfig = self?.getDrmModule()?.retrieve(drmNativeId)
drmConfig = self?.getDrmModule()?.retrieve(drmNativeId)
} else {
fairplayConfig = nil
drmConfig = nil
}

guard
self?.sources[nativeId] == nil,
let sourceConfig = RCTConvert.sourceConfig(config, drmConfig: fairplayConfig)
let sourceConfig = RCTConvert.sourceConfig(config, drmConfig: drmConfig)
else {
return
}
Expand Down
8 changes: 6 additions & 2 deletions src/drm/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ const DrmModule = NativeModules.DrmModule;
*/
export interface DrmConfig extends NativeInstanceConfig {
/**
* FairPlay specific configuration. Only applicable for iOS.
* FairPlay specific configuration.
*
* @platform iOS
*/
fairplay?: FairplayConfig;
/**
* Widevine specific configuration. Only applicable for Android.
* Widevine specific configuration.
*
* @platform Android, iOS (only for casting).
*/
widevine?: WidevineConfig;
}
Expand Down
14 changes: 11 additions & 3 deletions src/drm/widevineConfig.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Represents a Widevine Streaming DRM config.
* Android only.
* @platform Android, iOS (only for casting).
*/
export interface WidevineConfig {
/**
Expand All @@ -10,7 +10,7 @@ export interface WidevineConfig {
/**
* A map containing the HTTP request headers, or null.
*/
httpHeaders: Record<string, string>;
httpHeaders?: Record<string, string>;
/**
* A block to prepare the data which is sent as the body of the POST license request.
* As many DRM providers expect different, vendor-specific messages, this can be done using
Expand All @@ -19,6 +19,8 @@ export interface WidevineConfig {
* Note that both the passed `message` data and this block return value should be a Base64 string.
* So use whatever solution suits you best to handle Base64 in React Native.
*
* @platform Android
*
* @param message - Base64 encoded message data.
* @returns The processed Base64 encoded message.
*/
Expand All @@ -31,19 +33,25 @@ export interface WidevineConfig {
* Note that both the passed `license` data and this block return value should be a Base64 string.
* So use whatever solution suits you best to handle Base64 in React Native.
*
* @platform Android
*
* @param license - Base64 encoded license data.
* @returns The processed Base64 encoded license.
*/
prepareLicense?: (license: string) => string;
/**
* Set widevine's preferred security level.
*
* @platform Android
*/
preferredSecurityLevel?: string;
/**
* Indicates if the DRM sessions should be kept alive after a source is unloaded.
* This allows DRM sessions to be reused over several different source items with the same DRM configuration as well
* as the same DRM scheme information.
* Default: `false`
*
* @platform Android
*/
shouldKeepDrmSessionsAlive: boolean;
shouldKeepDrmSessionsAlive?: boolean;
}

0 comments on commit 4fa39ac

Please sign in to comment.