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
|
@@ -494,12 +494,12 @@ class PlayerEventEmitter internal constructor(
|
|
|
494
494
|
val qualityChangedEventType =
|
|
495
495
|
(if (trackType === MediaTrackType.AUDIO)
|
|
496
496
|
AudioTrackEventTypes.ACTIVEQUALITYCHANGEDEVENT
|
|
497
|
-
else VideoTrackEventTypes.ACTIVEQUALITYCHANGEDEVENT) as EventType<
|
|
497
|
+
else VideoTrackEventTypes.ACTIVEQUALITYCHANGEDEVENT) as EventType<ActiveQualityChangedEvent>
|
|
498
498
|
when (eventType) {
|
|
499
|
-
TrackEventType.ADD_TRACK -> track.addEventListener(qualityChangedEventType) { event: QualityChangedEvent
|
|
499
|
+
TrackEventType.ADD_TRACK -> track.addEventListener(qualityChangedEventType) { event: QualityChangedEvent<*, *> ->
|
|
500
500
|
onActiveQualityChanged(event)
|
|
501
501
|
}
|
|
502
|
-
TrackEventType.REMOVE_TRACK -> track.removeEventListener(qualityChangedEventType) { event: QualityChangedEvent
|
|
502
|
+
TrackEventType.REMOVE_TRACK -> track.removeEventListener(qualityChangedEventType) { event: QualityChangedEvent<*, *> ->
|
|
503
503
|
onActiveQualityChanged(event)
|
|
504
504
|
}
|
|
505
505
|
else -> { /* ignore */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import Foundation
|
|
4
4
|
import THEOplayerSDK
|
|
5
5
|
|
|
6
|
-
class
|
|
6
|
+
class THEOplayerRCTMainEventHandler {
|
|
7
7
|
// MARK: Members
|
|
8
8
|
private weak var player: THEOplayer?
|
|
9
9
|
private var currentPresentationMode = THEOplayerSDK.PresentationMode.inline // TheoPlayer's initial presentationMode
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
//
|
|
1
|
+
// THEOplayerRCTMediaTrackEventHandler.swift
|
|
2
2
|
|
|
3
3
|
import Foundation
|
|
4
4
|
import THEOplayerSDK
|
|
5
5
|
|
|
6
|
-
class
|
|
6
|
+
class THEOplayerRCTMediaTrackEventHandler {
|
|
7
7
|
// MARK: Members
|
|
8
8
|
private weak var player: THEOplayer?
|
|
9
9
|
|
|
@@ -7,7 +7,6 @@ import UIKit
|
|
|
7
7
|
let SD_PROP_SOURCES: String = "sources"
|
|
8
8
|
let SD_PROP_POSTER: String = "poster"
|
|
9
9
|
let SD_PROP_TEXTTRACKS: String = "textTracks"
|
|
10
|
-
let SD_PROP_ADS: String = "ads"
|
|
11
10
|
let SD_PROP_METADATA: String = "metadata"
|
|
12
11
|
let SD_PROP_SRC: String = "src"
|
|
13
12
|
let SD_PROP_TYPE: String = "type"
|
|
@@ -115,38 +114,8 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
115
114
|
}
|
|
116
115
|
|
|
117
116
|
// 4. extract Google IMA "ads"
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
#if ADS && GOOGLE_IMA
|
|
121
|
-
if let ads = sourceData[SD_PROP_ADS] {
|
|
122
|
-
adsDescriptions = []
|
|
123
|
-
// case: array of ads objects
|
|
124
|
-
if let adsDataArray = ads as? [[String:Any]] {
|
|
125
|
-
for adsData in adsDataArray {
|
|
126
|
-
if let adDescription = THEOplayerRCTSourceDescriptionBuilder.buildAdDescription(adsData) {
|
|
127
|
-
adsDescriptions?.append(adDescription)
|
|
128
|
-
} else {
|
|
129
|
-
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
130
|
-
print("[NATIVE] Could not create THEOplayer GoogleImaAdDescription from adsData array")
|
|
131
|
-
}
|
|
132
|
-
return nil
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// case: single ads object
|
|
137
|
-
else if let adsData = ads as? [String:Any] {
|
|
138
|
-
if let adDescription = THEOplayerRCTSourceDescriptionBuilder.buildAdDescription(adsData) {
|
|
139
|
-
adsDescriptions?.append(adDescription)
|
|
140
|
-
} else {
|
|
141
|
-
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
142
|
-
print("[NATIVE] Could not create THEOplayer GoogleImaAdDescription from adsData")
|
|
143
|
-
}
|
|
144
|
-
return nil
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
#endif
|
|
149
|
-
|
|
117
|
+
let adsDescriptions: [AdDescription]? = self.buildAdDescriptions(sourceData) // Ads Extension
|
|
118
|
+
|
|
150
119
|
// 5. extract metadata
|
|
151
120
|
var metadataDescription: MetadataDescription?
|
|
152
121
|
if let metadataData = sourceData[SD_PROP_METADATA] as? [String:Any] {
|
|
@@ -168,72 +137,35 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
168
137
|
- returns: a THEOplayer TypedSource. In case of SSAI we support GoogleDAITypedSource with GoogleDAIVodConfiguration or GoogleDAILiveConfiguration
|
|
169
138
|
*/
|
|
170
139
|
private static func buildTypedSource(_ typedSourceData: [String:Any]) -> TypedSource? {
|
|
140
|
+
let contentProtection = extractDrmConfiguration(from: typedSourceData)
|
|
171
141
|
if let src = typedSourceData[SD_PROP_SRC] as? String {
|
|
172
142
|
// extract the type
|
|
173
143
|
let type = typedSourceData[SD_PROP_TYPE] as? String ?? THEOplayerRCTSourceDescriptionBuilder.extractMimeType(src)
|
|
174
|
-
// check for a contentProtection
|
|
175
|
-
var contentProtection: MultiplatformDRMConfiguration?
|
|
176
|
-
if let contentProtectionData = typedSourceData[SD_PROP_CONTENT_PROTECTION] as? [String:Any] {
|
|
177
|
-
let sanitisedContentProtectionData = THEOplayerRCTSourceDescriptionBuilder.sanitiseContentProtectionData(contentProtectionData)
|
|
178
|
-
contentProtection = THEOplayerRCTSourceDescriptionBuilder.buildContentProtection(sanitisedContentProtectionData)
|
|
179
|
-
}
|
|
180
|
-
|
|
181
144
|
return TypedSource(src: src,
|
|
182
145
|
type: type,
|
|
183
146
|
drm: contentProtection)
|
|
184
147
|
}
|
|
185
148
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if let integration = ssaiData[SD_PROP_INTEGRATION] as? String,
|
|
190
|
-
integration == SSAIIntegrationId.GoogleDAISSAIIntegrationID._rawValue {
|
|
191
|
-
if let availabilityType = ssaiData[SD_PROP_AVAILABILITY_TYPE] as? String {
|
|
192
|
-
// build a GoogleDAIConfiguration
|
|
193
|
-
var googleDaiConfig: GoogleDAIConfiguration?
|
|
194
|
-
let authToken = ssaiData[SD_PROP_AUTH_TOKEN] as? String
|
|
195
|
-
let streamActivityMonitorID = ssaiData[SD_PROP_STREAM_ACTIVITY_MONITOR_ID] as? String
|
|
196
|
-
let adTagParameters = ssaiData[SD_PROP_AD_TAG_PARAMETERS] as? [String:String]
|
|
197
|
-
let apiKey = ssaiData[SD_PROP_APIKEY] as? String ?? ""
|
|
198
|
-
switch availabilityType {
|
|
199
|
-
case StreamType.vod._rawValue:
|
|
200
|
-
if let videoId = ssaiData[SD_PROP_VIDEOID] as? String,
|
|
201
|
-
let contentSourceID = ssaiData[SD_PROP_CONTENT_SOURCE_ID] as? String {
|
|
202
|
-
googleDaiConfig = GoogleDAIVodConfiguration(videoID: videoId,
|
|
203
|
-
contentSourceID: contentSourceID,
|
|
204
|
-
apiKey: apiKey,
|
|
205
|
-
authToken: authToken,
|
|
206
|
-
streamActivityMonitorID: streamActivityMonitorID,
|
|
207
|
-
adTagParameters: adTagParameters)
|
|
208
|
-
}
|
|
209
|
-
case StreamType.live._rawValue:
|
|
210
|
-
if let assetKey = ssaiData[SD_PROP_ASSET_KEY] as? String {
|
|
211
|
-
googleDaiConfig = GoogleDAILiveConfiguration(assetKey: assetKey,
|
|
212
|
-
apiKey: apiKey,
|
|
213
|
-
authToken: authToken,
|
|
214
|
-
streamActivityMonitorID: streamActivityMonitorID,
|
|
215
|
-
adTagParameters: adTagParameters)
|
|
216
|
-
}
|
|
217
|
-
default:
|
|
218
|
-
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
219
|
-
print("[NATIVE] THEOplayer ssai 'availabilityType' must be 'live' or 'vod'")
|
|
220
|
-
}
|
|
221
|
-
return nil
|
|
222
|
-
}
|
|
223
|
-
// when valid, create a GoogleDAITypedSource from the GoogleDAIConfiguration
|
|
224
|
-
if let config = googleDaiConfig {
|
|
225
|
-
return GoogleDAITypedSource(ssai: config)
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
149
|
+
// Check if we can extract a DAI source
|
|
150
|
+
if let daiSource = self.buildDAITypedSource(typedSourceData, contentProtection: contentProtection) {
|
|
151
|
+
return daiSource
|
|
229
152
|
}
|
|
230
|
-
|
|
153
|
+
|
|
231
154
|
if DEBUG_SOURCE_DESCRIPTION_BUIDER {
|
|
232
155
|
print("[NATIVE] THEOplayer TypedSource requires 'src' property in 'sources' description")
|
|
233
156
|
}
|
|
234
157
|
return nil
|
|
235
158
|
}
|
|
236
159
|
|
|
160
|
+
private static func extractDrmConfiguration(from typedSourceData: [String:Any]) -> MultiplatformDRMConfiguration? {
|
|
161
|
+
// check for a contentProtection
|
|
162
|
+
guard let contentProtectionData = typedSourceData[SD_PROP_CONTENT_PROTECTION] as? [String:Any] else {
|
|
163
|
+
return nil
|
|
164
|
+
}
|
|
165
|
+
let sanitisedContentProtectionData = THEOplayerRCTSourceDescriptionBuilder.sanitiseContentProtectionData(contentProtectionData)
|
|
166
|
+
return THEOplayerRCTSourceDescriptionBuilder.buildContentProtection(sanitisedContentProtectionData)
|
|
167
|
+
}
|
|
168
|
+
|
|
237
169
|
/**
|
|
238
170
|
Creates a THEOplayer TextTrackDescription. This requires a textTracks property in the RN source description.
|
|
239
171
|
- returns: a THEOplayer TextTrackDescription
|
|
@@ -255,33 +187,6 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
255
187
|
return nil
|
|
256
188
|
}
|
|
257
189
|
|
|
258
|
-
#if ADS && GOOGLE_IMA
|
|
259
|
-
/**
|
|
260
|
-
Creates a THEOplayer GoogleImaAdDescription. This requires an ads property in the RN source description.
|
|
261
|
-
- returns: a THEOplayer GoogleImaAdDescription
|
|
262
|
-
*/
|
|
263
|
-
static func buildAdDescription(_ adsData: [String:Any]) -> AdDescription? {
|
|
264
|
-
if let integration = adsData[SD_PROP_INTEGRATION] as? String,
|
|
265
|
-
integration == AdIntegration.google_ima._rawValue {
|
|
266
|
-
// timeOffset can be Int or String: 10, "01:32:54.78", "1234.56", "start", "end", "10%", ...
|
|
267
|
-
let timeOffset = adsData[SD_PROP_TIME_OFFSET] as? String ?? String(adsData[SD_PROP_TIME_OFFSET] as? Int ?? 0)
|
|
268
|
-
var srcString: String?
|
|
269
|
-
if let sourcesData = adsData[SD_PROP_SOURCES] as? [String:Any] {
|
|
270
|
-
srcString = sourcesData[SD_PROP_SRC] as? String
|
|
271
|
-
} else if let sourcesData = adsData[SD_PROP_SOURCES] as? String {
|
|
272
|
-
srcString = sourcesData
|
|
273
|
-
}
|
|
274
|
-
if let src = srcString {
|
|
275
|
-
return GoogleImaAdDescription(src: src, timeOffset: timeOffset)
|
|
276
|
-
} else {
|
|
277
|
-
if DEBUG_SOURCE_DESCRIPTION_BUIDER { print("[NATIVE] AdDescription requires 'src' property in 'ads' description.") }
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
if DEBUG_SOURCE_DESCRIPTION_BUIDER { print("[NATIVE] We currently require and only support the 'google-ima' integration in the 'ads' description.") }
|
|
281
|
-
return nil
|
|
282
|
-
}
|
|
283
|
-
#endif
|
|
284
|
-
|
|
285
190
|
/**
|
|
286
191
|
Creates a THEOplayer MetadataDescription. This requires a metadata property in the RN source description.
|
|
287
192
|
- returns: a THEOplayer MetadataDescription
|
|
@@ -314,7 +219,7 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
314
219
|
return MetadataDescription(metadataKeys: metadataData, title: title)
|
|
315
220
|
#endif
|
|
316
221
|
}
|
|
317
|
-
|
|
222
|
+
|
|
318
223
|
#if os(iOS)
|
|
319
224
|
static func buildChromecastMetadataType(_ metadataType: String?) -> ChromecastMetadataType {
|
|
320
225
|
guard let typeString = metadataType else {
|
|
@@ -329,7 +234,7 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
329
234
|
default: return ChromecastMetadataType.GENERIC
|
|
330
235
|
}
|
|
331
236
|
}
|
|
332
|
-
|
|
237
|
+
|
|
333
238
|
static func buildChromecastMetaDataImagesArray(_ metadataImagesArrayData: [[String:Any]]) -> [ChromecastMetadataImage]? {
|
|
334
239
|
var images: [ChromecastMetadataImage] = []
|
|
335
240
|
for metadataImageData in metadataImagesArrayData {
|
|
@@ -339,7 +244,7 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
339
244
|
}
|
|
340
245
|
return images.count > 0 ? images : nil
|
|
341
246
|
}
|
|
342
|
-
|
|
247
|
+
|
|
343
248
|
static func buildChromecastMetaDataImage(_ metadataImageData: [String:Any]) -> ChromecastMetadataImage? {
|
|
344
249
|
if let src = metadataImageData[SD_PROP_SRC] as? String,
|
|
345
250
|
let width = metadataImageData[SD_PROP_METADATA_IMAGE_WIDTH] as? Int,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
//
|
|
1
|
+
// THEOplayerRCTTextTrackEventHandler.swift
|
|
2
2
|
|
|
3
3
|
import Foundation
|
|
4
4
|
import THEOplayerSDK
|
|
5
5
|
|
|
6
|
-
class
|
|
6
|
+
class THEOplayerRCTTextTrackEventHandler {
|
|
7
7
|
// MARK: Members
|
|
8
8
|
private weak var player: THEOplayer?
|
|
9
9
|
|
|
@@ -3,19 +3,17 @@
|
|
|
3
3
|
import Foundation
|
|
4
4
|
import UIKit
|
|
5
5
|
import THEOplayerSDK
|
|
6
|
-
|
|
7
|
-
#if CHROMECAST
|
|
8
|
-
import GoogleCast
|
|
9
|
-
#endif
|
|
10
6
|
|
|
11
7
|
class THEOplayerRCTView: UIView {
|
|
12
8
|
// MARK: Members
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
var player: THEOplayer?
|
|
10
|
+
var mainEventHandler: THEOplayerRCTMainEventHandler
|
|
11
|
+
var textTrackEventHandler: THEOplayerRCTTextTrackEventHandler
|
|
12
|
+
var mediaTrackEventHandler: THEOplayerRCTMediaTrackEventHandler
|
|
13
|
+
var adEventHandler: THEOplayerRCTAdsEventHandler
|
|
14
|
+
var castEventHandler: THEOplayerRCTCastEventHandler
|
|
15
|
+
var adsConfig = AdsConfig()
|
|
16
|
+
var castConfig = CastConfig()
|
|
19
17
|
|
|
20
18
|
// MARK: Bridged props
|
|
21
19
|
private var src: SourceDescription?
|
|
@@ -31,25 +29,15 @@ class THEOplayerRCTView: UIView {
|
|
|
31
29
|
private var selectedAudioTrackUid: Int = 0
|
|
32
30
|
private var seek: Double? = nil // in msec
|
|
33
31
|
private var fullscreen: Bool = false
|
|
34
|
-
private var chromecastReceiverApplicationId: String?
|
|
35
|
-
|
|
36
|
-
#if os(iOS)
|
|
37
|
-
private var castStrategy: THEOplayerSDK.CastStrategy = THEOplayerSDK.CastStrategy.manual
|
|
38
|
-
#endif
|
|
39
|
-
#if os(iOS) && ADS && (GOOGLE_IMA || GOOGLE_DAI)
|
|
40
|
-
private var adSUIEnabled: Bool = true
|
|
41
|
-
private var googleImaUsesNativeIma: Bool = true
|
|
42
|
-
private var adPreloadType: THEOplayerSDK.AdPreloadType = .MIDROLL_AND_POSTROLL
|
|
43
|
-
#endif
|
|
44
32
|
|
|
45
33
|
// MARK: - Initialisation / view setup
|
|
46
34
|
init() {
|
|
47
35
|
// create event handlers to maintain event props
|
|
48
|
-
self.mainEventHandler =
|
|
49
|
-
self.textTrackEventHandler =
|
|
50
|
-
self.mediaTrackEventHandler =
|
|
51
|
-
self.adEventHandler =
|
|
52
|
-
self.castEventHandler =
|
|
36
|
+
self.mainEventHandler = THEOplayerRCTMainEventHandler()
|
|
37
|
+
self.textTrackEventHandler = THEOplayerRCTTextTrackEventHandler()
|
|
38
|
+
self.mediaTrackEventHandler = THEOplayerRCTMediaTrackEventHandler()
|
|
39
|
+
self.adEventHandler = THEOplayerRCTAdsEventHandler()
|
|
40
|
+
self.castEventHandler = THEOplayerRCTCastEventHandler()
|
|
53
41
|
|
|
54
42
|
super.init(frame: .zero)
|
|
55
43
|
}
|
|
@@ -66,22 +54,6 @@ class THEOplayerRCTView: UIView {
|
|
|
66
54
|
if DEBUG_THEOPLAYER_INTERACTION { print("[NATIVE] THEOplayer instance destroyed.") }
|
|
67
55
|
}
|
|
68
56
|
|
|
69
|
-
func ads() -> Ads? {
|
|
70
|
-
guard let player = self.player else {
|
|
71
|
-
return nil
|
|
72
|
-
}
|
|
73
|
-
return player.ads
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
#if os(iOS)
|
|
77
|
-
func cast() -> Cast? {
|
|
78
|
-
guard let player = self.player else {
|
|
79
|
-
return nil
|
|
80
|
-
}
|
|
81
|
-
return player.cast
|
|
82
|
-
}
|
|
83
|
-
#endif
|
|
84
|
-
|
|
85
57
|
required init?(coder aDecoder: NSCoder) {
|
|
86
58
|
fatalError("[NATIVE] init(coder:) has not been implemented")
|
|
87
59
|
}
|
|
@@ -122,14 +94,8 @@ class THEOplayerRCTView: UIView {
|
|
|
122
94
|
return
|
|
123
95
|
}
|
|
124
96
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
isCasting = self.player?.cast?.casting ?? false // TO FIX: remove 'isCasting' workaround
|
|
128
|
-
#endif
|
|
129
|
-
|
|
130
|
-
if !isCasting || self.player == nil {
|
|
131
|
-
self.player?.destroy()
|
|
132
|
-
self.player = nil
|
|
97
|
+
if !self.isCasting() || self.player == nil {
|
|
98
|
+
self.destroy()
|
|
133
99
|
DispatchQueue.main.async {
|
|
134
100
|
self.initPlayer()
|
|
135
101
|
if let player = self.player {
|
|
@@ -151,61 +117,24 @@ class THEOplayerRCTView: UIView {
|
|
|
151
117
|
|
|
152
118
|
private func initPlayer() {
|
|
153
119
|
if DEBUG_THEOPLAYER_INTERACTION { print("[NATIVE] 'lazy' init THEOplayer instance") }
|
|
154
|
-
#if os(
|
|
155
|
-
self.player = THEOplayer(configuration: THEOplayerConfiguration(chromeless: self.chromeless,
|
|
156
|
-
ads: self.initAdsConfiguration(),
|
|
157
|
-
license: self.license,
|
|
158
|
-
licenseUrl: self.licenseUrl,
|
|
159
|
-
pip: nil))
|
|
160
|
-
#else
|
|
120
|
+
#if os(iOS)
|
|
161
121
|
let stylePath = Bundle.main.path(forResource:"style", ofType: "css")
|
|
162
122
|
let cssPaths = stylePath != nil ? [stylePath!] : []
|
|
163
123
|
self.player = THEOplayer(configuration: THEOplayerConfiguration(chromeless: self.chromeless,
|
|
164
124
|
cssPaths: cssPaths,
|
|
165
125
|
pip: nil,
|
|
166
|
-
ads: self.
|
|
167
|
-
cast: self.
|
|
126
|
+
ads: self.playerAdsConfiguration(),
|
|
127
|
+
cast: self.playerCastConfiguration(),
|
|
168
128
|
license: self.license,
|
|
169
129
|
licenseUrl: self.licenseUrl))
|
|
170
|
-
#endif
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
private func initAdsConfiguration() -> AdsConfiguration? {
|
|
174
|
-
#if os(iOS) && ADS && (GOOGLE_IMA || GOOGLE_DAI)
|
|
175
|
-
// NOTE: GoogleIMAAdsConfiguration, GoogleDAIAdsConfigurationBuilder, GoogleDAIAdsConfigurationBuilder require iOS SDK 4.5.1 or higher
|
|
176
|
-
let googleIMAAdsConfiguration = GoogleIMAAdsConfiguration()
|
|
177
|
-
googleIMAAdsConfiguration.useNativeIma = self.googleImaUsesNativeIma
|
|
178
|
-
googleIMAAdsConfiguration.disableUI = !self.adSUIEnabled
|
|
179
|
-
let daiBuilder = GoogleDAIAdsConfigurationBuilder()
|
|
180
|
-
daiBuilder.disableUI = !self.adSUIEnabled
|
|
181
|
-
daiBuilder.enableBackgroundPlayback = true
|
|
182
|
-
let googleDaiAdsConfiguration = daiBuilder.build()
|
|
183
|
-
return AdsConfiguration(showCountdown: self.adSUIEnabled,
|
|
184
|
-
preload: self.adPreloadType,
|
|
185
|
-
googleIma: googleIMAAdsConfiguration,
|
|
186
|
-
googleDai: googleDaiAdsConfiguration)
|
|
187
|
-
|
|
188
|
-
// For lower iOS SDK versions replace the above with:
|
|
189
|
-
/*let googleIMAConfiguration = GoogleIMAConfiguration()
|
|
190
|
-
googleIMAConfiguration.useNativeIma = self.googleImaUsesNativeIma
|
|
191
|
-
googleIMAConfiguration.disableUI = !self.adSUIEnabled
|
|
192
|
-
return AdsConfiguration(showCountdown: self.adSUIEnabled,
|
|
193
|
-
preload: self.adPreloadType,
|
|
194
|
-
googleImaConfiguration: googleIMAConfiguration)*/
|
|
195
|
-
|
|
196
|
-
#elseif os(tvOS) && ADS && GOOGLE_IMA
|
|
197
|
-
return AdsConfiguration()
|
|
198
130
|
#else
|
|
199
|
-
|
|
131
|
+
self.player = THEOplayer(configuration: THEOplayerConfiguration(chromeless: self.chromeless,
|
|
132
|
+
ads: self.playerAdsConfiguration(),
|
|
133
|
+
license: self.license,
|
|
134
|
+
licenseUrl: self.licenseUrl,
|
|
135
|
+
pip: nil))
|
|
200
136
|
#endif
|
|
201
137
|
}
|
|
202
|
-
|
|
203
|
-
#if os(iOS)
|
|
204
|
-
private func initCastConfiguration() -> CastConfiguration? {
|
|
205
|
-
// prepare the config
|
|
206
|
-
return CastConfiguration(strategy: self.castStrategy)
|
|
207
|
-
}
|
|
208
|
-
#endif
|
|
209
138
|
|
|
210
139
|
private func syncPlayerSrc() {
|
|
211
140
|
// set sourceDescription on player
|
|
@@ -221,37 +150,8 @@ class THEOplayerRCTView: UIView {
|
|
|
221
150
|
self.license = configDict["license"] as? String
|
|
222
151
|
self.licenseUrl = configDict["licenseUrl"] as? String
|
|
223
152
|
self.chromeless = configDict["chromeless"] as? Bool ?? true
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
self.adSUIEnabled = adsConfig["uiEnabled"] as? Bool ?? true
|
|
227
|
-
if let adPreloadType = adsConfig["preload"] as? String {
|
|
228
|
-
self.adPreloadType = adPreloadType == "none" ? THEOplayerSDK.AdPreloadType.NONE : THEOplayerSDK.AdPreloadType.MIDROLL_AND_POSTROLL
|
|
229
|
-
}
|
|
230
|
-
if let googleImaConfiguration = adsConfig["googleImaConfiguration"] as? NSDictionary {
|
|
231
|
-
self.googleImaUsesNativeIma = googleImaConfiguration["useNativeIma"] as? Bool ?? true
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
#endif
|
|
235
|
-
#if os(iOS)
|
|
236
|
-
if let castConfig = configDict["cast"] as? NSDictionary {
|
|
237
|
-
if let castStrategy = castConfig["strategy"] as? String {
|
|
238
|
-
switch castStrategy {
|
|
239
|
-
case "auto":
|
|
240
|
-
self.castStrategy = THEOplayerSDK.CastStrategy.auto
|
|
241
|
-
case "manual":
|
|
242
|
-
self.castStrategy = THEOplayerSDK.CastStrategy.manual
|
|
243
|
-
case "disabled":
|
|
244
|
-
self.castStrategy = THEOplayerSDK.CastStrategy.disabled
|
|
245
|
-
default :
|
|
246
|
-
self.castStrategy = THEOplayerSDK.CastStrategy.manual
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
if let chromecastConfig = castConfig["chromecast"] as? NSDictionary,
|
|
250
|
-
let castReceiverApplicationId = chromecastConfig["appID"] as? String {
|
|
251
|
-
self.chromecastReceiverApplicationId = castReceiverApplicationId
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
#endif
|
|
153
|
+
self.parseAdsConfig(configDict: configDict)
|
|
154
|
+
self.parseCastConfig(configDict: configDict)
|
|
255
155
|
if DEBUG_PROP_UPDATES { print("[NATIVE] config prop updated.") }
|
|
256
156
|
}
|
|
257
157
|
|
|
@@ -571,14 +471,6 @@ class THEOplayerRCTView: UIView {
|
|
|
571
471
|
if DEBUG_PROP_UPDATES { print("[NATIVE] nativeMediaTrackEvent prop set.") }
|
|
572
472
|
}
|
|
573
473
|
|
|
574
|
-
// MARK: - Listener based AD event bridging
|
|
575
|
-
|
|
576
|
-
@objc(setOnNativeAdEvent:)
|
|
577
|
-
func setOnNativeAdEvent(nativeAdEvent: @escaping RCTDirectEventBlock) {
|
|
578
|
-
self.adEventHandler.onNativeAdEvent = nativeAdEvent
|
|
579
|
-
if DEBUG_PROP_UPDATES { print("[NATIVE] nativeAdEvent prop set.") }
|
|
580
|
-
}
|
|
581
|
-
|
|
582
474
|
// MARK: - Listener based CAST event bridging
|
|
583
475
|
|
|
584
476
|
@objc(setOnNativeCastEvent:)
|
|
@@ -586,4 +478,12 @@ class THEOplayerRCTView: UIView {
|
|
|
586
478
|
self.castEventHandler.onNativeCastEvent = nativeCastEvent
|
|
587
479
|
if DEBUG_PROP_UPDATES { print("[NATIVE] nativeCastEvent prop set.") }
|
|
588
480
|
}
|
|
481
|
+
|
|
482
|
+
// MARK: - Listener based AD event bridging
|
|
483
|
+
|
|
484
|
+
@objc(setOnNativeAdEvent:)
|
|
485
|
+
func setOnNativeAdEvent(nativeAdEvent: @escaping RCTDirectEventBlock) {
|
|
486
|
+
self.adEventHandler.onNativeAdEvent = nativeAdEvent
|
|
487
|
+
if DEBUG_PROP_UPDATES { print("[NATIVE] nativeAdEvent prop set.") }
|
|
488
|
+
}
|
|
589
489
|
}
|
|
@@ -40,7 +40,7 @@ let PROP_COMPANION_RESOURCE_URI: String = "resourceURI"
|
|
|
40
40
|
|
|
41
41
|
class THEOplayerRCTAdAggregator {
|
|
42
42
|
|
|
43
|
-
#if
|
|
43
|
+
#if (GOOGLE_IMA || GOOGLE_DAI)
|
|
44
44
|
class func aggregateAd(ad: Ad, processAdBreak: Bool = true) -> [String:Any] {
|
|
45
45
|
var adData: [String:Any] = [:]
|
|
46
46
|
adData[PROP_AD_INTEGRATION] = ad.integration._rawValue
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// THEOplayerRCTAdsAPI+DAI.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import UIKit
|
|
5
|
+
|
|
6
|
+
let ERROR_CODE_DAI_ACCESS_FAILURE = "dai_access_failure"
|
|
7
|
+
let ERROR_CODE_DAI_GET_SNAPBACK_FAILED = "dai_get_snapback_failed"
|
|
8
|
+
let ERROR_CODE_DAI_GET_SNAPBACK_UNDEFINED = "dai_get_snapback_undefined"
|
|
9
|
+
let ERROR_MESSAGE_DAI_ACCESS_FAILURE = "Could not access THEOplayer Ads DAI Module"
|
|
10
|
+
let ERROR_MESSAGE_DAI_GET_SNAPBACK_UNDEFINED = "Undefined dai snapback"
|
|
11
|
+
|
|
12
|
+
extension THEOplayerRCTAdsAPI {
|
|
13
|
+
|
|
14
|
+
#if GOOGLE_DAI
|
|
15
|
+
@objc(daiSnapback:resolver:rejecter:)
|
|
16
|
+
func daiSnapback(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
17
|
+
DispatchQueue.main.async {
|
|
18
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
19
|
+
if let ads = theView.ads(),
|
|
20
|
+
let dai = ads.dai {
|
|
21
|
+
dai.requestSnapBack { enabled, error in
|
|
22
|
+
if let err = error {
|
|
23
|
+
reject(ERROR_CODE_DAI_GET_SNAPBACK_FAILED, err.localizedDescription, error)
|
|
24
|
+
if DEBUG_ADS_API { print("[NATIVE] Retrieving dai snapback status failed: \(err.localizedDescription)") }
|
|
25
|
+
} else if let snapBack = enabled {
|
|
26
|
+
resolve(snapBack)
|
|
27
|
+
} else {
|
|
28
|
+
reject(ERROR_CODE_DAI_GET_SNAPBACK_UNDEFINED, ERROR_MESSAGE_DAI_GET_SNAPBACK_UNDEFINED, nil)
|
|
29
|
+
if DEBUG_ADS_API { print("[NATIVE] Retrieving dai snapback status failed.") }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
} else {
|
|
33
|
+
reject(ERROR_CODE_DAI_ACCESS_FAILURE, ERROR_MESSAGE_DAI_ACCESS_FAILURE, nil)
|
|
34
|
+
if DEBUG_ADS_API { print("[NATIVE] Could not retrieve dai snapback status (ads DAI module unavailable).") }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@objc(daiSetSnapback:enabled:)
|
|
40
|
+
func daiSetSnapback(_ node: NSNumber, enabled: Bool) -> Void {
|
|
41
|
+
DispatchQueue.main.async {
|
|
42
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
43
|
+
if let ads = theView.ads(),
|
|
44
|
+
let dai = ads.dai {
|
|
45
|
+
dai.setSnapBack(enabled, completionHandler: nil)
|
|
46
|
+
} else {
|
|
47
|
+
if DEBUG_ADS_API { print("[NATIVE] Could not update dai snapback status (ads DAI module unavailable).") }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
@objc(daiContentTimeForStreamTime:time:resolver:rejecter:)
|
|
53
|
+
func daiContentTimeForStreamTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
54
|
+
DispatchQueue.main.async {
|
|
55
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
56
|
+
if let ads = theView.ads(),
|
|
57
|
+
let dai = ads.dai {
|
|
58
|
+
let streamTime = timeValue.doubleValue * 0.001 // msec -> sec
|
|
59
|
+
let contentTime = dai.contentTime(from: streamTime) * 1000.0 // sec -> msec
|
|
60
|
+
resolve(contentTime)
|
|
61
|
+
} else {
|
|
62
|
+
reject(ERROR_CODE_DAI_ACCESS_FAILURE, ERROR_MESSAGE_DAI_ACCESS_FAILURE, nil)
|
|
63
|
+
if DEBUG_ADS_API { print("[NATIVE] Could not convert stream time to content time (ads DAI module unavailable).") }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@objc(daiStreamTimeForContentTime:time:resolver:rejecter:)
|
|
69
|
+
func daiStreamTimeForContentTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
70
|
+
DispatchQueue.main.async {
|
|
71
|
+
let theView = self.bridge.uiManager.view(forReactTag: node) as! THEOplayerRCTView
|
|
72
|
+
if let ads = theView.ads(),
|
|
73
|
+
let dai = ads.dai {
|
|
74
|
+
let contentTime = timeValue.doubleValue * 0.001 // msec -> sec
|
|
75
|
+
let streamTime = dai.streamTime(from: contentTime) * 1000.0 // sec -> msec
|
|
76
|
+
resolve(streamTime)
|
|
77
|
+
} else {
|
|
78
|
+
reject(ERROR_CODE_DAI_ACCESS_FAILURE, ERROR_MESSAGE_DAI_ACCESS_FAILURE, nil)
|
|
79
|
+
if DEBUG_ADS_API { print("[NATIVE] Could not convert content time to stream time (ads DAI module unavailable).") }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
#else
|
|
85
|
+
|
|
86
|
+
@objc(daiSnapback:resolver:rejecter:)
|
|
87
|
+
func daiSnapback(_ node: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
88
|
+
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
89
|
+
resolve(false)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@objc(daiContentTimeForStreamTime:time:resolver:rejecter:)
|
|
93
|
+
func daiContentTimeForStreamTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
94
|
+
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
95
|
+
resolve(timeValue.doubleValue)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@objc(daiStreamTimeForContentTime:time:resolver:rejecter:)
|
|
99
|
+
func daiStreamTimeForContentTime(_ node: NSNumber, timeValue: NSNumber, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) -> Void {
|
|
100
|
+
if DEBUG_ADS_API { print(ERROR_MESSAGE_ADS_UNSUPPORTED_FEATURE) }
|
|
101
|
+
resolve(timeValue.doubleValue)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
#endif
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|