react-native-theoplayer 11.2.0 → 11.3.0
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/CHANGELOG.md +20 -0
- package/android/build.gradle +2 -2
- package/android/src/main/java/com/theoplayer/PlayerConfigAdapter.kt +30 -0
- package/android/src/main/java/com/theoplayer/abr/ABRConfigurationAdapter.kt +21 -0
- package/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +38 -1
- package/android/src/main/java/com/theoplayer/track/TrackListAdapter.kt +2 -0
- package/ios/THEOplayerRCTPlayerAPI.swift +29 -13
- package/ios/THEOplayerRCTSourceDescriptionBuilder.swift +31 -6
- package/ios/THEOplayerRCTTrackMetadataAggregator.swift +35 -16
- package/ios/THEOplayerRCTView.swift +3 -0
- package/ios/cmcd/THEOplayerRCTView+CmcdConfig.swift +36 -0
- package/lib/commonjs/api/barrel.js +36 -25
- package/lib/commonjs/api/barrel.js.map +1 -1
- package/lib/commonjs/api/cmcd/CmcdConfiguration.js +67 -0
- package/lib/commonjs/api/cmcd/CmcdConfiguration.js.map +1 -0
- package/lib/commonjs/api/cmcd/barrel.js.map +1 -0
- package/lib/commonjs/api/source/barrel.js +5 -16
- package/lib/commonjs/api/source/barrel.js.map +1 -1
- package/lib/commonjs/api/track/TextTrack.js.map +1 -1
- package/lib/commonjs/internal/adapter/THEOplayerWebAdapter.js +13 -4
- package/lib/commonjs/internal/adapter/THEOplayerWebAdapter.js.map +1 -1
- package/lib/commonjs/internal/adapter/web/TrackUtils.js +2 -0
- package/lib/commonjs/internal/adapter/web/TrackUtils.js.map +1 -1
- package/lib/commonjs/manifest.json +1 -1
- package/lib/module/api/barrel.js +1 -0
- package/lib/module/api/barrel.js.map +1 -1
- package/lib/module/api/cmcd/CmcdConfiguration.js +67 -0
- package/lib/module/api/cmcd/CmcdConfiguration.js.map +1 -0
- package/lib/module/api/cmcd/barrel.js.map +1 -0
- package/lib/module/api/source/barrel.js +0 -1
- package/lib/module/api/source/barrel.js.map +1 -1
- package/lib/module/api/track/TextTrack.js.map +1 -1
- package/lib/module/internal/adapter/THEOplayerWebAdapter.js +13 -4
- package/lib/module/internal/adapter/THEOplayerWebAdapter.js.map +1 -1
- package/lib/module/internal/adapter/web/TrackUtils.js +2 -0
- package/lib/module/internal/adapter/web/TrackUtils.js.map +1 -1
- package/lib/module/manifest.json +1 -1
- package/lib/typescript/api/abr/ABRConfiguration.d.ts +2 -2
- package/lib/typescript/api/barrel.d.ts +1 -0
- package/lib/typescript/api/barrel.d.ts.map +1 -1
- package/lib/typescript/api/cmcd/CmcdConfiguration.d.ts +166 -0
- package/lib/typescript/api/cmcd/CmcdConfiguration.d.ts.map +1 -0
- package/lib/typescript/api/cmcd/barrel.d.ts.map +1 -0
- package/lib/typescript/api/config/PlayerConfiguration.d.ts +10 -0
- package/lib/typescript/api/config/PlayerConfiguration.d.ts.map +1 -1
- package/lib/typescript/api/source/SourceDescription.d.ts +2 -2
- package/lib/typescript/api/source/SourceDescription.d.ts.map +1 -1
- package/lib/typescript/api/source/barrel.d.ts +0 -1
- package/lib/typescript/api/source/barrel.d.ts.map +1 -1
- package/lib/typescript/api/track/TextTrack.d.ts +4 -0
- package/lib/typescript/api/track/TextTrack.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/THEOplayerWebAdapter.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/web/TrackUtils.d.ts.map +1 -1
- package/package.json +1 -1
- package/react-native-theoplayer.podspec +1 -1
- package/src/api/abr/ABRConfiguration.ts +2 -2
- package/src/api/barrel.ts +1 -0
- package/src/api/cmcd/CmcdConfiguration.ts +175 -0
- package/src/api/config/PlayerConfiguration.ts +11 -0
- package/src/api/source/SourceDescription.ts +2 -2
- package/src/api/source/barrel.ts +0 -1
- package/src/api/track/TextTrack.ts +5 -0
- package/src/internal/adapter/THEOplayerWebAdapter.ts +9 -4
- package/src/internal/adapter/web/TrackUtils.ts +2 -1
- package/src/manifest.json +1 -1
- package/lib/commonjs/api/source/cmcd/CmcdConfiguration.js +0 -27
- package/lib/commonjs/api/source/cmcd/CmcdConfiguration.js.map +0 -1
- package/lib/commonjs/api/source/cmcd/barrel.js.map +0 -1
- package/lib/module/api/source/cmcd/CmcdConfiguration.js +0 -24
- package/lib/module/api/source/cmcd/CmcdConfiguration.js.map +0 -1
- package/lib/module/api/source/cmcd/barrel.js.map +0 -1
- package/lib/typescript/api/source/cmcd/CmcdConfiguration.d.ts +0 -83
- package/lib/typescript/api/source/cmcd/CmcdConfiguration.d.ts.map +0 -1
- package/lib/typescript/api/source/cmcd/barrel.d.ts.map +0 -1
- package/src/api/source/cmcd/CmcdConfiguration.ts +0 -88
- /package/lib/commonjs/api/{source/cmcd → cmcd}/barrel.js +0 -0
- /package/lib/module/api/{source/cmcd → cmcd}/barrel.js +0 -0
- /package/lib/typescript/api/{source/cmcd → cmcd}/barrel.d.ts +0 -0
- /package/src/api/{source/cmcd → cmcd}/barrel.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.0/)
|
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [11.3.0] - 26-06-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Added Android support for `ABRConfiguration.preferredMaximumResolution`, mirroring the existing iOS behaviour. Set to `(0,0)` to remove the cap.
|
|
15
|
+
- Added basic support for CMCD event mode reporting of DRM and ad events.
|
|
16
|
+
- Added bridging of the `hlsDateRange` property from `PlayerConfiguration` to Android's `THEOplayerConfig`.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Fixed an issue on Android where `GoogleImaConfiguration.sessionId` was not properly passed.
|
|
21
|
+
|
|
22
|
+
## [11.2.1] - 26-06-09
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
|
|
26
|
+
- Fixed an issue on iOS where setting the targetVideoQuality uids was causing a crash due to an unsafe bounds-check.
|
|
27
|
+
|
|
8
28
|
## [11.2.0] - 26-06-04
|
|
9
29
|
|
|
10
30
|
### Added
|
package/android/build.gradle
CHANGED
|
@@ -126,8 +126,8 @@ repositories {
|
|
|
126
126
|
mavenLocal()
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
// The minimum supported THEOplayer version is 11.
|
|
130
|
-
def theoVersion = safeExtGet('THEOplayer_sdk', '[11.
|
|
129
|
+
// The minimum supported THEOplayer version is 11.5.0
|
|
130
|
+
def theoVersion = safeExtGet('THEOplayer_sdk', '[11.5.0, 12.0.0)')
|
|
131
131
|
def theoMediaSessionVersion = safeExtGet('THEOplayer_mediasession', '[11.0.0, 12.0.0)')
|
|
132
132
|
def theoAdsWrapperVersion = "11.0.0"
|
|
133
133
|
def coroutinesVersion = safeExtGet('coroutinesVersion', '1.10.2')
|
|
@@ -8,6 +8,8 @@ import com.theoplayer.android.api.THEOplayerConfig
|
|
|
8
8
|
import com.theoplayer.android.api.THEOplayerGlobal
|
|
9
9
|
import com.theoplayer.android.api.cast.CastStrategy
|
|
10
10
|
import com.theoplayer.android.api.cast.CastConfiguration
|
|
11
|
+
import com.theoplayer.android.api.cmcd.CMCDConfiguration
|
|
12
|
+
import com.theoplayer.android.api.cmcd.CMCDEndpointConfiguration
|
|
11
13
|
import com.theoplayer.android.api.pip.PipConfiguration
|
|
12
14
|
import com.theoplayer.android.api.player.NetworkConfiguration
|
|
13
15
|
import com.theoplayer.android.api.theolive.THEOLiveConfig
|
|
@@ -42,9 +44,15 @@ private const val PROP_THEOLIVE_CONFIG = "theoLive"
|
|
|
42
44
|
private const val PROP_THEOLIVE_EXTERNAL_SESSION_ID = "externalSessionId"
|
|
43
45
|
private const val PROP_THEOLIVE_ANALYTICS_DISABLED = "analyticsDisabled"
|
|
44
46
|
private const val PROP_THEOLIVE_DISCOVERY_URL = "discoveryUrl"
|
|
47
|
+
private const val PROP_HLS_DATERANGE = "hlsDateRange"
|
|
45
48
|
private const val PROP_MULTIMEDIA_TUNNELING_ENABLED = "tunnelingEnabled"
|
|
46
49
|
private const val PROP_DEBUG_LOGS_ENABLED = "debugLogsEnabled"
|
|
47
50
|
private const val PROP_SYSTEM_CAPTION_STYLE = "useSystemCaptionStyle"
|
|
51
|
+
private const val PROP_CMCD = "cmcd"
|
|
52
|
+
private const val PROP_CMCD_EXTERNAL_SESSION_ID = "externalSessionId"
|
|
53
|
+
private const val PROP_CMCD_USER_ID = "userId"
|
|
54
|
+
private const val PROP_CMCD_EVENT_ENDPOINTS = "eventEndpoints"
|
|
55
|
+
private const val PROP_CMCD_ENDPOINT_URL = "url"
|
|
48
56
|
|
|
49
57
|
|
|
50
58
|
class PlayerConfigAdapter(private val configProps: ReadableMap?) {
|
|
@@ -82,12 +90,18 @@ class PlayerConfigAdapter(private val configProps: ReadableMap?) {
|
|
|
82
90
|
pip(PipConfiguration.Builder().build())
|
|
83
91
|
// Opt-out for auto-integrations for now
|
|
84
92
|
autoIntegrations(false)
|
|
93
|
+
if (hasKey(PROP_HLS_DATERANGE)) {
|
|
94
|
+
hlsDateRange(getBoolean(PROP_HLS_DATERANGE))
|
|
95
|
+
}
|
|
85
96
|
if (hasKey(PROP_MULTIMEDIA_TUNNELING_ENABLED)) {
|
|
86
97
|
tunnelingEnabled(getBoolean(PROP_MULTIMEDIA_TUNNELING_ENABLED))
|
|
87
98
|
}
|
|
88
99
|
if (hasKey(PROP_SYSTEM_CAPTION_STYLE)) {
|
|
89
100
|
useSystemCaptionStyle(getBoolean(PROP_SYSTEM_CAPTION_STYLE))
|
|
90
101
|
}
|
|
102
|
+
if (hasKey(PROP_CMCD)) {
|
|
103
|
+
cmcd(cmcdConfig())
|
|
104
|
+
}
|
|
91
105
|
}
|
|
92
106
|
}.build()
|
|
93
107
|
}
|
|
@@ -241,6 +255,22 @@ class PlayerConfigAdapter(private val configProps: ReadableMap?) {
|
|
|
241
255
|
return MediaSessionConfigAdapter.fromProps(configProps?.getMap(PROP_MEDIA_CONTROL))
|
|
242
256
|
}
|
|
243
257
|
|
|
258
|
+
private fun cmcdConfig(): CMCDConfiguration {
|
|
259
|
+
val config = configProps?.getMap(PROP_CMCD)
|
|
260
|
+
val endpoints = config?.getArray(PROP_CMCD_EVENT_ENDPOINTS)?.let { arr ->
|
|
261
|
+
(0 until arr.size()).mapNotNull { i ->
|
|
262
|
+
arr.getMap(i)?.getString(PROP_CMCD_ENDPOINT_URL)?.let { url ->
|
|
263
|
+
CMCDEndpointConfiguration(url = url)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return CMCDConfiguration(
|
|
268
|
+
externalSessionId = config?.getString(PROP_CMCD_EXTERNAL_SESSION_ID),
|
|
269
|
+
userId = config?.getString(PROP_CMCD_USER_ID),
|
|
270
|
+
eventEndpoints = endpoints
|
|
271
|
+
)
|
|
272
|
+
}
|
|
273
|
+
|
|
244
274
|
private fun theoLiveConfig (): THEOLiveConfig {
|
|
245
275
|
val config = configProps?.getMap(PROP_THEOLIVE_CONFIG)
|
|
246
276
|
return THEOLiveConfig.Builder(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
package com.theoplayer.abr
|
|
2
2
|
|
|
3
|
+
import android.util.Size
|
|
3
4
|
import com.facebook.react.bridge.ReadableMap
|
|
4
5
|
import com.theoplayer.android.api.abr.AbrStrategyConfiguration
|
|
5
6
|
import com.theoplayer.android.api.abr.AbrStrategyMetadata
|
|
@@ -12,6 +13,9 @@ object ABRConfigurationAdapter {
|
|
|
12
13
|
private const val PROP_METADATA = "metadata"
|
|
13
14
|
private const val PROP_TYPE = "type"
|
|
14
15
|
private const val PROP_BITRATE = "bitrate"
|
|
16
|
+
private const val PROP_PREFERRED_MAXIMUM_RESOLUTION = "preferredMaximumResolution"
|
|
17
|
+
private const val PROP_WIDTH = "width"
|
|
18
|
+
private const val PROP_HEIGHT = "height"
|
|
15
19
|
|
|
16
20
|
fun applyABRConfigurationFromProps(player: Player?, abrProps: ReadableMap?) {
|
|
17
21
|
if (abrProps == null || player == null) {
|
|
@@ -20,6 +24,11 @@ object ABRConfigurationAdapter {
|
|
|
20
24
|
if (abrProps.hasKey(PROP_TARGET_BUFFER)) {
|
|
21
25
|
player.abr.targetBuffer = abrProps.getInt(PROP_TARGET_BUFFER)
|
|
22
26
|
}
|
|
27
|
+
// (0,0) is the documented sentinel for "no cap" and maps to a null Size on the native SDK.
|
|
28
|
+
val preferredMaximumResolutionProps = abrProps.getMap(PROP_PREFERRED_MAXIMUM_RESOLUTION)
|
|
29
|
+
if (preferredMaximumResolutionProps != null) {
|
|
30
|
+
player.abr.preferredMaximumResolution = preferredMaximumResolutionFromProps(preferredMaximumResolutionProps)
|
|
31
|
+
}
|
|
23
32
|
// Strategy can be either a string or an object
|
|
24
33
|
try {
|
|
25
34
|
val abrStrategyPropsString = abrProps.getString(PROP_STRATEGY)
|
|
@@ -50,6 +59,18 @@ object ABRConfigurationAdapter {
|
|
|
50
59
|
}
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
private fun preferredMaximumResolutionFromProps(props: ReadableMap?): Size? {
|
|
63
|
+
if (props == null || !props.hasKey(PROP_WIDTH) || !props.hasKey(PROP_HEIGHT)) {
|
|
64
|
+
return null
|
|
65
|
+
}
|
|
66
|
+
val width = props.getDouble(PROP_WIDTH).toInt()
|
|
67
|
+
val height = props.getDouble(PROP_HEIGHT).toInt()
|
|
68
|
+
if (width <= 0 || height <= 0) {
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
return Size(width, height)
|
|
72
|
+
}
|
|
73
|
+
|
|
53
74
|
private fun abrMetadataFromProps(props: ReadableMap?): AbrStrategyMetadata? {
|
|
54
75
|
if (props == null) {
|
|
55
76
|
return null
|
|
@@ -20,6 +20,8 @@ import com.theoplayer.android.api.player.track.texttrack.TextTrackKind
|
|
|
20
20
|
import com.theoplayer.android.api.source.metadata.ChromecastMetadataImage
|
|
21
21
|
import com.theoplayer.BuildConfig
|
|
22
22
|
import com.theoplayer.android.api.ads.theoads.TheoAdsLayoutOverride
|
|
23
|
+
import com.theoplayer.android.api.cmcd.CMCDEndpointConfiguration
|
|
24
|
+
import com.theoplayer.android.api.cmcd.CMCDSourceConfiguration
|
|
23
25
|
import com.theoplayer.android.api.cmcd.CMCDTransmissionMode
|
|
24
26
|
import com.theoplayer.android.api.error.ErrorCode
|
|
25
27
|
import com.theoplayer.android.api.source.AdIntegration
|
|
@@ -94,6 +96,11 @@ private const val TYPE_MILLICAST = "millicast"
|
|
|
94
96
|
|
|
95
97
|
private const val PROP_CMCD = "cmcd"
|
|
96
98
|
private const val CMCD_TRANSMISSION_MODE = "transmissionMode"
|
|
99
|
+
private const val CMCD_SESSION_ID = "sessionID"
|
|
100
|
+
private const val CMCD_EXTERNAL_SESSION_ID = "externalSessionId"
|
|
101
|
+
private const val CMCD_USER_ID = "userId"
|
|
102
|
+
private const val CMCD_EVENT_ENDPOINTS = "eventEndpoints"
|
|
103
|
+
private const val CMCD_ENDPOINT_URL = "url"
|
|
97
104
|
|
|
98
105
|
class SourceAdapter {
|
|
99
106
|
private val gson = Gson()
|
|
@@ -175,6 +182,11 @@ class SourceAdapter {
|
|
|
175
182
|
if (metadataDescription != null) {
|
|
176
183
|
builder.metadata(metadataDescription)
|
|
177
184
|
}
|
|
185
|
+
if (jsonSourceObject.has(PROP_CMCD)) {
|
|
186
|
+
parseCmcdSourceConfiguration(jsonSourceObject.getJSONObject(PROP_CMCD))?.let {
|
|
187
|
+
builder.cmcd(it)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
178
190
|
return builder.build()
|
|
179
191
|
} catch (e: JSONException) {
|
|
180
192
|
e.printStackTrace()
|
|
@@ -464,7 +476,10 @@ class SourceAdapter {
|
|
|
464
476
|
return BridgeUtils.fromJSONObjectToBridge(JSONObject(gson.toJson(typedSource)))
|
|
465
477
|
}
|
|
466
478
|
|
|
467
|
-
private fun parseCmcdTransmissionMode(cmcdConfiguration : JSONObject) : CMCDTransmissionMode {
|
|
479
|
+
private fun parseCmcdTransmissionMode(cmcdConfiguration : JSONObject) : CMCDTransmissionMode? {
|
|
480
|
+
if (!cmcdConfiguration.has(CMCD_TRANSMISSION_MODE)) {
|
|
481
|
+
return null
|
|
482
|
+
}
|
|
468
483
|
try {
|
|
469
484
|
val transmissionMode = cmcdConfiguration.optInt(CMCD_TRANSMISSION_MODE)
|
|
470
485
|
if (transmissionMode == CmcdTransmissionMode.QUERY_ARGUMENT.ordinal) {
|
|
@@ -476,4 +491,26 @@ class SourceAdapter {
|
|
|
476
491
|
return CMCDTransmissionMode.HTTP_HEADER
|
|
477
492
|
}
|
|
478
493
|
}
|
|
494
|
+
|
|
495
|
+
private fun parseCmcdSourceConfiguration(cmcdJson: JSONObject): CMCDSourceConfiguration? {
|
|
496
|
+
val sessionId = cmcdJson.optString(CMCD_SESSION_ID, null)
|
|
497
|
+
val externalSessionId = cmcdJson.optString(CMCD_EXTERNAL_SESSION_ID, null)
|
|
498
|
+
val userId = cmcdJson.optString(CMCD_USER_ID, null)
|
|
499
|
+
val endpoints = cmcdJson.optJSONArray(CMCD_EVENT_ENDPOINTS)?.let { arr ->
|
|
500
|
+
(0 until arr.length()).mapNotNull { i ->
|
|
501
|
+
arr.optJSONObject(i)?.optString(CMCD_ENDPOINT_URL)?.let { url ->
|
|
502
|
+
CMCDEndpointConfiguration(url = url)
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
if (sessionId == null && externalSessionId == null && userId == null && endpoints == null) {
|
|
507
|
+
return null
|
|
508
|
+
}
|
|
509
|
+
return CMCDSourceConfiguration(
|
|
510
|
+
sessionId = sessionId,
|
|
511
|
+
externalSessionId = externalSessionId,
|
|
512
|
+
userId = userId,
|
|
513
|
+
eventEndpoints = endpoints
|
|
514
|
+
)
|
|
515
|
+
}
|
|
479
516
|
}
|
|
@@ -26,6 +26,7 @@ private const val PROP_NAME = "name"
|
|
|
26
26
|
private const val PROP_ENABLED = "enabled"
|
|
27
27
|
private const val PROP_SRC = "src"
|
|
28
28
|
private const val PROP_FORCED = "forced"
|
|
29
|
+
private const val PROP_IN_BAND_METADATA_TRACK_DISPATCH_TYPE = "inBandMetadataTrackDispatchType"
|
|
29
30
|
private const val PROP_CAPTION_CHANNEL = "captionChannel"
|
|
30
31
|
private const val PROP_AUDIO_SAMPLING_RATE = "audioSamplingRate"
|
|
31
32
|
private const val PROP_BANDWIDTH = "bandwidth"
|
|
@@ -71,6 +72,7 @@ object TrackListAdapter {
|
|
|
71
72
|
textTrackPayload.putBoolean(PROP_FORCED, textTrack.isForced)
|
|
72
73
|
|
|
73
74
|
// THEOplayer v10.13+
|
|
75
|
+
textTrackPayload.putString(PROP_IN_BAND_METADATA_TRACK_DISPATCH_TYPE, textTrack.inBandMetadataTrackDispatchType ?: "")
|
|
74
76
|
textTrack.captionChannel?.let { textTrackPayload.putInt(PROP_CAPTION_CHANNEL, it) }
|
|
75
77
|
|
|
76
78
|
// Optionally pass cue list.
|
|
@@ -281,11 +281,13 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
281
281
|
let player = theView.player {
|
|
282
282
|
let uidValue = uid.intValue
|
|
283
283
|
let textTracks: TextTrackList = player.textTracks
|
|
284
|
-
|
|
284
|
+
let textTrackCount = textTracks.count
|
|
285
|
+
guard textTrackCount > 0 else {
|
|
285
286
|
return
|
|
286
287
|
}
|
|
287
288
|
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Showing textTrack \(uidValue) on TheoPlayer") }
|
|
288
|
-
for i in 0
|
|
289
|
+
for i in 0..<textTrackCount {
|
|
290
|
+
guard i < textTracks.count else { break }
|
|
289
291
|
let textTrack: TextTrack = textTracks.get(i)
|
|
290
292
|
if textTrack.uid == uidValue {
|
|
291
293
|
textTrack.mode = TextTrackMode.showing
|
|
@@ -304,11 +306,13 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
304
306
|
let player = theView.player {
|
|
305
307
|
let uidValue = uid.intValue
|
|
306
308
|
let audioTracks: AudioTrackList = player.audioTracks
|
|
307
|
-
|
|
309
|
+
let audioTrackCount = audioTracks.count
|
|
310
|
+
guard audioTrackCount > 0 else {
|
|
308
311
|
return
|
|
309
312
|
}
|
|
310
313
|
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Enabling audioTrack \(uidValue) on TheoPlayer") }
|
|
311
|
-
for i in 0
|
|
314
|
+
for i in 0..<audioTrackCount {
|
|
315
|
+
guard i < audioTracks.count else { break }
|
|
312
316
|
let audioTrack: MediaTrack = audioTracks.get(i)
|
|
313
317
|
audioTrack.enabled = (audioTrack.uid == uidValue)
|
|
314
318
|
}
|
|
@@ -323,11 +327,13 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
323
327
|
let player = theView.player {
|
|
324
328
|
let uidValue = uid.intValue
|
|
325
329
|
let videoTracks: VideoTrackList = player.videoTracks
|
|
326
|
-
|
|
330
|
+
let videoTrackCount = videoTracks.count
|
|
331
|
+
guard videoTrackCount > 0 else {
|
|
327
332
|
return
|
|
328
333
|
}
|
|
329
334
|
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Enabling videoTrack \(uidValue) on TheoPlayer") }
|
|
330
|
-
for i in 0
|
|
335
|
+
for i in 0..<videoTrackCount {
|
|
336
|
+
guard i < videoTracks.count else { break }
|
|
331
337
|
let videoTrack: MediaTrack = videoTracks.get(i)
|
|
332
338
|
videoTrack.enabled = (videoTrack.uid == uidValue)
|
|
333
339
|
}
|
|
@@ -341,27 +347,37 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
341
347
|
if let theView = self.bridge.uiManager.view(forReactTag: node) as? THEOplayerRCTView,
|
|
342
348
|
let player = theView.player {
|
|
343
349
|
let videoTracks: VideoTrackList = player.videoTracks
|
|
344
|
-
|
|
350
|
+
let videoTrackCount = videoTracks.count
|
|
351
|
+
guard videoTrackCount > 0 else {
|
|
345
352
|
return
|
|
346
353
|
}
|
|
347
354
|
var activeVideoTrack: VideoTrack?
|
|
348
|
-
for i in 0
|
|
355
|
+
for i in 0..<videoTrackCount {
|
|
356
|
+
guard i < videoTracks.count else { break }
|
|
349
357
|
let videoTrack: MediaTrack = videoTracks.get(i)
|
|
350
358
|
if videoTrack.enabled {
|
|
351
359
|
activeVideoTrack = videoTrack as? VideoTrack
|
|
352
360
|
}
|
|
353
361
|
}
|
|
354
362
|
if let foundTrack = activeVideoTrack {
|
|
355
|
-
let
|
|
363
|
+
let requestedBandwidths = Set(uids.map { $0.intValue })
|
|
364
|
+
let currentBandwidths = Set(foundTrack.targetQualities?.map { $0.bandwidth } ?? [])
|
|
365
|
+
guard requestedBandwidths != currentBandwidths else {
|
|
366
|
+
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] targetQualities: \(uids) already set on active videotrack. Skipping update.") }
|
|
367
|
+
return
|
|
368
|
+
}
|
|
369
|
+
let qualityCount = foundTrack.qualities.count
|
|
370
|
+
let matchingQualities = (0..<qualityCount).compactMap { index -> Quality? in
|
|
371
|
+
guard index < foundTrack.qualities.count else { return nil }
|
|
356
372
|
let quality = foundTrack.qualities.get(index)
|
|
357
|
-
return uids.contains { $0.intValue == quality.bandwidth } ? quality : nil
|
|
373
|
+
return uids.contains(where: { $0.intValue == quality.bandwidth }) ? quality : nil
|
|
358
374
|
}
|
|
359
|
-
foundTrack.targetQualities = matchingQualities.
|
|
375
|
+
foundTrack.targetQualities = matchingQualities.isEmpty ? nil : matchingQualities
|
|
360
376
|
if DEBUG_PLAYER_API {
|
|
361
377
|
if matchingQualities.count > 0 {
|
|
362
|
-
|
|
378
|
+
PrintUtils.printLog(logText: "[NATIVE] targetQualities: \(uids) set on active videotrack. (matching: \(matchingQualities.map(\.bandwidth)))")
|
|
363
379
|
} else {
|
|
364
|
-
|
|
380
|
+
PrintUtils.printLog(logText: "[NATIVE] targetQualities: \(uids) set on active videotrack. (no match or empty) => no quality restriction.")
|
|
365
381
|
}
|
|
366
382
|
}
|
|
367
383
|
}
|
|
@@ -64,6 +64,7 @@ let SD_PROP_INITIALIZATION_DELAY: String = "initializationDelay"
|
|
|
64
64
|
let SD_PROP_HLS_DATE_RANGE: String = "hlsDateRange"
|
|
65
65
|
let SD_PROP_BREAK_MANIFEST_URL: String = "breakManifestUrl"
|
|
66
66
|
let SD_PROP_CMCD: String = "cmcd"
|
|
67
|
+
let SD_PROP_TRANSMISSION_MODE: String = "transmissionMode"
|
|
67
68
|
let SD_PROP_QUERY_PARAMETERS: String = "queryParameters"
|
|
68
69
|
|
|
69
70
|
let EXTENSION_HLS: String = ".m3u8"
|
|
@@ -167,11 +168,15 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
// 6. configure CMCD
|
|
170
|
-
|
|
171
|
-
if cmcd
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
var cmcdSourceConfig: CMCDSourceConfiguration? = nil
|
|
172
|
+
if let cmcd = sourceData[SD_PROP_CMCD] as? [String:Any] {
|
|
173
|
+
let requestModeEnabled = cmcd[SD_PROP_TRANSMISSION_MODE] != nil
|
|
174
|
+
if requestModeEnabled {
|
|
175
|
+
typedSources.forEach { typedSource in
|
|
176
|
+
typedSource.cmcd = true
|
|
177
|
+
}
|
|
174
178
|
}
|
|
179
|
+
cmcdSourceConfig = THEOplayerRCTSourceDescriptionBuilder.buildCmcdSourceConfiguration(cmcd)
|
|
175
180
|
}
|
|
176
181
|
|
|
177
182
|
// 7. construct the SourceDescription
|
|
@@ -179,13 +184,33 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
179
184
|
textTracks: textTrackDescriptions,
|
|
180
185
|
ads: adsDescriptions,
|
|
181
186
|
poster: poster,
|
|
182
|
-
metadata: metadataDescription
|
|
183
|
-
|
|
187
|
+
metadata: metadataDescription,
|
|
188
|
+
cmcdConfiguration: cmcdSourceConfig)
|
|
189
|
+
|
|
184
190
|
return (sourceDescription, metadataAndChapterTrackDescriptions)
|
|
185
191
|
}
|
|
186
192
|
|
|
187
193
|
// MARK: Private build methods
|
|
188
194
|
|
|
195
|
+
private static func buildCmcdSourceConfiguration(_ cmcdData: [String:Any]) -> CMCDSourceConfiguration? {
|
|
196
|
+
let sessionId = cmcdData["sessionID"] as? String
|
|
197
|
+
let externalSessionId = cmcdData["externalSessionId"] as? String
|
|
198
|
+
let userId = cmcdData["userId"] as? String
|
|
199
|
+
let endpoints: [CMCDEndpointConfiguration]? = (cmcdData["eventEndpoints"] as? [[String: Any]])?.compactMap { dict in
|
|
200
|
+
guard let url = dict["url"] as? String else { return nil }
|
|
201
|
+
return CMCDEndpointConfiguration(url: url)
|
|
202
|
+
}
|
|
203
|
+
if sessionId == nil && externalSessionId == nil && userId == nil && endpoints == nil {
|
|
204
|
+
return nil
|
|
205
|
+
}
|
|
206
|
+
return CMCDSourceConfiguration(
|
|
207
|
+
sessionId: sessionId,
|
|
208
|
+
externalSessionId: externalSessionId,
|
|
209
|
+
userId: userId,
|
|
210
|
+
eventEndpoints: endpoints
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
189
214
|
/**
|
|
190
215
|
Creates a THEOplayer TypedSource. This requires a source property for non SSAI strreams (either as a string or as an object contiaining a src property). For SSAI streams the TypeSource can be created from the ssai property.
|
|
191
216
|
- returns: a THEOplayer TypedSource. In case of SSAI we support GoogleDAITypedSource with GoogleDAIVodConfiguration or GoogleDAILiveConfiguration
|
|
@@ -29,6 +29,7 @@ let PROP_CUE_CONTENT: String = "content"
|
|
|
29
29
|
let PROP_CUE_CUSTOM_ATTRIBUTES: String = "customAttributes"
|
|
30
30
|
let PROP_SRC: String = "src"
|
|
31
31
|
let PROP_FORCED: String = "forced"
|
|
32
|
+
let PROP_IN_BAND_METADATA_TRACK_DISPATCH_TYPE: String = "inBandMetadataTrackDispatchType"
|
|
32
33
|
let PROP_START_DATE: String = "startDate"
|
|
33
34
|
let PROP_END_DATE: String = "endDate"
|
|
34
35
|
let PROP_ATTRIBUTE_CLASS: String = "class"
|
|
@@ -56,10 +57,12 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
56
57
|
// MARK: TEXTTRACKS
|
|
57
58
|
class func aggregatedTextTrackListInfo(textTracks: TextTrackList, metadataTracks: [[String:Any]], player: THEOplayer) -> [[String:Any]] {
|
|
58
59
|
var trackEntries:[[String:Any]] = metadataTracks
|
|
59
|
-
|
|
60
|
+
let textTrackCount = textTracks.count
|
|
61
|
+
guard textTrackCount > 0 else {
|
|
60
62
|
return trackEntries
|
|
61
63
|
}
|
|
62
|
-
for i in 0
|
|
64
|
+
for i in 0..<textTrackCount {
|
|
65
|
+
guard i < textTracks.count else { break }
|
|
63
66
|
let textTrack: TextTrack = textTracks.get(i)
|
|
64
67
|
trackEntries.append(THEOplayerRCTTrackMetadataAggregator.aggregatedTextTrackInfo(textTrack: textTrack, player: player))
|
|
65
68
|
}
|
|
@@ -78,6 +81,7 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
78
81
|
entry[PROP_TYPE] = textTrack.type
|
|
79
82
|
entry[PROP_SRC] = textTrack.src
|
|
80
83
|
entry[PROP_FORCED] = textTrack.forced
|
|
84
|
+
entry[PROP_IN_BAND_METADATA_TRACK_DISPATCH_TYPE] = textTrack.inBandMetadataTrackDispatchType
|
|
81
85
|
// process cues when texttrack contains them
|
|
82
86
|
if !textTrack.cues.isEmpty {
|
|
83
87
|
var cueList: [[String:Any]] = []
|
|
@@ -90,10 +94,12 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
private class func selectedTextTrack(textTracks: TextTrackList) -> Int {
|
|
93
|
-
|
|
97
|
+
let textTrackCount = textTracks.count
|
|
98
|
+
guard textTrackCount > 0 else {
|
|
94
99
|
return 0
|
|
95
100
|
}
|
|
96
|
-
for i in 0
|
|
101
|
+
for i in 0..<textTrackCount {
|
|
102
|
+
guard i < textTracks.count else { break }
|
|
97
103
|
let textTrack: TextTrack = textTracks.get(i)
|
|
98
104
|
if textTrack.mode == TextTrackMode.showing {
|
|
99
105
|
return textTrack.uid
|
|
@@ -160,10 +166,12 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
160
166
|
// MARK: AUDIOTRACKS
|
|
161
167
|
private class func aggregatedAudioTrackListInfo(audioTracks: AudioTrackList) -> [[String:Any]] {
|
|
162
168
|
var audioTrackEntries:[[String:Any]] = []
|
|
163
|
-
|
|
169
|
+
let audioTrackCount = audioTracks.count
|
|
170
|
+
guard audioTrackCount > 0 else {
|
|
164
171
|
return audioTrackEntries
|
|
165
172
|
}
|
|
166
|
-
for i in 0
|
|
173
|
+
for i in 0..<audioTrackCount {
|
|
174
|
+
guard i < audioTracks.count else { break }
|
|
167
175
|
let audioTrack: MediaTrack = audioTracks.get(i) // should be casted to AudioTrack
|
|
168
176
|
audioTrackEntries.append(THEOplayerRCTTrackMetadataAggregator.aggregatedAudioTrackInfo(audioTrack: audioTrack))
|
|
169
177
|
}
|
|
@@ -181,7 +189,9 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
181
189
|
entry[PROP_ENABLED] = audioTrack.enabled
|
|
182
190
|
|
|
183
191
|
// add known qualities
|
|
184
|
-
|
|
192
|
+
let audioQualityCount = audioTrack.qualities.count
|
|
193
|
+
entry[PROP_QUALITIES] = (0..<audioQualityCount).compactMap { index -> [String:Any]? in
|
|
194
|
+
guard index < audioTrack.qualities.count else { return nil }
|
|
185
195
|
return THEOplayerRCTTrackMetadataAggregator.aggregatedQualityInfo(quality: audioTrack.qualities.get(index))
|
|
186
196
|
}
|
|
187
197
|
|
|
@@ -193,10 +203,12 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
193
203
|
}
|
|
194
204
|
|
|
195
205
|
private class func selectedAudioTrack(audioTracks: AudioTrackList) -> Int {
|
|
196
|
-
|
|
206
|
+
let audioTrackCount = audioTracks.count
|
|
207
|
+
guard audioTrackCount > 0 else {
|
|
197
208
|
return 0
|
|
198
209
|
}
|
|
199
|
-
for i in 0
|
|
210
|
+
for i in 0..<audioTrackCount {
|
|
211
|
+
guard i < audioTracks.count else { break }
|
|
200
212
|
let audioTrack: MediaTrack = audioTracks.get(i)
|
|
201
213
|
if audioTrack.enabled {
|
|
202
214
|
return audioTrack.uid
|
|
@@ -208,10 +220,12 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
208
220
|
// MARK: VIDEOTRACKS
|
|
209
221
|
private class func aggregatedVideoTrackListInfo(videoTracks: VideoTrackList) -> [[String:Any]] {
|
|
210
222
|
var videoTrackEntries:[[String:Any]] = []
|
|
211
|
-
|
|
223
|
+
let videoTrackCount = videoTracks.count
|
|
224
|
+
guard videoTrackCount > 0 else {
|
|
212
225
|
return videoTrackEntries
|
|
213
226
|
}
|
|
214
|
-
for i in 0
|
|
227
|
+
for i in 0..<videoTrackCount {
|
|
228
|
+
guard i < videoTracks.count else { break }
|
|
215
229
|
if let videoTrack: VideoTrack = videoTracks.get(i) as? VideoTrack {
|
|
216
230
|
videoTrackEntries.append(THEOplayerRCTTrackMetadataAggregator.aggregatedVideoTrackInfo(videoTrack: videoTrack))
|
|
217
231
|
}
|
|
@@ -230,7 +244,9 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
230
244
|
entry[PROP_ENABLED] = videoTrack.enabled
|
|
231
245
|
|
|
232
246
|
// add known qualities
|
|
233
|
-
|
|
247
|
+
let videoQualityCount = videoTrack.qualities.count
|
|
248
|
+
entry[PROP_QUALITIES] = (0..<videoQualityCount).compactMap { index -> [String:Any]? in
|
|
249
|
+
guard index < videoTrack.qualities.count else { return nil }
|
|
234
250
|
return THEOplayerRCTTrackMetadataAggregator.aggregatedQualityInfo(quality: videoTrack.qualities.get(index))
|
|
235
251
|
}
|
|
236
252
|
|
|
@@ -243,12 +259,14 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
243
259
|
}
|
|
244
260
|
|
|
245
261
|
private class func selectedVideoTrack(videoTracks: VideoTrackList) -> Int {
|
|
246
|
-
|
|
262
|
+
let videoTrackCount = videoTracks.count
|
|
263
|
+
guard videoTrackCount > 0 else {
|
|
247
264
|
return 0
|
|
248
265
|
}
|
|
249
|
-
for i in 0
|
|
266
|
+
for i in 0..<videoTrackCount {
|
|
267
|
+
guard i < videoTracks.count else { break }
|
|
250
268
|
let videoTrack: MediaTrack = videoTracks.get(i)
|
|
251
|
-
if
|
|
269
|
+
if videoTrack.enabled {
|
|
252
270
|
return videoTrack.uid
|
|
253
271
|
}
|
|
254
272
|
}
|
|
@@ -274,7 +292,7 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
274
292
|
"bandwidth": quality.bandwidth,
|
|
275
293
|
"codecs": "",
|
|
276
294
|
"id": identifier,
|
|
277
|
-
"uid":
|
|
295
|
+
"uid": quality.bandwidth,
|
|
278
296
|
"name": label,
|
|
279
297
|
"label": label,
|
|
280
298
|
"available": true,
|
|
@@ -311,6 +329,7 @@ class THEOplayerRCTTrackMetadataAggregator {
|
|
|
311
329
|
track[PROP_LABEL] = trackDescription.label ?? "no label"
|
|
312
330
|
track[PROP_TYPE] = "webvtt"
|
|
313
331
|
track[PROP_SRC] = trackDescription.src.absoluteString
|
|
332
|
+
track[PROP_IN_BAND_METADATA_TRACK_DISPATCH_TYPE] = ""
|
|
314
333
|
var cueList: [[String:Any]] = []
|
|
315
334
|
var cueIndex = 0
|
|
316
335
|
for c in cues {
|
|
@@ -48,6 +48,7 @@ public class THEOplayerRCTView: UIView {
|
|
|
48
48
|
var castConfig = CastConfig()
|
|
49
49
|
var uiConfig = UIConfig()
|
|
50
50
|
var theoliveConfig = THEOliveConfig()
|
|
51
|
+
var cmcdConfig = CmcdConfig()
|
|
51
52
|
|
|
52
53
|
public var bypassDropInstanceOnReactLifecycle = false // controls superView detaching behaviour
|
|
53
54
|
|
|
@@ -232,6 +233,7 @@ public class THEOplayerRCTView: UIView {
|
|
|
232
233
|
config.hlsDateRange = self.hlsDateRange
|
|
233
234
|
config.license = self.license
|
|
234
235
|
config.licenseUrl = self.licenseUrl
|
|
236
|
+
config.cmcd = self.playerCmcdConfiguration()
|
|
235
237
|
self.player = THEOplayer(configuration: config.build())
|
|
236
238
|
|
|
237
239
|
self.initAdsIntegration()
|
|
@@ -322,6 +324,7 @@ public class THEOplayerRCTView: UIView {
|
|
|
322
324
|
self.parseUIConfig(configDict: configDict)
|
|
323
325
|
self.parseMediaControlConfig(configDict: configDict)
|
|
324
326
|
self.parseTHEOliveConfig(configDict: configDict)
|
|
327
|
+
self.parseCmcdConfig(configDict: configDict)
|
|
325
328
|
if DEBUG_VIEW { PrintUtils.printLog(logText: "[NATIVE] config prop updated.") }
|
|
326
329
|
|
|
327
330
|
// Given the bridged config, create the initial THEOplayer instance
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// THEOplayerRCTView+CmcdConfig.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import THEOplayerSDK
|
|
5
|
+
|
|
6
|
+
struct CmcdConfig {
|
|
7
|
+
var externalSessionId: String?
|
|
8
|
+
var userId: String?
|
|
9
|
+
var eventEndpoints: [CMCDEndpointConfiguration]?
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
extension THEOplayerRCTView {
|
|
13
|
+
|
|
14
|
+
func parseCmcdConfig(configDict: NSDictionary) {
|
|
15
|
+
if let cmcdDict = configDict["cmcd"] as? NSDictionary {
|
|
16
|
+
self.cmcdConfig.externalSessionId = cmcdDict["externalSessionId"] as? String
|
|
17
|
+
self.cmcdConfig.userId = cmcdDict["userId"] as? String
|
|
18
|
+
self.cmcdConfig.eventEndpoints = (cmcdDict["eventEndpoints"] as? [[String: Any]])?.compactMap { dict -> CMCDEndpointConfiguration? in
|
|
19
|
+
guard let url = dict["url"] as? String else { return nil }
|
|
20
|
+
return CMCDEndpointConfiguration(url: url)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
func playerCmcdConfiguration() -> CMCDConfiguration? {
|
|
26
|
+
let config = self.cmcdConfig
|
|
27
|
+
if config.externalSessionId == nil && config.userId == nil && config.eventEndpoints == nil {
|
|
28
|
+
return nil
|
|
29
|
+
}
|
|
30
|
+
return CMCDConfiguration(
|
|
31
|
+
externalSessionId: config.externalSessionId,
|
|
32
|
+
userId: config.userId,
|
|
33
|
+
eventEndpoints: config.eventEndpoints
|
|
34
|
+
)
|
|
35
|
+
}
|
|
36
|
+
}
|