react-native-theoplayer 9.8.0 → 9.9.1
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 +25 -0
- package/android/build.gradle +9 -7
- package/android/src/main/java/com/theoplayer/ReactTHEOplayerPackage.kt +3 -1
- package/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt +32 -0
- package/android/src/main/java/com/theoplayer/presentation/PresentationManager.kt +63 -64
- package/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +12 -30
- package/android/src/main/java/com/theoplayer/theolive/EndpointAdapter.kt +2 -1
- package/android/src/main/java/com/theoplayer/theolive/THEOliveEventAdapter.kt +6 -2
- package/android/src/main/java/com/theoplayer/theolive/THEOliveModule.kt +49 -0
- package/ios/THEOplayerRCTBridge.m +5 -0
- package/ios/THEOplayerRCTDeviceEventHandler.swift +38 -0
- package/ios/THEOplayerRCTPlayerAPI.swift +14 -0
- package/ios/THEOplayerRCTSourceDescriptionBuilder.swift +1 -0
- package/ios/THEOplayerRCTTypeUtils.swift +13 -0
- package/ios/THEOplayerRCTView.swift +11 -0
- package/ios/theoAds/THEOplayerRCTSourceDescriptionBuilder+TheoAds.swift +4 -1
- package/ios/theolive/THEOplayerRCTTHEOliveAPI.swift +21 -4
- package/ios/theolive/THEOplayerRCTTHEOliveEventAdapter.swift +62 -0
- package/ios/theolive/THEOplayerRCTTHEOliveEventHandler.swift +6 -2
- package/lib/commonjs/api/event/TheoAdsEvent.js +1 -1
- package/lib/commonjs/api/event/TheoLiveEvent.js +7 -1
- package/lib/commonjs/api/event/TheoLiveEvent.js.map +1 -1
- package/lib/commonjs/api/source/ads/TheoAdDescription.js.map +1 -1
- package/lib/commonjs/internal/THEOplayerView.js +12 -1
- package/lib/commonjs/internal/THEOplayerView.js.map +1 -1
- package/lib/commonjs/internal/THEOplayerView.web.js +28 -3
- package/lib/commonjs/internal/THEOplayerView.web.js.map +1 -1
- package/lib/commonjs/internal/adapter/WebEventForwarder.js +5 -0
- package/lib/commonjs/internal/adapter/WebEventForwarder.js.map +1 -1
- package/lib/commonjs/internal/adapter/event/PlayerEvents.js +9 -1
- package/lib/commonjs/internal/adapter/event/PlayerEvents.js.map +1 -1
- package/lib/commonjs/internal/adapter/event/native/NativeTheoLiveEvent.js +2 -0
- package/lib/commonjs/internal/adapter/event/native/NativeTheoLiveEvent.js.map +1 -1
- package/lib/commonjs/internal/adapter/theolive/TheoLiveNativeAdapter.js +9 -15
- package/lib/commonjs/internal/adapter/theolive/TheoLiveNativeAdapter.js.map +1 -1
- package/lib/commonjs/internal/adapter/theolive/TheoLiveWebAdapter.js +8 -0
- package/lib/commonjs/internal/adapter/theolive/TheoLiveWebAdapter.js.map +1 -1
- package/lib/commonjs/manifest.json +1 -1
- package/lib/module/api/event/TheoAdsEvent.js +1 -1
- package/lib/module/api/event/TheoLiveEvent.js +8 -1
- package/lib/module/api/event/TheoLiveEvent.js.map +1 -1
- package/lib/module/api/source/ads/TheoAdDescription.js.map +1 -1
- package/lib/module/internal/THEOplayerView.js +12 -1
- package/lib/module/internal/THEOplayerView.js.map +1 -1
- package/lib/module/internal/THEOplayerView.web.js +28 -3
- package/lib/module/internal/THEOplayerView.web.js.map +1 -1
- package/lib/module/internal/adapter/WebEventForwarder.js +6 -1
- package/lib/module/internal/adapter/WebEventForwarder.js.map +1 -1
- package/lib/module/internal/adapter/event/PlayerEvents.js +7 -0
- package/lib/module/internal/adapter/event/PlayerEvents.js.map +1 -1
- package/lib/module/internal/adapter/event/native/NativeTheoLiveEvent.js +3 -1
- package/lib/module/internal/adapter/event/native/NativeTheoLiveEvent.js.map +1 -1
- package/lib/module/internal/adapter/theolive/TheoLiveNativeAdapter.js +10 -16
- package/lib/module/internal/adapter/theolive/TheoLiveNativeAdapter.js.map +1 -1
- package/lib/module/internal/adapter/theolive/TheoLiveWebAdapter.js +8 -0
- package/lib/module/internal/adapter/theolive/TheoLiveWebAdapter.js.map +1 -1
- package/lib/module/manifest.json +1 -1
- package/lib/typescript/api/event/TheoAdsEvent.d.ts +1 -1
- package/lib/typescript/api/event/TheoLiveEvent.d.ts +15 -5
- package/lib/typescript/api/event/TheoLiveEvent.d.ts.map +1 -1
- package/lib/typescript/api/source/ads/TheoAdDescription.d.ts +4 -0
- package/lib/typescript/api/source/ads/TheoAdDescription.d.ts.map +1 -1
- package/lib/typescript/api/theolive/TheoLiveAPI.d.ts +9 -3
- package/lib/typescript/api/theolive/TheoLiveAPI.d.ts.map +1 -1
- package/lib/typescript/api/theolive/TheoLiveEndpoint.d.ts +1 -0
- package/lib/typescript/api/theolive/TheoLiveEndpoint.d.ts.map +1 -1
- package/lib/typescript/internal/THEOplayerView.d.ts +1 -0
- package/lib/typescript/internal/THEOplayerView.d.ts.map +1 -1
- package/lib/typescript/internal/THEOplayerView.web.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/WebEventForwarder.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts +6 -1
- package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/event/native/NativeTheoLiveEvent.d.ts +5 -1
- package/lib/typescript/internal/adapter/event/native/NativeTheoLiveEvent.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/theolive/TheoLiveNativeAdapter.d.ts +3 -0
- package/lib/typescript/internal/adapter/theolive/TheoLiveNativeAdapter.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/theolive/TheoLiveWebAdapter.d.ts +2 -0
- package/lib/typescript/internal/adapter/theolive/TheoLiveWebAdapter.d.ts.map +1 -1
- package/package.json +2 -2
- package/react-native-theoplayer.podspec +7 -7
- package/src/api/event/TheoAdsEvent.ts +1 -1
- package/src/api/event/TheoLiveEvent.ts +16 -5
- package/src/api/source/ads/TheoAdDescription.ts +5 -0
- package/src/api/theolive/TheoLiveAPI.ts +9 -3
- package/src/api/theolive/TheoLiveEndpoint.ts +1 -0
- package/src/internal/THEOplayerView.tsx +15 -2
- package/src/internal/THEOplayerView.web.tsx +24 -2
- package/src/internal/adapter/WebEventForwarder.ts +5 -1
- package/src/internal/adapter/event/PlayerEvents.ts +10 -0
- package/src/internal/adapter/event/native/NativeTheoLiveEvent.ts +14 -2
- package/src/internal/adapter/theolive/TheoLiveNativeAdapter.ts +13 -16
- package/src/internal/adapter/theolive/TheoLiveWebAdapter.ts +10 -0
- package/src/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,31 @@ 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
|
+
## [9.9.1] - 25-07-31
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fixed an issue on Web in case the `aspect-ratio` CSS property is not supported on older browsers.
|
|
13
|
+
- Prevented passing infinite or NaN for intializationDelay in TheoAdDescription, on the Android bridge.
|
|
14
|
+
- Fixed and issue on Android where transitioning automatically into PiP presentation mode would fail on Android 16+.
|
|
15
|
+
- Fixed sources without an explicit `type` not being correctly recognized on Android when the URL contains query parameters.
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
|
|
19
|
+
- Added support for Clearkey DRM on Android.
|
|
20
|
+
- Added `endpoint` property to THEOlive `EndpointLoaded` event.
|
|
21
|
+
- Added support for ABRStrategyConfiguration on iOS
|
|
22
|
+
|
|
23
|
+
## [9.9.0] - 25-07-14
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- Added initializationDelay to TheoAdsDecription, to delay THEOads session creation for more optimal ads monetisation.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Fixed an issue on iOS where on iPads the fullscreen dimensions were not correctly updated after the first device orientation change.
|
|
32
|
+
|
|
8
33
|
## [9.8.0] - 25-07-08
|
|
9
34
|
|
|
10
35
|
### Changed
|
package/android/build.gradle
CHANGED
|
@@ -43,12 +43,12 @@ def enabledMediaSession = safeExtGet("THEOplayer_extensionMediaSession", 'true')
|
|
|
43
43
|
def enabledMillicast = safeExtGet("THEOplayer_extensionMillicast", 'false').toBoolean()
|
|
44
44
|
|
|
45
45
|
android {
|
|
46
|
-
compileSdk safeExtGet('THEOplayer_compileSdkVersion',
|
|
46
|
+
compileSdk safeExtGet('THEOplayer_compileSdkVersion', 35)
|
|
47
47
|
namespace "com.theoplayer"
|
|
48
48
|
|
|
49
49
|
defaultConfig {
|
|
50
50
|
minSdkVersion safeExtGet('THEOplayer_minSdkVersion', 21)
|
|
51
|
-
targetSdkVersion safeExtGet('THEOplayer_targetSdkVersion',
|
|
51
|
+
targetSdkVersion safeExtGet('THEOplayer_targetSdkVersion', 35)
|
|
52
52
|
versionCode 1
|
|
53
53
|
versionName "1.0"
|
|
54
54
|
|
|
@@ -124,15 +124,16 @@ repositories {
|
|
|
124
124
|
mavenLocal()
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
// The minimum supported THEOplayer version is 9.
|
|
128
|
-
def theoVersion = safeExtGet('THEOplayer_sdk', '[9.
|
|
127
|
+
// The minimum supported THEOplayer version is 9.9.0
|
|
128
|
+
def theoVersion = safeExtGet('THEOplayer_sdk', '[9.9.0, 10.0.0)')
|
|
129
129
|
def theoMediaSessionVersion = safeExtGet('THEOplayer_mediasession', '[8.0.0, 10.0.0)')
|
|
130
130
|
def theoAdsWrapperVersion = "9.0.0"
|
|
131
|
-
def coroutinesVersion = safeExtGet('coroutinesVersion', '1.
|
|
131
|
+
def coroutinesVersion = safeExtGet('coroutinesVersion', '1.10.2')
|
|
132
132
|
def appcompatVersion = safeExtGet('appcompatVersion', '1.7.1')
|
|
133
133
|
def corektxVersion = safeExtGet('corektxVersion', '1.13.1')
|
|
134
|
-
def gsonVersion =
|
|
135
|
-
def
|
|
134
|
+
def gsonVersion = safeExtGet('gsonVersion', '2.13.1')
|
|
135
|
+
def activityktxVersion = safeExtGet('activityktxVersion', '1.10.1')
|
|
136
|
+
def millicastVersion = safeExtGet('millicastVersion', '2.4.3')
|
|
136
137
|
|
|
137
138
|
dependencies {
|
|
138
139
|
def addOptiViewIntegration = { enabled, notation, additional = null ->
|
|
@@ -156,6 +157,7 @@ dependencies {
|
|
|
156
157
|
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
|
|
157
158
|
implementation("androidx.appcompat:appcompat:$appcompatVersion")
|
|
158
159
|
implementation("androidx.core:core-ktx:$corektxVersion")
|
|
160
|
+
implementation("androidx.activity:activity-ktx:$activityktxVersion")
|
|
159
161
|
implementation("com.google.code.gson:gson:$gsonVersion")
|
|
160
162
|
|
|
161
163
|
println("Using THEOplayer (${versionString(theoVersion)})")
|
|
@@ -11,6 +11,7 @@ import com.theoplayer.cast.CastModule
|
|
|
11
11
|
import com.theoplayer.broadcast.EventBroadcastModule
|
|
12
12
|
import com.theoplayer.playback.PlaybackSettingsModule
|
|
13
13
|
import com.theoplayer.player.PlayerModule
|
|
14
|
+
import com.theoplayer.theolive.THEOliveModule
|
|
14
15
|
|
|
15
16
|
class ReactTHEOplayerPackage : ReactPackage {
|
|
16
17
|
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
|
|
@@ -21,7 +22,8 @@ class ReactTHEOplayerPackage : ReactPackage {
|
|
|
21
22
|
CastModule(reactContext),
|
|
22
23
|
CacheModule(reactContext),
|
|
23
24
|
EventBroadcastModule(reactContext),
|
|
24
|
-
PlaybackSettingsModule(reactContext)
|
|
25
|
+
PlaybackSettingsModule(reactContext),
|
|
26
|
+
THEOliveModule(reactContext)
|
|
25
27
|
)
|
|
26
28
|
}
|
|
27
29
|
|
|
@@ -9,6 +9,7 @@ import com.google.gson.Gson
|
|
|
9
9
|
import com.theoplayer.android.api.contentprotection.KeySystemId
|
|
10
10
|
import com.theoplayer.android.api.contentprotection.Request
|
|
11
11
|
import com.theoplayer.android.api.contentprotection.Response
|
|
12
|
+
import com.theoplayer.android.api.source.drm.ClearkeyKeySystemConfiguration
|
|
12
13
|
import com.theoplayer.android.api.source.drm.DRMConfiguration
|
|
13
14
|
import com.theoplayer.android.api.source.drm.DRMIntegrationId
|
|
14
15
|
import com.theoplayer.android.api.source.drm.KeySystemConfiguration
|
|
@@ -27,6 +28,7 @@ const val PROP_KEYSYSTEM_ID: String = "keySystemId"
|
|
|
27
28
|
const val PROP_DRM_CONFIG: String = "drmConfig"
|
|
28
29
|
const val PROP_PLAYREADY: String = "playready"
|
|
29
30
|
const val PROP_WIDEVINE: String = "widevine"
|
|
31
|
+
const val PROP_CLEARKEY: String = "clearkey"
|
|
30
32
|
const val PROP_REQUEST_ID: String = "requestId"
|
|
31
33
|
const val PROP_URL: String = "url"
|
|
32
34
|
const val PROP_METHOD: String = "method"
|
|
@@ -42,6 +44,9 @@ const val PROP_LICENSE_TYPE_TEMPORARY: String = "temporary"
|
|
|
42
44
|
const val PROP_LICENSE_TYPE_PERSISTENT: String = "persistent"
|
|
43
45
|
const val PROP_QUERY_PARAMETERS: String = "queryParameters"
|
|
44
46
|
const val PROP_CERTIFICATE: String = "certificate"
|
|
47
|
+
const val PROP_KEYS: String = "keys"
|
|
48
|
+
const val PROP_ID: String = "id"
|
|
49
|
+
const val PROP_VALUE: String = "value"
|
|
45
50
|
|
|
46
51
|
object ContentProtectionAdapter {
|
|
47
52
|
|
|
@@ -93,6 +98,9 @@ object ContentProtectionAdapter {
|
|
|
93
98
|
if (jsonConfig.has(PROP_PLAYREADY)) {
|
|
94
99
|
playready(keySystemConfigurationFromJson(jsonConfig.getJSONObject(PROP_PLAYREADY)))
|
|
95
100
|
}
|
|
101
|
+
if (jsonConfig.has(PROP_CLEARKEY)) {
|
|
102
|
+
clearkey(clearkeyKeySystemConfigurationFromJson(jsonConfig.getJSONObject(PROP_CLEARKEY)))
|
|
103
|
+
}
|
|
96
104
|
if (jsonConfig.has(PROP_INTEGRATION_PARAMETERS)) {
|
|
97
105
|
integrationParameters(fromJSONObjectToMap(jsonConfig.getJSONObject(PROP_INTEGRATION_PARAMETERS)))
|
|
98
106
|
}
|
|
@@ -113,6 +121,30 @@ object ContentProtectionAdapter {
|
|
|
113
121
|
}.build()
|
|
114
122
|
}
|
|
115
123
|
|
|
124
|
+
private fun clearkeyKeySystemConfigurationFromJson(config: JSONObject): ClearkeyKeySystemConfiguration {
|
|
125
|
+
val jsonKeys = config.optJSONArray(PROP_KEYS)
|
|
126
|
+
val keys = mutableListOf<ClearkeyKeySystemConfiguration.ClearkeyDecryptionKey>()
|
|
127
|
+
if (jsonKeys != null) {
|
|
128
|
+
for (i in 0 until jsonKeys.length()) {
|
|
129
|
+
val jsonKey = jsonKeys.optJSONObject(i)
|
|
130
|
+
if (jsonKey != null) {
|
|
131
|
+
val id = jsonKey.optString(PROP_ID)
|
|
132
|
+
val value = jsonKey.optString(PROP_VALUE)
|
|
133
|
+
if (!id.isNullOrEmpty() && !value.isNullOrEmpty()) {
|
|
134
|
+
keys.add(ClearkeyKeySystemConfiguration.ClearkeyDecryptionKey(id, value))
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ClearkeyKeySystemConfiguration.Builder(config.optString(PROP_LICENSE_ACQUISITION_URL))
|
|
140
|
+
.apply {
|
|
141
|
+
useCredentials(config.optBoolean(PROP_USE_CREDENTIALS))
|
|
142
|
+
headers(fromJSONObjectToMap(config.optJSONObject(PROP_HEADERS)))
|
|
143
|
+
queryParameters(fromJSONObjectToMap(config.optJSONObject(PROP_QUERY_PARAMETERS)))
|
|
144
|
+
keys(keys.toTypedArray())
|
|
145
|
+
}.build()
|
|
146
|
+
}
|
|
147
|
+
|
|
116
148
|
private fun licenseTypeFromString(str: String?): LicenseType? {
|
|
117
149
|
return when (str) {
|
|
118
150
|
PROP_LICENSE_TYPE_PERSISTENT -> LicenseType.PERSISTENT
|
|
@@ -26,7 +26,12 @@ import com.theoplayer.android.api.error.ErrorCode
|
|
|
26
26
|
import com.theoplayer.android.api.error.THEOplayerException
|
|
27
27
|
import com.theoplayer.android.api.player.PresentationMode
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
const val IS_TRANSITION_INTO_PIP = "isTransitioningToPip"
|
|
30
|
+
const val IS_IN_PIP_MODE = "isInPictureInPictureMode"
|
|
31
|
+
const val ON_USER_LEAVE_HINT = "onUserLeaveHint"
|
|
32
|
+
const val ON_PIP_MODE_CHANGED = "onPictureInPictureModeChanged"
|
|
33
|
+
|
|
34
|
+
@Suppress("KotlinConstantConditions", "SimplifyBooleanWithConstants")
|
|
30
35
|
class PresentationManager(
|
|
31
36
|
private val viewCtx: ReactTHEOplayerContext,
|
|
32
37
|
private val reactContext: ThemedReactContext,
|
|
@@ -34,34 +39,28 @@ class PresentationManager(
|
|
|
34
39
|
) {
|
|
35
40
|
private var supportsPip = false
|
|
36
41
|
private var onUserLeaveHintReceiver: BroadcastReceiver? = null
|
|
42
|
+
private var onUserLeaveHintRunnable: Runnable = Runnable {
|
|
43
|
+
if (pipConfig.startsAutomatically == true) {
|
|
44
|
+
setPresentation(PresentationMode.PICTURE_IN_PICTURE)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
37
47
|
private var onPictureInPictureModeChanged: BroadcastReceiver? = null
|
|
38
48
|
private val pipUtils: PipUtils = PipUtils(viewCtx, reactContext)
|
|
39
49
|
private val fullScreenLayoutObserver = FullScreenLayoutObserver()
|
|
40
50
|
private val playerGroupRestoreOptions by lazy {
|
|
41
51
|
PlayerGroupRestoreOptions()
|
|
42
52
|
}
|
|
43
|
-
|
|
44
53
|
var currentPresentationMode: PresentationMode = PresentationMode.INLINE
|
|
45
54
|
private set
|
|
46
55
|
var currentPresentationModeChangeContext: PresentationModeChangeContext? = null
|
|
47
56
|
private set
|
|
48
|
-
|
|
49
57
|
var pipConfig: PipConfig = PipConfig()
|
|
50
58
|
|
|
51
59
|
init {
|
|
52
|
-
onUserLeaveHintReceiver = object : BroadcastReceiver() {
|
|
53
|
-
override fun onReceive(context: Context?, intent: Intent?) {
|
|
54
|
-
// Optionally into PiP mode when the app goes to background.
|
|
55
|
-
if (pipConfig.startsAutomatically == true) {
|
|
56
|
-
setPresentation(PresentationMode.PICTURE_IN_PICTURE)
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
60
|
onPictureInPictureModeChanged = object : BroadcastReceiver() {
|
|
61
61
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
62
|
-
val transitioningToPip = intent
|
|
63
|
-
|
|
64
|
-
val inPip = intent?.getBooleanExtra("isInPictureInPictureMode", false) ?: false
|
|
62
|
+
val transitioningToPip = intent?.getBooleanExtra(IS_TRANSITION_INTO_PIP, false) ?: false
|
|
63
|
+
val inPip = intent?.getBooleanExtra(IS_IN_PIP_MODE, false) ?: false
|
|
65
64
|
// Dispatch event on every PiP mode change
|
|
66
65
|
when {
|
|
67
66
|
transitioningToPip -> onEnterPip(true)
|
|
@@ -75,35 +74,50 @@ class PresentationManager(
|
|
|
75
74
|
reactContext.packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE)
|
|
76
75
|
}
|
|
77
76
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
77
|
+
reactContext.currentActivity?.let { activity ->
|
|
78
|
+
// On Android 16+, the broadcasted onUserLeaveHint comes too late to activate PiP presentation
|
|
79
|
+
// mode, and the activity will not go into PiP when the user backgrounds the app. In this case
|
|
80
|
+
// we rely on the newer addOnUserLeaveHintListener and ignore the broadcast event.
|
|
81
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
|
|
82
|
+
onUserLeaveHintReceiver = object : BroadcastReceiver() {
|
|
83
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
84
|
+
onUserLeaveHintRunnable.run()
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
(activity as? ComponentActivity)?.addOnUserLeaveHintListener(onUserLeaveHintRunnable)
|
|
89
|
+
}
|
|
87
90
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
91
|
+
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
92
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
93
|
+
activity.registerReceiver(
|
|
94
|
+
onUserLeaveHintReceiver,
|
|
95
|
+
IntentFilter(ON_USER_LEAVE_HINT),
|
|
96
|
+
Context.RECEIVER_EXPORTED
|
|
97
|
+
)
|
|
98
|
+
activity.registerReceiver(
|
|
99
|
+
onPictureInPictureModeChanged,
|
|
100
|
+
IntentFilter(ON_PIP_MODE_CHANGED),
|
|
101
|
+
Context.RECEIVER_EXPORTED
|
|
102
|
+
)
|
|
103
|
+
} else {
|
|
104
|
+
activity.registerReceiver(onUserLeaveHintReceiver, IntentFilter(ON_USER_LEAVE_HINT))
|
|
105
|
+
activity.registerReceiver(onPictureInPictureModeChanged, IntentFilter(ON_PIP_MODE_CHANGED))
|
|
106
|
+
}
|
|
97
107
|
}
|
|
98
108
|
}
|
|
99
109
|
|
|
100
110
|
fun destroy() {
|
|
101
111
|
try {
|
|
102
|
-
reactContext.currentActivity?.
|
|
103
|
-
|
|
112
|
+
reactContext.currentActivity?.let { activity ->
|
|
113
|
+
activity.unregisterReceiver(onUserLeaveHintReceiver)
|
|
114
|
+
(activity as? ComponentActivity)?.addOnUserLeaveHintListener(onUserLeaveHintRunnable)
|
|
115
|
+
activity.unregisterReceiver(onPictureInPictureModeChanged)
|
|
116
|
+
}
|
|
117
|
+
|
|
104
118
|
fullScreenLayoutObserver.remove()
|
|
105
119
|
pipUtils.destroy()
|
|
106
|
-
} catch (
|
|
120
|
+
} catch (_: Exception) {
|
|
107
121
|
}
|
|
108
122
|
}
|
|
109
123
|
|
|
@@ -126,25 +140,17 @@ class PresentationManager(
|
|
|
126
140
|
|
|
127
141
|
private fun enterPip() {
|
|
128
142
|
// PiP not supported
|
|
129
|
-
if (!supportsPip || Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
|
|
130
|
-
return
|
|
131
|
-
}
|
|
143
|
+
if (!supportsPip || Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
132
144
|
|
|
133
145
|
// Already in right PiP state?
|
|
134
|
-
if (currentPresentationMode == PresentationMode.PICTURE_IN_PICTURE)
|
|
135
|
-
return
|
|
136
|
-
}
|
|
146
|
+
if (currentPresentationMode == PresentationMode.PICTURE_IN_PICTURE) return
|
|
137
147
|
|
|
138
148
|
// Check to see whether this activity is in the process of finishing, either because you
|
|
139
149
|
// called finish on it or someone else has requested that it finished.
|
|
140
|
-
if (reactContext.currentActivity?.isFinishing == true)
|
|
141
|
-
return
|
|
142
|
-
}
|
|
150
|
+
if (reactContext.currentActivity?.isFinishing == true) return
|
|
143
151
|
|
|
144
152
|
// Check whether the special permission Picture-in-Picture is given.
|
|
145
|
-
if (!hasPipPermission())
|
|
146
|
-
return
|
|
147
|
-
}
|
|
153
|
+
if (!hasPipPermission()) return
|
|
148
154
|
|
|
149
155
|
try {
|
|
150
156
|
pipUtils.enable()
|
|
@@ -160,17 +166,14 @@ class PresentationManager(
|
|
|
160
166
|
private fun onEnterPip(transitioningToPip: Boolean = false) {
|
|
161
167
|
updatePresentationMode(
|
|
162
168
|
PresentationMode.PICTURE_IN_PICTURE,
|
|
163
|
-
if (transitioningToPip)
|
|
164
|
-
PresentationModeChangeContext(PresentationModeChangePipContext.TRANSITIONING_TO_PIP)
|
|
169
|
+
if (transitioningToPip) PresentationModeChangeContext(PresentationModeChangePipContext.TRANSITIONING_TO_PIP)
|
|
165
170
|
else null
|
|
166
171
|
)
|
|
167
172
|
}
|
|
168
173
|
|
|
169
174
|
private fun onExitPip() {
|
|
170
175
|
val pipCtx: PresentationModeChangePipContext =
|
|
171
|
-
if ((reactContext.currentActivity as? ComponentActivity)
|
|
172
|
-
?.lifecycle?.currentState == Lifecycle.State.CREATED
|
|
173
|
-
) {
|
|
176
|
+
if ((reactContext.currentActivity as? ComponentActivity)?.lifecycle?.currentState == Lifecycle.State.CREATED) {
|
|
174
177
|
PresentationModeChangePipContext.CLOSED
|
|
175
178
|
} else {
|
|
176
179
|
PresentationModeChangePipContext.RESTORED
|
|
@@ -182,6 +185,7 @@ class PresentationManager(
|
|
|
182
185
|
pipUtils.disable()
|
|
183
186
|
}
|
|
184
187
|
|
|
188
|
+
@Suppress("DEPRECATION")
|
|
185
189
|
private fun hasPipPermission(): Boolean {
|
|
186
190
|
val appOps = reactContext.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager?
|
|
187
191
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
@@ -192,7 +196,6 @@ class PresentationManager(
|
|
|
192
196
|
reactContext.packageName
|
|
193
197
|
) == AppOpsManager.MODE_ALLOWED
|
|
194
198
|
} else {
|
|
195
|
-
@Suppress("DEPRECATION")
|
|
196
199
|
appOps?.checkOpNoThrow(
|
|
197
200
|
AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
|
|
198
201
|
reactContext.applicationInfo.uid,
|
|
@@ -210,9 +213,7 @@ class PresentationManager(
|
|
|
210
213
|
}
|
|
211
214
|
|
|
212
215
|
private fun setFullscreen(fullscreen: Boolean) {
|
|
213
|
-
if ((fullscreen && currentPresentationMode == PresentationMode.FULLSCREEN) ||
|
|
214
|
-
(!fullscreen && currentPresentationMode == PresentationMode.INLINE)
|
|
215
|
-
) {
|
|
216
|
+
if ((fullscreen && currentPresentationMode == PresentationMode.FULLSCREEN) || (!fullscreen && currentPresentationMode == PresentationMode.INLINE)) {
|
|
216
217
|
return
|
|
217
218
|
}
|
|
218
219
|
val activity = reactContext.currentActivity ?: return
|
|
@@ -254,8 +255,7 @@ class PresentationManager(
|
|
|
254
255
|
val activity = reactContext.currentActivity ?: return null
|
|
255
256
|
// Try to search in parents and as a fallback option from root to bottom using depth-first order
|
|
256
257
|
return reactPlayerGroup?.getClosestParentOfType()
|
|
257
|
-
?: (activity.window.decorView.rootView as? ViewGroup)
|
|
258
|
-
?.getClosestParentOfType(false)
|
|
258
|
+
?: (activity.window.decorView.rootView as? ViewGroup)?.getClosestParentOfType(false)
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
private fun reparentPlayerToRoot() {
|
|
@@ -281,7 +281,9 @@ class PresentationManager(
|
|
|
281
281
|
|
|
282
282
|
// Re-parent the playerViewGroup from the root node to its original parent
|
|
283
283
|
removeView(playerGroup)
|
|
284
|
-
playerGroupRestoreOptions.parentNode?.addView(
|
|
284
|
+
playerGroupRestoreOptions.parentNode?.addView(
|
|
285
|
+
playerGroup, playerGroupRestoreOptions.childIndex ?: 0
|
|
286
|
+
)
|
|
285
287
|
playerGroupRestoreOptions.reset()
|
|
286
288
|
}
|
|
287
289
|
}
|
|
@@ -289,12 +291,9 @@ class PresentationManager(
|
|
|
289
291
|
// endregion
|
|
290
292
|
|
|
291
293
|
private fun updatePresentationMode(
|
|
292
|
-
presentationMode: PresentationMode,
|
|
293
|
-
context: PresentationModeChangeContext? = null
|
|
294
|
+
presentationMode: PresentationMode, context: PresentationModeChangeContext? = null
|
|
294
295
|
) {
|
|
295
|
-
if (presentationMode == currentPresentationMode &&
|
|
296
|
-
context == currentPresentationModeChangeContext
|
|
297
|
-
) {
|
|
296
|
+
if (presentationMode == currentPresentationMode && context == currentPresentationModeChangeContext) {
|
|
298
297
|
return
|
|
299
298
|
}
|
|
300
299
|
val prevPresentationMode = currentPresentationMode
|
|
@@ -2,6 +2,7 @@ package com.theoplayer.source
|
|
|
2
2
|
|
|
3
3
|
import android.text.TextUtils
|
|
4
4
|
import android.util.Log
|
|
5
|
+
import androidx.core.net.toUri
|
|
5
6
|
import com.google.gson.Gson
|
|
6
7
|
import com.theoplayer.android.api.ads.theoads.TheoAdDescription
|
|
7
8
|
import com.theoplayer.android.api.error.THEOplayerException
|
|
@@ -67,6 +68,7 @@ private const val PROP_OVERRIDE_LAYOUT = "overrideLayout"
|
|
|
67
68
|
private const val PROP_NETWORK_CODE = "networkCode"
|
|
68
69
|
private const val PROP_USE_ID3 = "useId3"
|
|
69
70
|
private const val PROP_RETRIEVE_POD_ID_URI = "retrievePodIdURI"
|
|
71
|
+
private const val PROP_INITIALIZATION_DELAY = "initializationDelay"
|
|
70
72
|
private const val PROP_SSE_ENDPOINT = "sseEndpoint"
|
|
71
73
|
private const val PROP_LATENCY_CONFIGURATION = "latencyConfiguration"
|
|
72
74
|
private const val PROP_PLAYBACK_PIPELINE = "playbackPipeline"
|
|
@@ -270,38 +272,17 @@ class SourceAdapter {
|
|
|
270
272
|
private fun parseSourceType(jsonTypedSource: JSONObject): SourceType? {
|
|
271
273
|
val type = jsonTypedSource.optString(PROP_TYPE)
|
|
272
274
|
if (type.isNotEmpty()) {
|
|
273
|
-
|
|
274
|
-
return SourceType.DASH
|
|
275
|
-
}
|
|
276
|
-
if ("application/x-mpegurl" == type) {
|
|
277
|
-
return SourceType.HLSX
|
|
278
|
-
}
|
|
279
|
-
if ("application/vnd.theo.hesp+json" == type) {
|
|
280
|
-
return SourceType.HESP
|
|
281
|
-
}
|
|
282
|
-
if ("application/vnd.apple.mpegurl" == type) {
|
|
283
|
-
return SourceType.HLS
|
|
284
|
-
}
|
|
285
|
-
if ("video/mp4" == type) {
|
|
286
|
-
return SourceType.MP4
|
|
287
|
-
}
|
|
288
|
-
if ("audio/mpeg" == type) {
|
|
289
|
-
return SourceType.MP3
|
|
290
|
-
}
|
|
275
|
+
SourceType.entries.find { it.mimeType == type }?.let { return it }
|
|
291
276
|
} else {
|
|
292
277
|
// No type given, check for known extension.
|
|
293
|
-
val src = jsonTypedSource.optString(PROP_SRC)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
return SourceType.MP4
|
|
302
|
-
}
|
|
303
|
-
if (src.endsWith(".mp3")) {
|
|
304
|
-
return SourceType.MP3
|
|
278
|
+
val src = jsonTypedSource.optString(PROP_SRC).toUri()
|
|
279
|
+
src.lastPathSegment?.let { fileName ->
|
|
280
|
+
when {
|
|
281
|
+
fileName.endsWith(".mpd") -> return SourceType.DASH
|
|
282
|
+
fileName.endsWith(".m3u8") -> return SourceType.HLSX
|
|
283
|
+
fileName.endsWith(".mp4") -> return SourceType.MP4
|
|
284
|
+
fileName.endsWith(".mp3") -> return SourceType.MP3
|
|
285
|
+
}
|
|
305
286
|
}
|
|
306
287
|
}
|
|
307
288
|
return null
|
|
@@ -365,6 +346,7 @@ class SourceAdapter {
|
|
|
365
346
|
overrideLayout = parseOverrideLayout(jsonAdDescription.optString(PROP_OVERRIDE_LAYOUT)),
|
|
366
347
|
useId3 = jsonAdDescription.optBoolean(PROP_USE_ID3, false),
|
|
367
348
|
retrievePodIdURI = jsonAdDescription.optString(PROP_RETRIEVE_POD_ID_URI).takeIf { it.isNotEmpty() },
|
|
349
|
+
initializationDelay = jsonAdDescription.optDouble(PROP_INITIALIZATION_DELAY).takeIf { it.isFinite() },
|
|
368
350
|
sseEndpoint = jsonAdDescription.optString(PROP_SSE_ENDPOINT).takeIf { it.isNotEmpty() },
|
|
369
351
|
)
|
|
370
352
|
}
|
|
@@ -10,12 +10,12 @@ import com.theoplayer.android.api.theolive.KeySystemConfiguration
|
|
|
10
10
|
private const val PROP_HESP_SRC = "hespSrc"
|
|
11
11
|
private const val PROP_HLS_SRC = "hlsSrc"
|
|
12
12
|
private const val PROP_AD_SRC = "adSrc"
|
|
13
|
+
private const val PROP_CDN = "cdn"
|
|
13
14
|
private const val PROP_TARGET_LATENCY = "targetLatency"
|
|
14
15
|
private const val PROP_WEIGHT = "weight"
|
|
15
16
|
private const val PROP_PRIORITY = "priority"
|
|
16
17
|
private const val PROP_CONTENT_PROTECTION = "contentProtection"
|
|
17
18
|
private const val PROP_INTEGRATION = "integration"
|
|
18
|
-
|
|
19
19
|
private const val PROP_WIDEVINE = "widevine"
|
|
20
20
|
private const val PROP_PLAYREADY = "playready"
|
|
21
21
|
private const val PROP_FAIRPLAY = "fairplay"
|
|
@@ -29,6 +29,7 @@ object EndpointAdapter {
|
|
|
29
29
|
endPoint.hespSrc?.let { putString(PROP_HESP_SRC, it) }
|
|
30
30
|
endPoint.hlsSrc?.let { putString(PROP_HLS_SRC, it) }
|
|
31
31
|
endPoint.adSrc?.let { putString(PROP_AD_SRC, it) }
|
|
32
|
+
endPoint.cdn?.let { putString(PROP_CDN, it) }
|
|
32
33
|
endPoint.targetLatency?.let { putDouble(PROP_TARGET_LATENCY, it) }
|
|
33
34
|
putInt(PROP_WEIGHT, endPoint.weight)
|
|
34
35
|
putInt(PROP_PRIORITY, endPoint.priority)
|
|
@@ -8,11 +8,12 @@ import com.theoplayer.android.api.event.player.theolive.EndpointLoadedEvent
|
|
|
8
8
|
import com.theoplayer.android.api.event.player.theolive.IntentToFallbackEvent
|
|
9
9
|
import com.theoplayer.android.api.event.player.theolive.TheoLiveEventTypes
|
|
10
10
|
import com.theoplayer.android.api.player.theolive.TheoLive
|
|
11
|
+
import com.theoplayer.util.PayloadBuilder
|
|
11
12
|
|
|
12
13
|
private const val EVENT_PROP_TYPE = "type"
|
|
13
|
-
|
|
14
14
|
private const val EVENT_PROP_DISTRIBUTION_ID = "distributionId"
|
|
15
15
|
private const val EVENT_PROP_ENDPOINT = "endpoint"
|
|
16
|
+
private const val EVENT_PROP_REASON = "reason"
|
|
16
17
|
|
|
17
18
|
class THEOliveEventAdapter(private val theoLiveApi: TheoLive, private val emitter: Emitter) {
|
|
18
19
|
|
|
@@ -55,9 +56,12 @@ class THEOliveEventAdapter(private val theoLiveApi: TheoLive, private val emitte
|
|
|
55
56
|
})
|
|
56
57
|
}
|
|
57
58
|
|
|
58
|
-
private fun onIntentOfFallback(
|
|
59
|
+
private fun onIntentOfFallback(event: IntentToFallbackEvent) {
|
|
59
60
|
emitter.emit(Arguments.createMap().apply {
|
|
60
61
|
putString(EVENT_PROP_TYPE, "intenttofallback")
|
|
62
|
+
event.reason?.let {
|
|
63
|
+
putMap(EVENT_PROP_REASON, PayloadBuilder().error(it.code.id.toString(), it.message).build())
|
|
64
|
+
}
|
|
61
65
|
})
|
|
62
66
|
}
|
|
63
67
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package com.theoplayer.theolive
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.*
|
|
4
|
+
import com.theoplayer.*
|
|
5
|
+
import com.theoplayer.util.ViewResolver
|
|
6
|
+
|
|
7
|
+
private const val TAG = "THEORCTTHEOliveModule"
|
|
8
|
+
|
|
9
|
+
private const val PROP_ENGINE_LATENCY = "engineLatency"
|
|
10
|
+
private const val PROP_DISTRIBUTION_LATENCY = "distributionLatency"
|
|
11
|
+
private const val PROP_PLAYER_LATENCY = "playerLatency"
|
|
12
|
+
private const val PROP_THEOLIVE_LATENCY = "theoliveLatency"
|
|
13
|
+
|
|
14
|
+
@Suppress("unused")
|
|
15
|
+
class THEOliveModule(context: ReactApplicationContext) : ReactContextBaseJavaModule(context) {
|
|
16
|
+
private val viewResolver: ViewResolver = ViewResolver(context)
|
|
17
|
+
|
|
18
|
+
override fun getName(): String {
|
|
19
|
+
return TAG
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@ReactMethod
|
|
23
|
+
fun currentLatency(tag: Int, promise: Promise) {
|
|
24
|
+
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
|
|
25
|
+
// NYI on Android SDK
|
|
26
|
+
promise.resolve(-1.0)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@ReactMethod
|
|
31
|
+
fun latencies(tag: Int, promise: Promise) {
|
|
32
|
+
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
|
|
33
|
+
// NYI on Android SDK
|
|
34
|
+
promise.resolve(Arguments.createMap().apply {
|
|
35
|
+
putDouble(PROP_ENGINE_LATENCY, -1.0)
|
|
36
|
+
putDouble(PROP_DISTRIBUTION_LATENCY, -1.0)
|
|
37
|
+
putDouble(PROP_PLAYER_LATENCY, -1.0)
|
|
38
|
+
putDouble(PROP_THEOLIVE_LATENCY, -1.0)
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@ReactMethod
|
|
44
|
+
fun setAuthToken(tag: Int, token: String) {
|
|
45
|
+
viewResolver.resolveViewByTag(tag) { view: ReactTHEOplayerView? ->
|
|
46
|
+
view?.player?.theoLive?.authToken = token
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -43,6 +43,7 @@ RCT_EXPORT_VIEW_PROPERTY(onNativeTextTrackListEvent, RCTDirectEventBlock);
|
|
|
43
43
|
RCT_EXPORT_VIEW_PROPERTY(onNativeTextTrackEvent, RCTDirectEventBlock);
|
|
44
44
|
RCT_EXPORT_VIEW_PROPERTY(onNativeMediaTrackListEvent, RCTDirectEventBlock);
|
|
45
45
|
RCT_EXPORT_VIEW_PROPERTY(onNativeMediaTrackEvent, RCTDirectEventBlock);
|
|
46
|
+
RCT_EXPORT_VIEW_PROPERTY(onNativeDeviceOrientationChanged, RCTDirectEventBlock);
|
|
46
47
|
RCT_EXPORT_VIEW_PROPERTY(onNativePlayerReady, RCTDirectEventBlock);
|
|
47
48
|
RCT_EXPORT_VIEW_PROPERTY(onNativePresentationModeChange, RCTDirectEventBlock);
|
|
48
49
|
RCT_EXPORT_VIEW_PROPERTY(onNativeResize, RCTDirectEventBlock);
|
|
@@ -279,4 +280,8 @@ RCT_EXTERN_METHOD(latencies:(nonnull NSNumber *)node
|
|
|
279
280
|
resolver:(RCTPromiseResolveBlock)resolve
|
|
280
281
|
rejecter:(RCTPromiseRejectBlock)reject)
|
|
281
282
|
|
|
283
|
+
RCT_EXTERN_METHOD(setAuthToken:(nonnull NSNumber *)node
|
|
284
|
+
token:(nonnull NSString *)token)
|
|
285
|
+
|
|
282
286
|
@end
|
|
287
|
+
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// THEOplayerRCTDeviceEventHandler.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import THEOplayerSDK
|
|
5
|
+
|
|
6
|
+
public class THEOplayerRCTDeviceEventHandler {
|
|
7
|
+
// MARK: Events
|
|
8
|
+
var onNativeDeviceOrientationChanged: RCTDirectEventBlock?
|
|
9
|
+
|
|
10
|
+
init() {
|
|
11
|
+
#if os(iOS)
|
|
12
|
+
UIDevice.current.beginGeneratingDeviceOrientationNotifications()
|
|
13
|
+
NotificationCenter.default.addObserver(
|
|
14
|
+
self,
|
|
15
|
+
selector: #selector(self.handleOrientationChange),
|
|
16
|
+
name: UIDevice.orientationDidChangeNotification,
|
|
17
|
+
object: nil
|
|
18
|
+
)
|
|
19
|
+
#endif
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func destroy() {
|
|
23
|
+
#if os(iOS)
|
|
24
|
+
NotificationCenter.default.removeObserver(self, name: UIDevice.orientationDidChangeNotification, object: nil)
|
|
25
|
+
UIDevice.current.endGeneratingDeviceOrientationNotifications()
|
|
26
|
+
#endif
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
#if os(iOS)
|
|
30
|
+
@objc private func handleOrientationChange() {
|
|
31
|
+
DispatchQueue.main.async {
|
|
32
|
+
if let forwardedNativeOrientationChanged = self.onNativeDeviceOrientationChanged {
|
|
33
|
+
forwardedNativeOrientationChanged([:])
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
#endif
|
|
38
|
+
}
|
|
@@ -132,6 +132,20 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
132
132
|
player.abr.preferredMaximumResolution = CGSize(width: Double(width), height: Double(height))
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
|
+
if let configuredStrategy = abrConfig["strategy"] as? String {
|
|
136
|
+
player.abr.strategy = ABRStrategyConfiguration(type: THEOplayerRCTTypeUtils.abrStrategyFromString(configuredStrategy))
|
|
137
|
+
} else if let configuredStrategy = abrConfig["strategy"] as? [String:Any] {
|
|
138
|
+
if let type = configuredStrategy["type"] as? String {
|
|
139
|
+
let abrType = THEOplayerRCTTypeUtils.abrStrategyFromString(type)
|
|
140
|
+
var abrMetadata: ABRMetadata?
|
|
141
|
+
if let metadata = configuredStrategy["metadata"] as? [String:Any],
|
|
142
|
+
let bitrate = metadata["bitrate"] as? Double {
|
|
143
|
+
abrMetadata = ABRMetadata(bitrate: bitrate)
|
|
144
|
+
}
|
|
145
|
+
let strategy = ABRStrategyConfiguration(type: abrType, metadata: abrMetadata)
|
|
146
|
+
player.abr.strategy = strategy
|
|
147
|
+
}
|
|
148
|
+
}
|
|
135
149
|
}
|
|
136
150
|
}
|
|
137
151
|
}
|
|
@@ -59,6 +59,7 @@ let SD_PROP_OVERRIDE_LAYOUT: String = "overrideLayout"
|
|
|
59
59
|
let SD_PROP_OVERRIDE_AD_SRC: String = "overrideAdSrc"
|
|
60
60
|
let SD_PROP_USE_ID3: String = "useId3"
|
|
61
61
|
let SD_PROP_RETRIEVE_POD_ID_URI: String = "retrievePodIdURI"
|
|
62
|
+
let SD_PROP_INITIALIZATION_DELAY: String = "initializationDelay"
|
|
62
63
|
let SD_PROP_HLS_DATE_RANGE: String = "hlsDateRange"
|
|
63
64
|
let SD_PROP_CMCD: String = "cmcd"
|
|
64
65
|
|