react-native-theoplayer 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/theoplayer/PlayerEventEmitter.kt +3 -3
- package/android/src/main/java/com/theoplayer/ReactTHEOplayerView.kt +1 -0
- package/ios/{THEOplayerRCTViewMainEventHandler.swift → THEOplayerRCTMainEventHandler.swift} +1 -1
- package/ios/{THEOplayerRCTViewMediaTrackEventHandler.swift → THEOplayerRCTMediaTrackEventHandler.swift} +2 -2
- package/ios/THEOplayerRCTSourceDescriptionBuilder.swift +19 -114
- package/ios/{THEOplayerRCTViewTextTrackEventHandler.swift → THEOplayerRCTTextTrackEventHandler.swift} +2 -2
- package/ios/THEOplayerRCTView.swift +33 -133
- package/ios/{THEOplayerRCTAdAggregator.swift → ads/THEOplayerRCTAdAggregator.swift} +1 -1
- package/ios/ads/THEOplayerRCTAdsAPI+DAI.swift +108 -0
- package/ios/{THEOplayerRCTAdsAPI.swift → ads/THEOplayerRCTAdsAPI.swift} +6 -96
- package/ios/{THEOplayerRCTViewAdEventHandler.swift → ads/THEOplayerRCTAdsEventHandler.swift} +3 -3
- package/ios/ads/THEOplayerRCTSourceDescriptionBuilder+Ads.swift +130 -0
- package/ios/ads/THEOplayerRCTView+Ads.swift +15 -0
- package/ios/ads/THEOplayerRCTView+AdsConfig.swift +82 -0
- package/ios/casting/THEOplayerRCTCastAPI+Airplay.swift +97 -0
- package/ios/casting/THEOplayerRCTCastAPI+Chromecast.swift +132 -0
- package/ios/casting/THEOplayerRCTCastAPI.swift +53 -0
- package/ios/{THEOplayerRCTViewCastEventHandler.swift → casting/THEOplayerRCTCastEventHandler.swift} +1 -1
- package/ios/casting/THEOplayerRCTView+CastConfig.swift +47 -0
- package/ios/casting/THEOplayerRCTView+Casting.swift +17 -0
- package/ios/custom/react-native-theoplayer_custom.podspec +6 -6
- package/package.json +1 -1
- package/react-native-theoplayer.podspec +1 -1
- package/ios/THEOplayerRCTCastAPI.swift +0 -275
- /package/ios/{THEOplayerRCTContentProtectionAPI.swift → contentprotection/THEOplayerRCTContentProtectionAPI.swift} +0 -0
- /package/ios/{THEOplayerRCTContentProtectionAggregator.swift → contentprotection/THEOplayerRCTContentProtectionAggregator.swift} +0 -0
- /package/ios/{THEOplayerRCTProxyContentProtectionIntegration.swift → contentprotection/THEOplayerRCTProxyContentProtectionIntegration.swift} +0 -0
- /package/ios/{THEOplayerRCTProxyContentProtectionIntegrationFactory.swift → contentprotection/THEOplayerRCTProxyContentProtectionIntegrationFactory.swift} +0 -0
|
@@ -10,7 +10,6 @@ import Foundation
|
|
|
10
10
|
import UIKit
|
|
11
11
|
|
|
12
12
|
let ERROR_CODE_ADS_ACCESS_FAILURE = "ads_access_failure"
|
|
13
|
-
let ERROR_CODE_DAI_ACCESS_FAILURE = "dai_access_failure"
|
|
14
13
|
let ERROR_CODE_ADS_GET_PLAYING_STATE_FAILED = "ads_get_playing_state_failure"
|
|
15
14
|
let ERROR_CODE_ADS_GET_CURRENT_ADBREAK_FAILED = "ads_get_current_adbreak_failure"
|
|
16
15
|
let ERROR_CODE_ADS_GET_CURRENT_ADBREAK_UNDEFINED = "ads_get_current_adbreak_undefined"
|
|
@@ -18,16 +17,11 @@ let ERROR_CODE_ADS_GET_CURRENT_ADS_FAILED = "ads_get_current_ads_failure"
|
|
|
18
17
|
let ERROR_CODE_ADS_GET_CURRENT_ADS_UNDEFINED = "ads_get_current_ads_undefined"
|
|
19
18
|
let ERROR_CODE_ADS_GET_SCHEDULED_ADBREAKS_FAILED = "ads_get_scheduled_adbreaks_failure"
|
|
20
19
|
let ERROR_CODE_ADS_GET_SCHEDULED_ADBREAKS_UNDEFINED = "ads_get_scheduled_adbreaks_undefined"
|
|
21
|
-
let ERROR_CODE_DAI_GET_SNAPBACK_FAILED = "dai_get_snapback_failed"
|
|
22
|
-
let ERROR_CODE_DAI_GET_SNAPBACK_UNDEFINED = "dai_get_snapback_undefined"
|
|
23
|
-
|
|
24
20
|
let ERROR_MESSAGE_ADS_ACCESS_FAILURE = "Could not access THEOplayer Ads Module"
|
|
25
|
-
let ERROR_MESSAGE_DAI_ACCESS_FAILURE = "Could not access THEOplayer Ads DAI Module"
|
|
26
21
|
let ERROR_MESSAGE_ADS_GET_CURRENT_ADBREAK_UNDEFINED = "Undefined adBreak object"
|
|
27
22
|
let ERROR_MESSAGE_ADS_GET_CURRENT_ADS_UNDEFINED = "Undefined ads array"
|
|
28
23
|
let ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE = "This functionality is not supported by the provided iOS SDK"
|
|
29
24
|
let ERROR_MESSAGE_ADS_GET_SCHEDULED_ADBREAKS_UNDEFINED = "Undefined adbreaks array"
|
|
30
|
-
let ERROR_MESSAGE_DAI_GET_SNAPBACK_UNDEFINED = "Undefined dai snapback"
|
|
31
25
|
|
|
32
26
|
@objc(THEOplayerRCTAdsAPI)
|
|
33
27
|
class THEOplayerRCTAdsAPI: NSObject, RCTBridgeModule {
|
|
@@ -41,7 +35,8 @@ class THEOplayerRCTAdsAPI: NSObject, RCTBridgeModule {
|
|
|
41
35
|
return false
|
|
42
36
|
}
|
|
43
37
|
|
|
44
|
-
#if
|
|
38
|
+
#if (GOOGLE_IMA || GOOGLE_DAI)
|
|
39
|
+
|
|
45
40
|
@objc(skip:)
|
|
46
41
|
func skip(_ node: NSNumber) -> Void {
|
|
47
42
|
|
|
@@ -158,14 +153,16 @@ class THEOplayerRCTAdsAPI: NSObject, RCTBridgeModule {
|
|
|
158
153
|
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
159
154
|
if let adData = adDict as? [String:Any],
|
|
160
155
|
let ads = theView.ads(),
|
|
161
|
-
let adDescription = THEOplayerRCTSourceDescriptionBuilder.
|
|
156
|
+
let adDescription = THEOplayerRCTSourceDescriptionBuilder.buildSingleAdDescription(adData) {
|
|
162
157
|
ads.schedule(adDescription: adDescription)
|
|
163
158
|
} else {
|
|
164
159
|
if DEBUG_ADS_API { print("[NATIVE] Could not schedule new ad.") }
|
|
165
160
|
}
|
|
166
161
|
}
|
|
167
162
|
}
|
|
163
|
+
|
|
168
164
|
#else
|
|
165
|
+
|
|
169
166
|
@objc(skip:)
|
|
170
167
|
func skip(_ node: NSNumber) -> Void {
|
|
171
168
|
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
@@ -202,94 +199,7 @@ class THEOplayerRCTAdsAPI: NSObject, RCTBridgeModule {
|
|
|
202
199
|
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
203
200
|
return
|
|
204
201
|
}
|
|
205
|
-
#endif
|
|
206
|
-
|
|
207
|
-
#if os(iOS) && ADS && GOOGLE_DAI
|
|
208
|
-
@objc(daiSnapback:resolver:rejecter:)
|
|
209
|
-
func daiSnapback(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
210
|
-
DispatchQueue.main.async {
|
|
211
|
-
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
212
|
-
if let ads = theView.ads(),
|
|
213
|
-
let dai = ads.dai {
|
|
214
|
-
dai.requestSnapBack { enabled, error in
|
|
215
|
-
if let err = error {
|
|
216
|
-
reject(ERROR_CODE_DAI_GET_SNAPBACK_FAILED, err.localizedDescription, error)
|
|
217
|
-
if DEBUG_ADS_API { print("[NATIVE] Retrieving dai snapback status failed: \(err.localizedDescription)") }
|
|
218
|
-
} else if let snapBack = enabled {
|
|
219
|
-
resolve(snapBack)
|
|
220
|
-
} else {
|
|
221
|
-
reject(ERROR_CODE_DAI_GET_SNAPBACK_UNDEFINED, ERROR_MESSAGE_DAI_GET_SNAPBACK_UNDEFINED, nil)
|
|
222
|
-
if DEBUG_ADS_API { print("[NATIVE] Retrieving dai snapback status failed.") }
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
} else {
|
|
226
|
-
reject(ERROR_CODE_DAI_ACCESS_FAILURE, ERROR_MESSAGE_DAI_ACCESS_FAILURE, nil)
|
|
227
|
-
if DEBUG_ADS_API { print("[NATIVE] Could not retrieve dai snapback status (ads DAI module unavailable).") }
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
@objc(daiSetSnapback:enabled:)
|
|
233
|
-
func daiSetSnapback(_ node: NSNumber, enabled: Bool) -> Void {
|
|
234
|
-
DispatchQueue.main.async {
|
|
235
|
-
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
236
|
-
if let ads = theView.ads(),
|
|
237
|
-
let dai = ads.dai {
|
|
238
|
-
dai.setSnapBack(enabled, completionHandler: nil)
|
|
239
|
-
} else {
|
|
240
|
-
if DEBUG_ADS_API { print("[NATIVE] Could not update dai snapback status (ads DAI module unavailable).") }
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
202
|
|
|
245
|
-
@objc(daiContentTimeForStreamTime:time:resolver:rejecter:)
|
|
246
|
-
func daiContentTimeForStreamTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
247
|
-
DispatchQueue.main.async {
|
|
248
|
-
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
249
|
-
if let ads = theView.ads(),
|
|
250
|
-
let dai = ads.dai {
|
|
251
|
-
let streamTime = timeValue.doubleValue * 0.001 // msec -> sec
|
|
252
|
-
let contentTime = dai.contentTime(from: streamTime) * 1000.0 // sec -> msec
|
|
253
|
-
resolve(contentTime)
|
|
254
|
-
} else {
|
|
255
|
-
reject(ERROR_CODE_DAI_ACCESS_FAILURE, ERROR_MESSAGE_DAI_ACCESS_FAILURE, nil)
|
|
256
|
-
if DEBUG_ADS_API { print("[NATIVE] Could not convert stream time to content time (ads DAI module unavailable).") }
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
@objc(daiStreamTimeForContentTime:time:resolver:rejecter:)
|
|
262
|
-
func daiStreamTimeForContentTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
263
|
-
DispatchQueue.main.async {
|
|
264
|
-
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
265
|
-
if let ads = theView.ads(),
|
|
266
|
-
let dai = ads.dai {
|
|
267
|
-
let contentTime = timeValue.doubleValue * 0.001 // msec -> sec
|
|
268
|
-
let streamTime = dai.streamTime(from: contentTime) * 1000.0 // sec -> msec
|
|
269
|
-
resolve(streamTime)
|
|
270
|
-
} else {
|
|
271
|
-
reject(ERROR_CODE_DAI_ACCESS_FAILURE, ERROR_MESSAGE_DAI_ACCESS_FAILURE, nil)
|
|
272
|
-
if DEBUG_ADS_API { print("[NATIVE] Could not convert content time to stream time (ads DAI module unavailable).") }
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
#else
|
|
277
|
-
@objc(daiSnapback:resolver:rejecter:)
|
|
278
|
-
func daiSnapback(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
279
|
-
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
280
|
-
resolve(false)
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
@objc(daiContentTimeForStreamTime:time:resolver:rejecter:)
|
|
284
|
-
func daiContentTimeForStreamTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
285
|
-
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
286
|
-
resolve(timeValue.doubleValue)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
@objc(daiStreamTimeForContentTime:time:resolver:rejecter:)
|
|
290
|
-
func daiStreamTimeForContentTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
291
|
-
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
292
|
-
resolve(timeValue.doubleValue)
|
|
293
|
-
}
|
|
294
203
|
#endif
|
|
204
|
+
|
|
295
205
|
}
|
package/ios/{THEOplayerRCTViewAdEventHandler.swift → ads/THEOplayerRCTAdsEventHandler.swift}
RENAMED
|
@@ -16,7 +16,7 @@ let EVENT_TYPE_AD_THIRD_QUARTILE: String = "adthirdquartile"
|
|
|
16
16
|
let EVENT_TYPE_AD_MIDPOINT: String = "admidpoint"
|
|
17
17
|
let EVENT_TYPE_AD_LOADED: String = "adloaded"
|
|
18
18
|
|
|
19
|
-
class
|
|
19
|
+
class THEOplayerRCTAdsEventHandler {
|
|
20
20
|
// MARK: Members
|
|
21
21
|
private weak var player: THEOplayer?
|
|
22
22
|
|
|
@@ -54,7 +54,7 @@ class THEOplayerRCTViewAdEventHandler {
|
|
|
54
54
|
return
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
#if
|
|
57
|
+
#if (GOOGLE_IMA || GOOGLE_DAI)
|
|
58
58
|
// AD_BEGIN
|
|
59
59
|
self.adBeginListener = player.ads.addEventListener(type: AdsEventTypes.AD_BEGIN) { [weak self] event in
|
|
60
60
|
if DEBUG_THEOPLAYER_EVENTS { print("[NATIVE] Received AD_BEGIN event from THEOplayer Ads") }
|
|
@@ -180,7 +180,7 @@ class THEOplayerRCTViewAdEventHandler {
|
|
|
180
180
|
return
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
#if
|
|
183
|
+
#if (GOOGLE_IMA || GOOGLE_DAI)
|
|
184
184
|
// AD_BEGIN
|
|
185
185
|
if let adBeginListener = self.adBeginListener {
|
|
186
186
|
player.ads.removeEventListener(type: AdsEventTypes.AD_BEGIN, listener: adBeginListener)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// THEOplayerRCTSourceDescriptionBuilder.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import THEOplayerSDK
|
|
5
|
+
import UIKit
|
|
6
|
+
|
|
7
|
+
let SD_PROP_ADS: String = "ads"
|
|
8
|
+
|
|
9
|
+
extension THEOplayerRCTSourceDescriptionBuilder {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
Builds a THEOplayer SourceDescription that can be passed as a source for the THEOplayer.
|
|
13
|
+
- returns: a THEOplayer TypedSource. In case of SSAI we support GoogleDAITypedSource with GoogleDAIVodConfiguration or GoogleDAILiveConfiguration
|
|
14
|
+
*/
|
|
15
|
+
static func buildAdDescriptions(_ sourceData: NSDictionary) -> [AdDescription]? {
|
|
16
|
+
var adsDescriptions: [AdDescription]?
|
|
17
|
+
|
|
18
|
+
#if GOOGLE_IMA
|
|
19
|
+
if let ads = sourceData[SD_PROP_ADS] {
|
|
20
|
+
adsDescriptions = []
|
|
21
|
+
// case: array of ads objects
|
|
22
|
+
if let adsDataArray = ads as? [[String:Any]] {
|
|
23
|
+
for adsData in adsDataArray {
|
|
24
|
+
if let adDescription = THEOplayerRCTSourceDescriptionBuilder.buildSingleAdDescription(adsData) {
|
|
25
|
+
adsDescriptions?.append(adDescription)
|
|
26
|
+
} else {
|
|
27
|
+
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
28
|
+
print("[NATIVE] Could not create THEOplayer GoogleImaAdDescription from adsData array")
|
|
29
|
+
}
|
|
30
|
+
return nil
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// case: single ads object
|
|
35
|
+
else if let adsData = ads as? [String:Any] {
|
|
36
|
+
if let adDescription = THEOplayerRCTSourceDescriptionBuilder.buildSingleAdDescription(adsData) {
|
|
37
|
+
adsDescriptions?.append(adDescription)
|
|
38
|
+
} else {
|
|
39
|
+
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
40
|
+
print("[NATIVE] Could not create THEOplayer GoogleImaAdDescription from adsData")
|
|
41
|
+
}
|
|
42
|
+
return nil
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
#endif
|
|
47
|
+
|
|
48
|
+
return adsDescriptions
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
Creates a THEOplayer GoogleImaAdDescription. This requires an ads property in the RN source description.
|
|
53
|
+
- returns: a THEOplayer GoogleImaAdDescription
|
|
54
|
+
*/
|
|
55
|
+
static func buildSingleAdDescription(_ adsData: [String:Any]) -> AdDescription? {
|
|
56
|
+
#if GOOGLE_IMA
|
|
57
|
+
if let integration = adsData[SD_PROP_INTEGRATION] as? String,
|
|
58
|
+
integration == AdIntegration.google_ima._rawValue {
|
|
59
|
+
// timeOffset can be Int or String: 10, "01:32:54.78", "1234.56", "start", "end", "10%", ...
|
|
60
|
+
let timeOffset = adsData[SD_PROP_TIME_OFFSET] as? String ?? String(adsData[SD_PROP_TIME_OFFSET] as? Int ?? 0)
|
|
61
|
+
var srcString: String?
|
|
62
|
+
if let sourcesData = adsData[SD_PROP_SOURCES] as? [String:Any] {
|
|
63
|
+
srcString = sourcesData[SD_PROP_SRC] as? String
|
|
64
|
+
} else if let sourcesData = adsData[SD_PROP_SOURCES] as? String {
|
|
65
|
+
srcString = sourcesData
|
|
66
|
+
}
|
|
67
|
+
if let src = srcString {
|
|
68
|
+
return GoogleImaAdDescription(src: src, timeOffset: timeOffset)
|
|
69
|
+
} else {
|
|
70
|
+
if DEBUG_SOURCE_DESCRIPTION_BUIDER { print("[NATIVE] AdDescription requires 'src' property in 'ads' description.") }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if DEBUG_SOURCE_DESCRIPTION_BUIDER { print("[NATIVE] We currently require and only support the 'google-ima' integration in the 'ads' description.") }
|
|
74
|
+
#endif
|
|
75
|
+
|
|
76
|
+
return nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
static func buildDAITypedSource(_ typedSourceData: [String:Any], contentProtection: MultiplatformDRMConfiguration?) -> TypedSource? {
|
|
80
|
+
#if GOOGLE_DAI
|
|
81
|
+
// check for alternative Google DAI SSAI
|
|
82
|
+
if let ssaiData = typedSourceData[SD_PROP_SSAI] as? [String:Any] {
|
|
83
|
+
if let integration = ssaiData[SD_PROP_INTEGRATION] as? String,
|
|
84
|
+
integration == SSAIIntegrationId.GoogleDAISSAIIntegrationID._rawValue {
|
|
85
|
+
if let availabilityType = ssaiData[SD_PROP_AVAILABILITY_TYPE] as? String {
|
|
86
|
+
// build a GoogleDAIConfiguration
|
|
87
|
+
var googleDaiConfig: GoogleDAIConfiguration?
|
|
88
|
+
let authToken = ssaiData[SD_PROP_AUTH_TOKEN] as? String
|
|
89
|
+
let streamActivityMonitorID = ssaiData[SD_PROP_STREAM_ACTIVITY_MONITOR_ID] as? String
|
|
90
|
+
let adTagParameters = ssaiData[SD_PROP_AD_TAG_PARAMETERS] as? [String:String]
|
|
91
|
+
let apiKey = ssaiData[SD_PROP_APIKEY] as? String ?? ""
|
|
92
|
+
switch availabilityType {
|
|
93
|
+
case StreamType.vod._rawValue:
|
|
94
|
+
if let videoId = ssaiData[SD_PROP_VIDEOID] as? String,
|
|
95
|
+
let contentSourceID = ssaiData[SD_PROP_CONTENT_SOURCE_ID] as? String {
|
|
96
|
+
googleDaiConfig = GoogleDAIVodConfiguration(videoID: videoId,
|
|
97
|
+
contentSourceID: contentSourceID,
|
|
98
|
+
apiKey: apiKey,
|
|
99
|
+
authToken: authToken,
|
|
100
|
+
streamActivityMonitorID: streamActivityMonitorID,
|
|
101
|
+
adTagParameters: adTagParameters)
|
|
102
|
+
}
|
|
103
|
+
case StreamType.live._rawValue:
|
|
104
|
+
if let assetKey = ssaiData[SD_PROP_ASSET_KEY] as? String {
|
|
105
|
+
googleDaiConfig = GoogleDAILiveConfiguration(assetKey: assetKey,
|
|
106
|
+
apiKey: apiKey,
|
|
107
|
+
authToken: authToken,
|
|
108
|
+
streamActivityMonitorID: streamActivityMonitorID,
|
|
109
|
+
adTagParameters: adTagParameters)
|
|
110
|
+
}
|
|
111
|
+
default:
|
|
112
|
+
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
113
|
+
print("[NATIVE] THEOplayer ssai 'availabilityType' must be 'live' or 'vod'")
|
|
114
|
+
}
|
|
115
|
+
return nil
|
|
116
|
+
}
|
|
117
|
+
// when valid, create a GoogleDAITypedSource from the GoogleDAIConfiguration
|
|
118
|
+
if let config = googleDaiConfig {
|
|
119
|
+
return GoogleDAITypedSource(src: "", type: "application/x-mpegurl",
|
|
120
|
+
drm: contentProtection, ssai: config)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
#endif
|
|
126
|
+
|
|
127
|
+
return nil
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// THEOplayerRCTView+Ads.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import THEOplayerSDK
|
|
5
|
+
|
|
6
|
+
struct AdsConfig {
|
|
7
|
+
var adSUIEnabled: Bool = true
|
|
8
|
+
var googleImaUsesNativeIma: Bool = true
|
|
9
|
+
var adPreloadTypeString: String = "none"
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#if os(iOS)
|
|
13
|
+
|
|
14
|
+
extension THEOplayerRCTView {
|
|
15
|
+
|
|
16
|
+
func parseAdsConfig(configDict: NSDictionary) {
|
|
17
|
+
if let adsConfig = configDict["ads"] as? NSDictionary {
|
|
18
|
+
self.adsConfig.adSUIEnabled = adsConfig["uiEnabled"] as? Bool ?? true
|
|
19
|
+
if let adPreloadType = adsConfig["preload"] as? String {
|
|
20
|
+
self.adsConfig.adPreloadTypeString = adPreloadType
|
|
21
|
+
}
|
|
22
|
+
if let googleImaConfiguration = adsConfig["googleImaConfiguration"] as? NSDictionary {
|
|
23
|
+
self.adsConfig.googleImaUsesNativeIma = googleImaConfiguration["useNativeIma"] as? Bool ?? true
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#if (GOOGLE_IMA || GOOGLE_DAI)
|
|
29
|
+
func playerAdsConfiguration() -> AdsConfiguration? {
|
|
30
|
+
// NOTE: GoogleIMAAdsConfiguration, GoogleDAIAdsConfigurationBuilder, GoogleDAIAdsConfigurationBuilder require iOS SDK 4.5.1 or higher
|
|
31
|
+
let googleIMAAdsConfiguration = GoogleIMAAdsConfiguration()
|
|
32
|
+
googleIMAAdsConfiguration.useNativeIma = self.adsConfig.googleImaUsesNativeIma
|
|
33
|
+
googleIMAAdsConfiguration.disableUI = !self.adsConfig.adSUIEnabled
|
|
34
|
+
let daiBuilder = GoogleDAIAdsConfigurationBuilder()
|
|
35
|
+
daiBuilder.disableUI = !self.adsConfig.adSUIEnabled
|
|
36
|
+
daiBuilder.enableBackgroundPlayback = true
|
|
37
|
+
let googleDaiAdsConfiguration = daiBuilder.build()
|
|
38
|
+
return AdsConfiguration(showCountdown: self.adsConfig.adSUIEnabled,
|
|
39
|
+
preload: self.adPreloadType(),
|
|
40
|
+
googleIma: googleIMAAdsConfiguration,
|
|
41
|
+
googleDai: googleDaiAdsConfiguration)
|
|
42
|
+
|
|
43
|
+
// For lower iOS SDK versions replace the above with:
|
|
44
|
+
/*let googleIMAConfiguration = GoogleIMAConfiguration()
|
|
45
|
+
googleIMAConfiguration.useNativeIma = self.googleImaUsesNativeIma
|
|
46
|
+
googleIMAConfiguration.disableUI = !self.adSUIEnabled
|
|
47
|
+
return AdsConfiguration(showCountdown: self.adSUIEnabled,
|
|
48
|
+
preload: self.adPreloadType,
|
|
49
|
+
googleImaConfiguration: googleIMAConfiguration)*/
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private func adPreloadType() -> THEOplayerSDK.AdPreloadType {
|
|
53
|
+
switch self.adsConfig.adPreloadTypeString {
|
|
54
|
+
case "midroll-and-postroll":
|
|
55
|
+
return THEOplayerSDK.AdPreloadType.MIDROLL_AND_POSTROLL
|
|
56
|
+
case "none":
|
|
57
|
+
return THEOplayerSDK.AdPreloadType.NONE
|
|
58
|
+
default :
|
|
59
|
+
return THEOplayerSDK.AdPreloadType.NONE
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
#else
|
|
63
|
+
func playerAdsConfiguration() -> AdsConfiguration? { return nil }
|
|
64
|
+
#endif
|
|
65
|
+
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
#elseif os(tvOS)
|
|
69
|
+
|
|
70
|
+
extension THEOplayerRCTView {
|
|
71
|
+
|
|
72
|
+
func parseAdsConfig(configDict: NSDictionary) {}
|
|
73
|
+
|
|
74
|
+
#if GOOGLE_IMA
|
|
75
|
+
func playerAdsConfiguration() -> AdsConfiguration? { return AdsConfiguration() }
|
|
76
|
+
#else
|
|
77
|
+
func playerAdsConfiguration() -> AdsConfiguration? { return nil }
|
|
78
|
+
#endif
|
|
79
|
+
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#endif
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// THEOplayerRCTCastAPI+Airplay.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
let ERROR_CODE_AIRPLAY_ACCESS_FAILURE = "airplay_access_failure"
|
|
6
|
+
let ERROR_MESSAGE_AIRPLAY_UNSUPPORTED_FEATURE = "Airplay is not supported by the provided iOS SDK"
|
|
7
|
+
let ERROR_MESSAGE_AIRPLAY_ACCESS_FAILURE = "Could not access THEOplayer Airplay API"
|
|
8
|
+
|
|
9
|
+
extension THEOplayerRCTCastAPI {
|
|
10
|
+
|
|
11
|
+
#if os(iOS)
|
|
12
|
+
|
|
13
|
+
@objc(airplayCasting:resolver:rejecter:)
|
|
14
|
+
func airplayCasting(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
15
|
+
DispatchQueue.main.async {
|
|
16
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
17
|
+
if let cast = theView.cast(),
|
|
18
|
+
let airplay = cast.airPlay {
|
|
19
|
+
resolve(airplay.casting)
|
|
20
|
+
} else {
|
|
21
|
+
reject(ERROR_CODE_AIRPLAY_ACCESS_FAILURE, ERROR_MESSAGE_AIRPLAY_ACCESS_FAILURE, nil)
|
|
22
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not retrieve current airplay casting status.") }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@objc(airplayState:resolver:rejecter:)
|
|
28
|
+
func airplayState(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
29
|
+
DispatchQueue.main.async {
|
|
30
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
31
|
+
if let cast = theView.cast(),
|
|
32
|
+
let airplay = cast.airPlay {
|
|
33
|
+
resolve(airplay.state._rawValue)
|
|
34
|
+
} else {
|
|
35
|
+
reject(ERROR_CODE_AIRPLAY_ACCESS_FAILURE, ERROR_MESSAGE_AIRPLAY_ACCESS_FAILURE, nil)
|
|
36
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not retrieve current airplay state.") }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc(airplayStart:)
|
|
42
|
+
func airplayStart(_ node: NSNumber) -> Void {
|
|
43
|
+
DispatchQueue.main.async {
|
|
44
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
45
|
+
if let cast = theView.cast(),
|
|
46
|
+
let airplay = cast.airPlay {
|
|
47
|
+
if DEBUG_CAST_API { print("[NATIVE] Starting airplay session.") }
|
|
48
|
+
airplay.start()
|
|
49
|
+
} else {
|
|
50
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not start airplay session.") }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@objc(airplayStop:)
|
|
56
|
+
func airplayStop(_ node: NSNumber) -> Void {
|
|
57
|
+
DispatchQueue.main.async {
|
|
58
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
59
|
+
if let cast = theView.cast(),
|
|
60
|
+
let airplay = cast.airPlay {
|
|
61
|
+
if DEBUG_CAST_API { print("[NATIVE] Stopping airplay session.") }
|
|
62
|
+
airplay.stop()
|
|
63
|
+
} else {
|
|
64
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not stop airplay session.") }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#else
|
|
70
|
+
|
|
71
|
+
@objc(airplayCasting:resolver:rejecter:)
|
|
72
|
+
func airplayCasting(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
73
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_AIRPLAY_UNSUPPORTED_FEATURE) }
|
|
74
|
+
resolve(false)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
@objc(airplayState:resolver:rejecter:)
|
|
78
|
+
func airplayState(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
79
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_AIRPLAY_UNSUPPORTED_FEATURE) }
|
|
80
|
+
resolve("unavailable")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@objc(airplayStart:)
|
|
84
|
+
func airplayStart(_ node: NSNumber) -> Void {
|
|
85
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_AIRPLAY_UNSUPPORTED_FEATURE) }
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
@objc(airplayStop:)
|
|
89
|
+
func airplayStop(_ node: NSNumber) -> Void {
|
|
90
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_AIRPLAY_UNSUPPORTED_FEATURE) }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
#endif
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// THEOplayerRCTCastAPI+Chromecast.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
|
|
5
|
+
let ERROR_CODE_CHROMECAST_ACCESS_FAILURE = "chromecast_access_failure"
|
|
6
|
+
let ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE = "Chromecast is not supported by the provided iOS SDK"
|
|
7
|
+
let ERROR_MESSAGE_CHROMECAST_ACCESS_FAILURE = "Could not access THEOplayer Chromecast API"
|
|
8
|
+
|
|
9
|
+
extension THEOplayerRCTCastAPI {
|
|
10
|
+
|
|
11
|
+
#if os(iOS) && CHROMECAST
|
|
12
|
+
|
|
13
|
+
@objc(chromecastCasting:resolver:rejecter:)
|
|
14
|
+
func chromecastCasting(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
15
|
+
DispatchQueue.main.async {
|
|
16
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
17
|
+
if let cast = theView.cast(),
|
|
18
|
+
let chromecast = cast.chromecast {
|
|
19
|
+
resolve(chromecast.casting)
|
|
20
|
+
} else {
|
|
21
|
+
reject(ERROR_CODE_CHROMECAST_ACCESS_FAILURE, ERROR_MESSAGE_CHROMECAST_ACCESS_FAILURE, nil)
|
|
22
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not retrieve current chromecast casting status.") }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@objc(chromecastState:resolver:rejecter:)
|
|
28
|
+
func chromecastState(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
29
|
+
DispatchQueue.main.async {
|
|
30
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
31
|
+
if let cast = theView.cast(),
|
|
32
|
+
let chromecast = cast.chromecast {
|
|
33
|
+
resolve(chromecast.state?._rawValue)
|
|
34
|
+
} else {
|
|
35
|
+
reject(ERROR_CODE_CHROMECAST_ACCESS_FAILURE, ERROR_MESSAGE_CHROMECAST_ACCESS_FAILURE, nil)
|
|
36
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not retrieve current chromecast state.") }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
@objc(chromecastStart:)
|
|
42
|
+
func chromecastStart(_ node: NSNumber) -> Void {
|
|
43
|
+
DispatchQueue.main.async {
|
|
44
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
45
|
+
if let cast = theView.cast(),
|
|
46
|
+
let chromecast = cast.chromecast {
|
|
47
|
+
if DEBUG_CAST_API { print("[NATIVE] Starting chromecast session.") }
|
|
48
|
+
chromecast.start()
|
|
49
|
+
} else {
|
|
50
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not start chromecast session.") }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@objc(chromecastStop:)
|
|
56
|
+
func chromecastStop(_ node: NSNumber) -> Void {
|
|
57
|
+
DispatchQueue.main.async {
|
|
58
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
59
|
+
if let cast = theView.cast(),
|
|
60
|
+
let chromecast = cast.chromecast {
|
|
61
|
+
if DEBUG_CAST_API { print("[NATIVE] Stopping chromecast session.") }
|
|
62
|
+
chromecast.stop()
|
|
63
|
+
} else {
|
|
64
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not stop chromecast session.") }
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
@objc(chromecastJoin:)
|
|
70
|
+
func chromecastJoin(_ node: NSNumber) -> Void {
|
|
71
|
+
DispatchQueue.main.async {
|
|
72
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
73
|
+
if let cast = theView.cast(),
|
|
74
|
+
let chromecast = cast.chromecast {
|
|
75
|
+
if DEBUG_CAST_API { print("[NATIVE] Joining chromecast session.") }
|
|
76
|
+
chromecast.join()
|
|
77
|
+
} else {
|
|
78
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not join chromecast session.") }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@objc(chromecastLeave:)
|
|
84
|
+
func chromecastLeave(_ node: NSNumber) -> Void {
|
|
85
|
+
DispatchQueue.main.async {
|
|
86
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
87
|
+
if let cast = theView.cast(),
|
|
88
|
+
let chromecast = cast.chromecast {
|
|
89
|
+
if DEBUG_CAST_API { print("[NATIVE] Leaving chromecast session.") }
|
|
90
|
+
chromecast.leave()
|
|
91
|
+
} else {
|
|
92
|
+
if DEBUG_CAST_API { print("[NATIVE] Could not leave chromecast session.") }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
#else
|
|
98
|
+
|
|
99
|
+
@objc(chromecastCasting:resolver:rejecter:)
|
|
100
|
+
func chromecastCasting(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
101
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE) }
|
|
102
|
+
resolve(false)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@objc(chromecastState:resolver:rejecter:)
|
|
106
|
+
func chromecastState(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
107
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE) }
|
|
108
|
+
resolve("unavailable")
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@objc(chromecastStart:)
|
|
112
|
+
func chromecastStart(_ node: NSNumber) -> Void {
|
|
113
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE) }
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@objc(chromecastStop:)
|
|
117
|
+
func chromecastStop(_ node: NSNumber) -> Void {
|
|
118
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE) }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@objc(chromecastJoin:)
|
|
122
|
+
func chromecastJoin(_ node: NSNumber) -> Void {
|
|
123
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE) }
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
@objc(chromecastLeave:)
|
|
127
|
+
func chromecastLeave(_ node: NSNumber) -> Void {
|
|
128
|
+
if DEBUG_CAST_API { print(ERROR_MESSAGE_CHROMECAST_UNSUPPORTED_FEATURE) }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
#endif
|
|
132
|
+
}
|