react-native-theoplayer 9.1.2 → 9.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 +28 -0
- package/README.md +2 -1
- package/android/build.gradle +12 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/java/com/theoplayer/ReactTHEOplayerContext.kt +14 -0
- package/android/src/main/java/com/theoplayer/presentation/PipUtils.kt +11 -1
- package/android/src/main/java/com/theoplayer/source/MillicastSourceAdapter.kt +104 -0
- package/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +7 -5
- package/android/src/main/java/com/theoplayer/track/TextTrackStyleAdapter.kt +4 -0
- package/ios/THEOplayerRCTBridge.m +2 -0
- package/ios/THEOplayerRCTMainEventHandler.swift +2 -2
- package/ios/THEOplayerRCTPlayerAPI.swift +26 -0
- package/ios/THEOplayerRCTSourceDescriptionBuilder.swift +5 -1
- package/ios/THEOplayerRCTView.swift +8 -1
- package/ios/millicast/THEOplayerRCTSourceDescriptionBuilder+Millicast.swift +189 -0
- package/ios/millicast/THEOplayerRCTView+Millicast.swift +21 -0
- package/ios/sideloadedMetadata/THEOplayerRCTSideloadedMetadataProcessor.swift +3 -1
- package/lib/commonjs/api/barrel.js +31 -20
- package/lib/commonjs/api/barrel.js.map +1 -1
- package/lib/commonjs/api/millicast/MillicastConnectOptions.js +2 -0
- package/lib/commonjs/api/millicast/MillicastConnectOptions.js.map +1 -0
- package/lib/commonjs/api/millicast/MillicastSource.js +6 -0
- package/lib/commonjs/api/millicast/MillicastSource.js.map +1 -0
- package/lib/commonjs/api/millicast/barrel.js +28 -0
- package/lib/commonjs/api/millicast/barrel.js.map +1 -0
- package/lib/commonjs/api/track/TextTrackStyle.js.map +1 -1
- package/lib/commonjs/internal/THEOplayerView.js +4 -4
- package/lib/commonjs/internal/THEOplayerView.js.map +1 -1
- package/lib/commonjs/internal/adapter/WebEventForwarder.js +4 -4
- package/lib/commonjs/internal/adapter/WebEventForwarder.js.map +1 -1
- package/lib/commonjs/internal/adapter/event/PlayerEvents.js +15 -1
- package/lib/commonjs/internal/adapter/event/PlayerEvents.js.map +1 -1
- package/lib/commonjs/internal/adapter/track/TextTrackStyleAdapter.js +10 -0
- package/lib/commonjs/internal/adapter/track/TextTrackStyleAdapter.js.map +1 -1
- package/lib/commonjs/internal/utils/Dimensions.js +4 -6
- package/lib/commonjs/internal/utils/Dimensions.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/millicast/MillicastConnectOptions.js +2 -0
- package/lib/module/api/millicast/MillicastConnectOptions.js.map +1 -0
- package/lib/module/api/millicast/MillicastSource.js +4 -0
- package/lib/module/api/millicast/MillicastSource.js.map +1 -0
- package/lib/module/api/millicast/barrel.js +5 -0
- package/lib/module/api/millicast/barrel.js.map +1 -0
- package/lib/module/api/track/TextTrackStyle.js.map +1 -1
- package/lib/module/internal/THEOplayerView.js +5 -5
- package/lib/module/internal/THEOplayerView.js.map +1 -1
- package/lib/module/internal/adapter/WebEventForwarder.js +5 -5
- package/lib/module/internal/adapter/WebEventForwarder.js.map +1 -1
- package/lib/module/internal/adapter/event/PlayerEvents.js +12 -0
- package/lib/module/internal/adapter/event/PlayerEvents.js.map +1 -1
- package/lib/module/internal/adapter/track/TextTrackStyleAdapter.js +10 -0
- package/lib/module/internal/adapter/track/TextTrackStyleAdapter.js.map +1 -1
- package/lib/module/internal/utils/Dimensions.js +5 -7
- package/lib/module/internal/utils/Dimensions.js.map +1 -1
- package/lib/module/manifest.json +1 -1
- package/lib/typescript/api/backgroundAudio/BackgroundAudioConfiguration.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/event/PlayerEvent.d.ts +12 -0
- package/lib/typescript/api/event/PlayerEvent.d.ts.map +1 -1
- package/lib/typescript/api/millicast/MillicastConnectOptions.d.ts +204 -0
- package/lib/typescript/api/millicast/MillicastConnectOptions.d.ts.map +1 -0
- package/lib/typescript/api/millicast/MillicastSource.d.ts +51 -0
- package/lib/typescript/api/millicast/MillicastSource.d.ts.map +1 -0
- package/lib/typescript/api/millicast/barrel.d.ts +3 -0
- package/lib/typescript/api/millicast/barrel.d.ts.map +1 -0
- package/lib/typescript/api/track/TextTrackStyle.d.ts +7 -0
- package/lib/typescript/api/track/TextTrackStyle.d.ts.map +1 -1
- package/lib/typescript/internal/THEOplayerView.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 +9 -3
- package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/event/native/NativePlayerEvent.d.ts +12 -0
- package/lib/typescript/internal/adapter/event/native/NativePlayerEvent.d.ts.map +1 -1
- package/lib/typescript/internal/adapter/track/TextTrackStyleAdapter.d.ts +3 -0
- package/lib/typescript/internal/adapter/track/TextTrackStyleAdapter.d.ts.map +1 -1
- package/lib/typescript/internal/utils/Dimensions.d.ts.map +1 -1
- package/package.json +3 -2
- package/react-native-theoplayer.json +1 -1
- package/react-native-theoplayer.podspec +15 -9
- package/src/api/backgroundAudio/BackgroundAudioConfiguration.ts +2 -2
- package/src/api/barrel.ts +1 -0
- package/src/api/event/PlayerEvent.ts +14 -0
- package/src/api/millicast/MillicastConnectOptions.ts +229 -0
- package/src/api/millicast/MillicastSource.ts +56 -0
- package/src/api/millicast/barrel.ts +2 -0
- package/src/api/track/TextTrackStyle.ts +8 -0
- package/src/internal/THEOplayerView.tsx +10 -6
- package/src/internal/adapter/THEOplayerWebAdapter.ts +1 -1
- package/src/internal/adapter/WebEventForwarder.ts +8 -4
- package/src/internal/adapter/event/PlayerEvents.ts +17 -2
- package/src/internal/adapter/event/native/NativePlayerEvent.ts +14 -0
- package/src/internal/adapter/track/TextTrackStyleAdapter.ts +12 -0
- package/src/internal/utils/Dimensions.ts +5 -7
- package/src/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ 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.3.0] - 25-06-02
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- Fixed an issue on iOS where the fullscreen dimensions could become wrong when the device is Flat Up or Flat Down on the table, or in an angle close to these.
|
|
13
|
+
- Fixed an issue on Android where switching to the PiP window during an IMA ad caused a crash on Android 8-11.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- Added `currentTime` property to `SeekingEvent` and `SeekedEvent`, indicating the player time to which is being seeked.
|
|
18
|
+
|
|
19
|
+
## [9.2.0] - 25-05-20
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Added support for Millicast on iOS, Android and Web.
|
|
24
|
+
- Added support for `TextTrackStyle.edgeColor` on Android.
|
|
25
|
+
- Added support for THEOads on tvOS.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- Fixed an issue where the last cue of a VTT was not parsed if there is no newline after it.
|
|
30
|
+
- Fixed an issue on Android where a conversion build error would occur when targeting SDK version 9.4.
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- Changed the fullscreen dimensions calculation on iOS to make use of the native screen width and height, while taking into account the device orientation.
|
|
35
|
+
|
|
8
36
|
## [9.1.2] - 25-05-14
|
|
9
37
|
|
|
10
38
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# React Native THEOplayer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
<img src="./doc/logo-optiview-dark.png#gh-dark-mode-only" height="120" alt="OptiView logo"><img src="./doc/logo-optiview-light.png#gh-light-mode-only" height="120" alt="OptiView logo"><img src="./doc/logo-react-native.png" height="120" alt="React Native logo">
|
|
4
4
|
|
|
5
5
|
## License
|
|
6
6
|
|
|
@@ -183,6 +183,7 @@ This section gives an overview of features, limitations and known issues:
|
|
|
183
183
|
- [Fullscreen presentation](./doc/fullscreen.md)
|
|
184
184
|
- [Media Caching](./doc/media-caching.md)
|
|
185
185
|
- [Migrating to THEOplayer 9.x🔥](./doc/migrating-to-react-native-theoplayer-9.md)
|
|
186
|
+
- [Millicast](./doc/millicast.md)
|
|
186
187
|
- [Picture-in-Picture (PiP)](./doc/pip.md)
|
|
187
188
|
- [Subtitles, Closed Captions and Metadata tracks](./doc/texttracks.md)
|
|
188
189
|
- [Limitations and known issues](./doc/limitations.md)
|
package/android/build.gradle
CHANGED
|
@@ -40,6 +40,7 @@ def enabledTHEOads = safeExtGet("THEOplayer_extensionTHEOads", 'false').toBoolea
|
|
|
40
40
|
def enabledAds = enabledGoogleIMA || enabledGoogleDAI || enabledTHEOads
|
|
41
41
|
def enabledCast = safeExtGet("THEOplayer_extensionCast", 'false').toBoolean()
|
|
42
42
|
def enabledMediaSession = safeExtGet("THEOplayer_extensionMediaSession", 'true').toBoolean()
|
|
43
|
+
def enabledMillicast = safeExtGet("THEOplayer_extensionMillicast", 'false').toBoolean()
|
|
43
44
|
|
|
44
45
|
android {
|
|
45
46
|
compileSdk safeExtGet('THEOplayer_compileSdkVersion', 34)
|
|
@@ -76,6 +77,7 @@ android {
|
|
|
76
77
|
buildConfigField "boolean", "EXTENSION_ADS", "${enabledAds}"
|
|
77
78
|
buildConfigField "boolean", "EXTENSION_CAST", "${enabledCast}"
|
|
78
79
|
buildConfigField "boolean", "EXTENSION_MEDIASESSION", "${enabledMediaSession}"
|
|
80
|
+
buildConfigField "boolean", "EXTENSION_MILLICAST", "${enabledMillicast}"
|
|
79
81
|
|
|
80
82
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
81
83
|
|
|
@@ -173,6 +175,16 @@ dependencies {
|
|
|
173
175
|
println('Disable THEOplayer cast extension.')
|
|
174
176
|
compileOnly "com.theoplayer.theoplayer-sdk-android:integration-cast:$theoplayer_sdk_version"
|
|
175
177
|
}
|
|
178
|
+
|
|
179
|
+
if (enabledMillicast) {
|
|
180
|
+
println('Enable THEOplayer millicast extension.')
|
|
181
|
+
implementation "com.theoplayer.theoplayer-sdk-android:integration-millicast:${theoplayer_sdk_version}"
|
|
182
|
+
implementation "com.millicast:millicast-sdk-android:2.0.0"
|
|
183
|
+
} else {
|
|
184
|
+
println('Disable THEOplayer millicast extension.')
|
|
185
|
+
compileOnly "com.theoplayer.theoplayer-sdk-android:integration-millicast:${theoplayer_sdk_version}"
|
|
186
|
+
compileOnly "com.millicast:millicast-sdk-android:2.0.0"
|
|
187
|
+
}
|
|
176
188
|
}
|
|
177
189
|
|
|
178
190
|
// Make sure to align all ads extension versions
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# is disabled: it is expected.
|
|
3
3
|
-dontwarn com.theoplayer.android.api.**
|
|
4
4
|
-dontwarn com.google.android.gms.cast.**
|
|
5
|
+
-dontwarn com.millicast.**
|
|
5
6
|
|
|
6
7
|
# We rely on gson to instantiate some source classes from json, so make sure they are not obfuscated.
|
|
7
8
|
-keep,includedescriptorclasses class com.theoplayer.android.api.source.** { *; }
|
|
@@ -26,6 +26,8 @@ import com.theoplayer.android.api.cast.CastIntegration
|
|
|
26
26
|
import com.theoplayer.android.api.cast.CastIntegrationFactory
|
|
27
27
|
import com.theoplayer.android.api.event.EventListener
|
|
28
28
|
import com.theoplayer.android.api.event.player.*
|
|
29
|
+
import com.theoplayer.android.api.millicast.MillicastIntegration
|
|
30
|
+
import com.theoplayer.android.api.millicast.MillicastIntegrationFactory
|
|
29
31
|
import com.theoplayer.android.api.player.Player
|
|
30
32
|
import com.theoplayer.android.api.player.RenderingTarget
|
|
31
33
|
import com.theoplayer.android.connector.mediasession.MediaSessionConnector
|
|
@@ -90,6 +92,7 @@ class ReactTHEOplayerContext private constructor(
|
|
|
90
92
|
@Suppress("UnstableApiUsage")
|
|
91
93
|
var wasPlayingOnHostPause: Boolean = false
|
|
92
94
|
private var isHostPaused: Boolean = false
|
|
95
|
+
private var millicastIntegration: MillicastIntegration? = null
|
|
93
96
|
|
|
94
97
|
private val isBackgroundAudioEnabled: Boolean
|
|
95
98
|
get() = backgroundAudioConfig.enabled
|
|
@@ -332,6 +335,17 @@ class ReactTHEOplayerContext private constructor(
|
|
|
332
335
|
} catch (e: Exception) {
|
|
333
336
|
Log.w(TAG, "Failed to configure Cast integration ${e.message}")
|
|
334
337
|
}
|
|
338
|
+
try {
|
|
339
|
+
if (BuildConfig.EXTENSION_MILLICAST) {
|
|
340
|
+
millicastIntegration = MillicastIntegrationFactory.createMillicastIntegration()
|
|
341
|
+
.also {
|
|
342
|
+
playerView.player.addIntegration(it)
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
} catch (e: Exception) {
|
|
346
|
+
Log.w(TAG, "Failed to configure Millicast integration ${e.message}")
|
|
347
|
+
}
|
|
348
|
+
|
|
335
349
|
// Add other future integrations here.
|
|
336
350
|
}
|
|
337
351
|
|
|
@@ -252,7 +252,17 @@ class PipUtils(
|
|
|
252
252
|
val intent = Intent(ACTION_MEDIA_CONTROL).putExtra(EXTRA_ACTION, requestCode)
|
|
253
253
|
val pendingIntent =
|
|
254
254
|
PendingIntent.getBroadcast(reactContext, requestCode, intent, PendingIntent.FLAG_IMMUTABLE)
|
|
255
|
-
val icon: Icon = Icon.createWithResource(
|
|
255
|
+
val icon: Icon = Icon.createWithResource(
|
|
256
|
+
reactContext,
|
|
257
|
+
when {
|
|
258
|
+
enabled -> iconId
|
|
259
|
+
// On Android 8-11 devices, if you go to PiP during an IMA ad on Android,
|
|
260
|
+
// the NO_ICON causes a System UI crash.
|
|
261
|
+
Build.VERSION.SDK_INT in Build.VERSION_CODES.O..Build.VERSION_CODES.R ->
|
|
262
|
+
android.R.drawable.screen_background_light_transparent
|
|
263
|
+
else -> NO_ICON
|
|
264
|
+
}
|
|
265
|
+
)
|
|
256
266
|
val title = reactContext.getString(titleId)
|
|
257
267
|
val desc = reactContext.getString(descId)
|
|
258
268
|
return RemoteAction(icon, title, desc, pendingIntent)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
package com.theoplayer.source
|
|
2
|
+
|
|
3
|
+
import com.millicast.subscribers.ForcePlayoutDelay
|
|
4
|
+
import com.millicast.subscribers.Option
|
|
5
|
+
import com.theoplayer.BuildConfig
|
|
6
|
+
import com.theoplayer.android.api.error.ErrorCode
|
|
7
|
+
import com.theoplayer.android.api.error.THEOplayerException
|
|
8
|
+
import com.theoplayer.android.api.millicast.MillicastSource
|
|
9
|
+
import org.json.JSONObject
|
|
10
|
+
|
|
11
|
+
private const val PROP_SRC = "src"
|
|
12
|
+
private const val PROP_STREAM_ACCOUNT_ID = "streamAccountId"
|
|
13
|
+
private const val PROP_API_URL = "apiUrl"
|
|
14
|
+
private const val PROP_SUBSCRIBER_TOKEN = "subscriberToken"
|
|
15
|
+
private const val PROP_CONNECT_OPTIONS = "connectOptions"
|
|
16
|
+
|
|
17
|
+
private const val BWE_MONITOR_DURATION_US = "bweMonitorDurationUs"
|
|
18
|
+
private const val BWE_RATE_CHANGE_PERCENTAGE = "bweRateChangePercentage"
|
|
19
|
+
private const val DISABLE_AUDIO = "disableAudio"
|
|
20
|
+
private const val DTX = "dtx"
|
|
21
|
+
private const val EXCLUDED_SOURCE_ID = "excludedSourceIds"
|
|
22
|
+
private const val FORCE_PLAYOUT_DELAY = "forcePlayoutDelay"
|
|
23
|
+
private const val MINIMUM_DELAY = "min"
|
|
24
|
+
private const val MAXIMUM_DELAY = "max"
|
|
25
|
+
private const val FORCE_SMOOTH = "forceSmooth"
|
|
26
|
+
private const val JITTER_MINIMUM_DELAY_MS = "jitterMinimumDelayMs"
|
|
27
|
+
private const val MAXIMUM_BITRATE = "maximumBitrate"
|
|
28
|
+
private const val MULTIPLEXED_AUDIO_TRACK = "multiplexedAudioTracks"
|
|
29
|
+
private const val PINNED_SOURCE_ID = "pinnedSourceId"
|
|
30
|
+
private const val RTC_EVENT_LOG_OUTPUT_PATH = "rtcEventLogOutputPath"
|
|
31
|
+
private const val STATS_DELAY_MS = "statsDelayMs"
|
|
32
|
+
private const val STEREO = "stereo"
|
|
33
|
+
private const val UPWARDS_LAYER_WAIT_TIME_MS = "upwardsLayerWaitTimeMs"
|
|
34
|
+
|
|
35
|
+
private const val ERROR_MILLICAST_NOT_ENABLED = "Millicast support not enabled."
|
|
36
|
+
|
|
37
|
+
fun parseMillicastSource(jsonTypedSource: JSONObject): MillicastSource {
|
|
38
|
+
// Check whether the integration was enabled
|
|
39
|
+
if (!BuildConfig.EXTENSION_MILLICAST) {
|
|
40
|
+
throw THEOplayerException(ErrorCode.SOURCE_INVALID, ERROR_MILLICAST_NOT_ENABLED)
|
|
41
|
+
}
|
|
42
|
+
return MillicastSource(
|
|
43
|
+
src = jsonTypedSource.optString(PROP_SRC),
|
|
44
|
+
streamAccountId = jsonTypedSource.optString(PROP_STREAM_ACCOUNT_ID),
|
|
45
|
+
apiUrl = jsonTypedSource.optString(PROP_API_URL),
|
|
46
|
+
subscriberToken = when (val token = jsonTypedSource.optString(PROP_SUBSCRIBER_TOKEN)) {
|
|
47
|
+
"" -> null
|
|
48
|
+
else -> token
|
|
49
|
+
},
|
|
50
|
+
connectOptions = jsonTypedSource.optJSONObject(PROP_CONNECT_OPTIONS)
|
|
51
|
+
?.let { buildMillicastConnectOptions(it) }
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private fun buildMillicastConnectOptions(jsonObject: JSONObject): Option {
|
|
56
|
+
return Option(
|
|
57
|
+
bweMonitorDurationUs = when (val bweMonitorDurationUs = jsonObject.optInt(BWE_MONITOR_DURATION_US, -1)) {
|
|
58
|
+
-1 -> null
|
|
59
|
+
else -> bweMonitorDurationUs
|
|
60
|
+
},
|
|
61
|
+
bweRateChangePercentage = when (val bweRateChangePercentage = jsonObject.optDouble(BWE_RATE_CHANGE_PERCENTAGE, -1.0)) {
|
|
62
|
+
-1.0 -> null
|
|
63
|
+
else -> bweRateChangePercentage
|
|
64
|
+
},
|
|
65
|
+
disableAudio = jsonObject.optBoolean(DISABLE_AUDIO),
|
|
66
|
+
dtx = jsonObject.optBoolean(DTX),
|
|
67
|
+
excludedSourceId = when (val excludedSourceId = jsonObject.optJSONArray(EXCLUDED_SOURCE_ID)) {
|
|
68
|
+
null -> arrayOf()
|
|
69
|
+
else -> Array(excludedSourceId.length()) {
|
|
70
|
+
excludedSourceId.getString(it)
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
forcePlayoutDelay = jsonObject.optJSONObject(FORCE_PLAYOUT_DELAY)?.let {
|
|
74
|
+
ForcePlayoutDelay(
|
|
75
|
+
minimumDelay = it.getInt(MINIMUM_DELAY),
|
|
76
|
+
maximumDelay = it.getInt(MAXIMUM_DELAY)
|
|
77
|
+
)
|
|
78
|
+
},
|
|
79
|
+
forceSmooth = jsonObject.optBoolean(FORCE_SMOOTH),
|
|
80
|
+
jitterMinimumDelayMs = jsonObject.optInt(JITTER_MINIMUM_DELAY_MS),
|
|
81
|
+
maximumBitrate = when (val maximumBitrate = jsonObject.optInt(MAXIMUM_BITRATE, -1)) {
|
|
82
|
+
-1 -> null
|
|
83
|
+
else -> maximumBitrate
|
|
84
|
+
},
|
|
85
|
+
multiplexedAudioTrack = when (val multiplexedAudioTrack = jsonObject.optString(MULTIPLEXED_AUDIO_TRACK)) {
|
|
86
|
+
"" -> UByte.MIN_VALUE
|
|
87
|
+
else -> multiplexedAudioTrack.toUByte()
|
|
88
|
+
},
|
|
89
|
+
pinnedSourceId = when (val pinnedSourceId = jsonObject.optString(PINNED_SOURCE_ID)) {
|
|
90
|
+
"" -> null
|
|
91
|
+
else -> pinnedSourceId
|
|
92
|
+
},
|
|
93
|
+
rtcEventLogOutputPath = jsonObject.optString(RTC_EVENT_LOG_OUTPUT_PATH),
|
|
94
|
+
statsDelayMs = when (val statsDelayMs = jsonObject.optInt(STATS_DELAY_MS, -1)) {
|
|
95
|
+
-1 -> 1000
|
|
96
|
+
else -> statsDelayMs
|
|
97
|
+
},
|
|
98
|
+
stereo = jsonObject.optBoolean(STEREO, true),
|
|
99
|
+
upwardsLayerWaitTimeMs = when (val upwardsLayerWaitTimeMs = jsonObject.optInt(UPWARDS_LAYER_WAIT_TIME_MS, -1)) {
|
|
100
|
+
-1 -> null
|
|
101
|
+
else -> upwardsLayerWaitTimeMs
|
|
102
|
+
}
|
|
103
|
+
)
|
|
104
|
+
}
|
|
@@ -79,6 +79,7 @@ private const val ERROR_MISSING_CSAI_INTEGRATION = "Missing CSAI integration"
|
|
|
79
79
|
private const val PROP_SSAI_INTEGRATION_GOOGLE_DAI = "google-dai"
|
|
80
80
|
|
|
81
81
|
private const val INTEGRATION_THEOLIVE = "theolive"
|
|
82
|
+
private const val TYPE_MILLICAST = "millicast"
|
|
82
83
|
private const val PLAYBACK_PIPELINE_LEGACY = "legacy"
|
|
83
84
|
|
|
84
85
|
private const val PROP_CMCD = "cmcd"
|
|
@@ -107,7 +108,7 @@ class SourceAdapter {
|
|
|
107
108
|
// CMCD
|
|
108
109
|
var cmcdTransmissionMode: CMCDTransmissionMode? = null
|
|
109
110
|
if (jsonSourceObject.has(PROP_CMCD)) {
|
|
110
|
-
cmcdTransmissionMode = parseCmcdTransmissionMode(jsonSourceObject.getJSONObject(PROP_CMCD))
|
|
111
|
+
cmcdTransmissionMode = parseCmcdTransmissionMode(jsonSourceObject.getJSONObject(PROP_CMCD))
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
// typed sources
|
|
@@ -184,8 +185,9 @@ class SourceAdapter {
|
|
|
184
185
|
@Throws(THEOplayerException::class)
|
|
185
186
|
private fun parseTypedSource(jsonTypedSource: JSONObject, cmcdTransmissionMode: CMCDTransmissionMode? = null): TypedSource {
|
|
186
187
|
// Some integrations do not support the Builder pattern
|
|
187
|
-
return when
|
|
188
|
-
INTEGRATION_THEOLIVE -> parseTheoLiveSource(jsonTypedSource)
|
|
188
|
+
return when {
|
|
189
|
+
(jsonTypedSource.optString(PROP_INTEGRATION)) == INTEGRATION_THEOLIVE -> parseTheoLiveSource(jsonTypedSource)
|
|
190
|
+
(jsonTypedSource.optString(PROP_TYPE)) == TYPE_MILLICAST -> parseMillicastSource(jsonTypedSource)
|
|
189
191
|
else -> parseTypedSourceFromBuilder(jsonTypedSource, cmcdTransmissionMode)
|
|
190
192
|
}
|
|
191
193
|
}
|
|
@@ -406,7 +408,7 @@ class SourceAdapter {
|
|
|
406
408
|
}
|
|
407
409
|
|
|
408
410
|
private fun parseMetadataDescription(metadataDescription: JSONObject): MetadataDescription {
|
|
409
|
-
val metadata = HashMap<String, Any
|
|
411
|
+
val metadata = HashMap<String, Any?>()
|
|
410
412
|
val keys = metadataDescription.keys()
|
|
411
413
|
while (keys.hasNext()) {
|
|
412
414
|
val key = keys.next()
|
|
@@ -420,7 +422,7 @@ class SourceAdapter {
|
|
|
420
422
|
Log.w(TAG, "Failed to parse metadata key $key")
|
|
421
423
|
}
|
|
422
424
|
}
|
|
423
|
-
return MetadataDescription(metadata)
|
|
425
|
+
return MetadataDescription(metadata as MutableMap<String, Any?>)
|
|
424
426
|
}
|
|
425
427
|
|
|
426
428
|
private fun parseDashConfig(dashConfig: JSONObject): DashPlaybackConfiguration {
|
|
@@ -6,6 +6,7 @@ import com.theoplayer.android.api.player.track.texttrack.TextTrackStyle
|
|
|
6
6
|
|
|
7
7
|
private const val PROP_BACKGROUND_COLOR = "backgroundColor"
|
|
8
8
|
private const val PROP_EDGE_STYLE = "edgeStyle"
|
|
9
|
+
private const val PROP_EDGE_COLOR = "edgeColor"
|
|
9
10
|
private const val PROP_FONT_COLOR = "fontColor"
|
|
10
11
|
private const val PROP_FONT_FAMILY = "fontFamily"
|
|
11
12
|
private const val PROP_FONT_SIZE = "fontSize"
|
|
@@ -30,6 +31,9 @@ object TextTrackStyleAdapter {
|
|
|
30
31
|
if (props.hasKey(PROP_EDGE_STYLE)) {
|
|
31
32
|
style.edgeType = edgeStyleFromProps(props.getString(PROP_EDGE_STYLE))
|
|
32
33
|
}
|
|
34
|
+
if (props.hasKey(PROP_EDGE_COLOR)) {
|
|
35
|
+
style.edgeColor = colorFromBridgeColor(props.getMap(PROP_EDGE_COLOR)) ?: Color.WHITE
|
|
36
|
+
}
|
|
33
37
|
if (props.hasKey(PROP_FONT_COLOR)) {
|
|
34
38
|
style.fontColor = colorFromBridgeColor(props.getMap(PROP_FONT_COLOR)) ?: Color.WHITE
|
|
35
39
|
}
|
|
@@ -118,6 +118,8 @@ RCT_EXTERN_METHOD(setTextTrackStyle:(nonnull NSNumber *)node
|
|
|
118
118
|
RCT_EXTERN_METHOD(setKeepScreenOn:(nonnull NSNumber *)node
|
|
119
119
|
keepScreenOn:(BOOL)keepScreenOn)
|
|
120
120
|
|
|
121
|
+
RCT_EXTERN__BLOCKING_SYNCHRONOUS_METHOD(getUsableScreenDimensions)
|
|
122
|
+
|
|
121
123
|
@end
|
|
122
124
|
|
|
123
125
|
// ----------------------------------------------------------------------------
|
|
@@ -218,7 +218,7 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
218
218
|
self.seekingListener = player.addEventListener(type: PlayerEventTypes.SEEKING) { [weak self] event in
|
|
219
219
|
if DEBUG_THEOPLAYER_EVENTS { PrintUtils.printLog(logText: "[NATIVE] Received SEEKING event from THEOplayer") }
|
|
220
220
|
if let forwardedSeekingEvent = self?.onNativeSeeking {
|
|
221
|
-
forwardedSeekingEvent([:])
|
|
221
|
+
forwardedSeekingEvent(["currentTime": 1e3 * event.currentTime])
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
224
|
if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] Seeking listener attached to THEOplayer") }
|
|
@@ -227,7 +227,7 @@ public class THEOplayerRCTMainEventHandler {
|
|
|
227
227
|
self.timeUpdateListener = player.addEventListener(type: PlayerEventTypes.SEEKED) { [weak self] event in
|
|
228
228
|
if DEBUG_THEOPLAYER_EVENTS { PrintUtils.printLog(logText: "[NATIVE] Received SEEKED event from THEOplayer") }
|
|
229
229
|
if let forwardedSeekedEvent = self?.onNativeSeeked {
|
|
230
|
-
forwardedSeekedEvent([:])
|
|
230
|
+
forwardedSeekedEvent(["currentTime": 1e3 * event.currentTime])
|
|
231
231
|
}
|
|
232
232
|
}
|
|
233
233
|
if DEBUG_EVENTHANDLER { PrintUtils.printLog(logText: "[NATIVE] Seeked listener attached to THEOplayer") }
|
|
@@ -383,4 +383,30 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
|
|
|
383
383
|
}
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
|
+
|
|
387
|
+
@objc(getUsableScreenDimensions)
|
|
388
|
+
func getUsableScreenDimensions() -> NSDictionary {
|
|
389
|
+
let boundsSize: CGSize = UIScreen.main.bounds.size;
|
|
390
|
+
let smallest = boundsSize.width < boundsSize.height ? boundsSize.width : boundsSize.height
|
|
391
|
+
let biggest = boundsSize.width < boundsSize.height ? boundsSize.height : boundsSize.width
|
|
392
|
+
|
|
393
|
+
#if os(iOS)
|
|
394
|
+
var orientation = UIInterfaceOrientation.unknown
|
|
395
|
+
DispatchQueue.main.sync {
|
|
396
|
+
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { // can only be accessed from main thread.
|
|
397
|
+
orientation = windowScene.interfaceOrientation
|
|
398
|
+
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] Current UI orientation: \(orientation.rawValue <= 2 ? "portrait" : "landscape")")}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// iOS, portait
|
|
403
|
+
if orientation.rawValue <= 2 {
|
|
404
|
+
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] portrait dimensions: \(smallest) - \(biggest)")}
|
|
405
|
+
return ["width": smallest, "height": biggest] // ios portrait
|
|
406
|
+
}
|
|
407
|
+
#endif
|
|
408
|
+
// tvOS and iOS landscape
|
|
409
|
+
if DEBUG_PLAYER_API { PrintUtils.printLog(logText: "[NATIVE] landscape dimensions: \(biggest) - \(smallest)")}
|
|
410
|
+
return ["width": biggest, "height": smallest] // ios landscape or tvos
|
|
411
|
+
}
|
|
386
412
|
}
|
|
@@ -189,7 +189,11 @@ class THEOplayerRCTSourceDescriptionBuilder {
|
|
|
189
189
|
if let integration = typedSourceData[SD_PROP_INTEGRATION] as? String, integration == "theolive" {
|
|
190
190
|
return THEOplayerRCTSourceDescriptionBuilder.buildTHEOliveDescription(typedSourceData)
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
|
|
193
|
+
if let type = typedSourceData[SD_PROP_TYPE] as? String, type == "millicast" {
|
|
194
|
+
return THEOplayerRCTSourceDescriptionBuilder.buildMillicastDescription(typedSourceData)
|
|
195
|
+
}
|
|
196
|
+
|
|
193
197
|
if let src = typedSourceData[SD_PROP_SRC] as? String {
|
|
194
198
|
// extract the type
|
|
195
199
|
let type = typedSourceData[SD_PROP_TYPE] as? String ?? THEOplayerRCTSourceDescriptionBuilder.extractMimeType(src)
|
|
@@ -14,6 +14,9 @@ import GoogleInteractiveMediaAds
|
|
|
14
14
|
#if canImport(THEOplayerGoogleCastIntegration)
|
|
15
15
|
import THEOplayerGoogleCastIntegration
|
|
16
16
|
#endif
|
|
17
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
18
|
+
import THEOplayerMillicastIntegration
|
|
19
|
+
#endif
|
|
17
20
|
|
|
18
21
|
public class THEOplayerRCTView: UIView {
|
|
19
22
|
// MARK: Members
|
|
@@ -46,7 +49,10 @@ public class THEOplayerRCTView: UIView {
|
|
|
46
49
|
#if canImport(THEOplayerGoogleCastIntegration)
|
|
47
50
|
var castIntegration: THEOplayerGoogleCastIntegration.CastIntegration?
|
|
48
51
|
#endif
|
|
49
|
-
|
|
52
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
53
|
+
var millicastIntegration: THEOplayerMillicastIntegration.MillicastIntegration?
|
|
54
|
+
#endif
|
|
55
|
+
|
|
50
56
|
var mediaControlConfig = MediaControlConfig() {
|
|
51
57
|
didSet {
|
|
52
58
|
self.remoteCommandsManager.setMediaControlConfig(mediaControlConfig)
|
|
@@ -173,6 +179,7 @@ public class THEOplayerRCTView: UIView {
|
|
|
173
179
|
self.initAdsIntegration()
|
|
174
180
|
self.initCastIntegration()
|
|
175
181
|
self.initTheoAdsIntegration()
|
|
182
|
+
self.initMillicastIntegration()
|
|
176
183
|
self.initBackgroundAudio()
|
|
177
184
|
self.initPip()
|
|
178
185
|
return self.player
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// THEOplayerRCTSourceDescriptionBuilder+Millicast.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import THEOplayerSDK
|
|
5
|
+
import UIKit
|
|
6
|
+
|
|
7
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
8
|
+
import MillicastSDK
|
|
9
|
+
import THEOplayerMillicastIntegration
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
let SD_PROP_ACCOUNTID: String = "streamAccountId"
|
|
13
|
+
let SD_PROP_SUBSCRIBERTOKEN: String = "subscriberToken"
|
|
14
|
+
let SD_PROP_CONNECTOPTIONS: String = "connectOptions"
|
|
15
|
+
|
|
16
|
+
extension THEOplayerRCTSourceDescriptionBuilder {
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
Builds a THEOplayer SourceDescription that can be passed as a source for the THEOplayer.
|
|
20
|
+
- returns: a Millicast TypedSource.
|
|
21
|
+
*/
|
|
22
|
+
static func buildMillicastDescription(_ millicastData: [String:Any]) -> TypedSource? {
|
|
23
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
24
|
+
if let src = millicastData[SD_PROP_SRC] as? String,
|
|
25
|
+
let accountID = millicastData[SD_PROP_ACCOUNTID] as? String {
|
|
26
|
+
let subscriberToken = millicastData[SD_PROP_SUBSCRIBERTOKEN] as? String;
|
|
27
|
+
|
|
28
|
+
if millicastData[SD_PROP_CONNECTOPTIONS] == nil {
|
|
29
|
+
return MillicastSource(src: src, streamAccountId: accountID, subscriberToken: subscriberToken)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let dict = millicastData[SD_PROP_CONNECTOPTIONS] as? NSDictionary;
|
|
33
|
+
let connectOptions = buildConnectOptions(dict)
|
|
34
|
+
return MillicastSource(src: src, streamAccountId: accountID, subscriberToken: nil, connectOptions: connectOptions)
|
|
35
|
+
}
|
|
36
|
+
#endif
|
|
37
|
+
return nil
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
41
|
+
static func buildConnectOptions(_ connectOptions: NSDictionary?) -> MCClientOptions {
|
|
42
|
+
let result: MCClientOptions = .init()
|
|
43
|
+
if let bweMonitorDurationUs = connectOptions?["bweMonitorDurationUs"] as? NSNumber {
|
|
44
|
+
result.bweMonitorDurationUs = bweMonitorDurationUs
|
|
45
|
+
}
|
|
46
|
+
if let bweRateChangePercentage = connectOptions?["bweRateChangePercentage"] as? NSNumber {
|
|
47
|
+
result.bweRateChangePercentage = bweRateChangePercentage
|
|
48
|
+
}
|
|
49
|
+
if let degradationPreferences = connectOptions?["degradationPreferences"] as? String {
|
|
50
|
+
switch degradationPreferences {
|
|
51
|
+
case "balanced":
|
|
52
|
+
result.degradationPreferences = MCDegradationPreferences.balanced
|
|
53
|
+
case "disabled":
|
|
54
|
+
result.degradationPreferences = MCDegradationPreferences.disabled
|
|
55
|
+
case "maintainFrameRate":
|
|
56
|
+
result.degradationPreferences = MCDegradationPreferences.maintainFrameRate
|
|
57
|
+
case "maintainResolution":
|
|
58
|
+
result.degradationPreferences = MCDegradationPreferences.maintainResolution
|
|
59
|
+
default:
|
|
60
|
+
result.degradationPreferences = MCDegradationPreferences.default
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if let disableAudio = connectOptions?["disableAudio"] as? Bool {
|
|
64
|
+
result.disableAudio = disableAudio
|
|
65
|
+
}
|
|
66
|
+
if let dtx = connectOptions?["dtx"] as? Bool {
|
|
67
|
+
result.dtx = dtx
|
|
68
|
+
}
|
|
69
|
+
if let excludedSourceId = connectOptions?["excludedSourceIds"] as? [Any] {
|
|
70
|
+
result.excludedSourceId = excludedSourceId
|
|
71
|
+
}
|
|
72
|
+
if let forcePlayoutDelayDict = connectOptions?["forcePlayoutDelay"] as? NSDictionary,
|
|
73
|
+
let maximum = forcePlayoutDelayDict["max"] as? Int32,
|
|
74
|
+
let minimum = forcePlayoutDelayDict["min"] as? Int32 {
|
|
75
|
+
let forcePlayoutDelay: MCForcePlayoutDelay = .init(min: minimum, max: maximum)
|
|
76
|
+
result.forcePlayoutDelay = forcePlayoutDelay
|
|
77
|
+
}
|
|
78
|
+
if let forceSmooth = connectOptions?["forceSmooth"] as? Bool {
|
|
79
|
+
result.forceSmooth = forceSmooth
|
|
80
|
+
}
|
|
81
|
+
if let jitterMinimumDelayMs = connectOptions?["jitterMinimumDelayMs"] as? Int32 {
|
|
82
|
+
result.jitterMinimumDelayMs = jitterMinimumDelayMs
|
|
83
|
+
}
|
|
84
|
+
if let maximumBitrate = connectOptions?["maximumBitrate"] as? NSNumber {
|
|
85
|
+
result.maximumBitrate = maximumBitrate
|
|
86
|
+
}
|
|
87
|
+
if let multiplexedAudioTrack = connectOptions?["multiplexedAudioTracks"] as? Int32 {
|
|
88
|
+
result.multiplexedAudioTrack = multiplexedAudioTrack
|
|
89
|
+
}
|
|
90
|
+
if let pinnedSourceId = connectOptions?["pinnedSourceId"] as? String {
|
|
91
|
+
result.pinnedSourceId = pinnedSourceId
|
|
92
|
+
}
|
|
93
|
+
if let priority = connectOptions?["priority"] as? NSNumber {
|
|
94
|
+
result.priority = priority
|
|
95
|
+
}
|
|
96
|
+
if let recordStream = connectOptions?["recordStream"] as? Bool {
|
|
97
|
+
result.recordStream = recordStream
|
|
98
|
+
}
|
|
99
|
+
if let rtcEventLogOutputPath = connectOptions?["rtcEventLogOutputPath"] as? String {
|
|
100
|
+
result.rtcEventLogOutputPath = rtcEventLogOutputPath
|
|
101
|
+
}
|
|
102
|
+
if let simulcast = connectOptions?["simulcast"] as? Bool {
|
|
103
|
+
result.simulcast = simulcast
|
|
104
|
+
}
|
|
105
|
+
if let sourceId = connectOptions?["sourceId"] as? String {
|
|
106
|
+
result.sourceId = sourceId
|
|
107
|
+
}
|
|
108
|
+
if let statsDelayMs = connectOptions?["statsDelayMs"] as? Int32 {
|
|
109
|
+
result.statsDelayMs = statsDelayMs
|
|
110
|
+
}
|
|
111
|
+
if let stereo = connectOptions?["stereo"] as? Bool {
|
|
112
|
+
result.stereo = stereo
|
|
113
|
+
}
|
|
114
|
+
if let svcMode = connectOptions?["svcMode"] as? String {
|
|
115
|
+
switch svcMode {
|
|
116
|
+
case "l1t2":
|
|
117
|
+
result.svcMode = MCScalabilityMode.l1t2
|
|
118
|
+
case "l1t2h":
|
|
119
|
+
result.svcMode = MCScalabilityMode.l1t2h
|
|
120
|
+
case "l1t3":
|
|
121
|
+
result.svcMode = MCScalabilityMode.l1t3
|
|
122
|
+
case "l1t3h":
|
|
123
|
+
result.svcMode = MCScalabilityMode.l1t3h
|
|
124
|
+
case "l2t1":
|
|
125
|
+
result.svcMode = MCScalabilityMode.l2t1
|
|
126
|
+
case "l2t1Key":
|
|
127
|
+
result.svcMode = MCScalabilityMode.l2t1Key
|
|
128
|
+
case "l2t1h":
|
|
129
|
+
result.svcMode = MCScalabilityMode.l2t1h
|
|
130
|
+
case "l2t2":
|
|
131
|
+
result.svcMode = MCScalabilityMode.l2t2
|
|
132
|
+
case "l2t2Key":
|
|
133
|
+
result.svcMode = MCScalabilityMode.l2t2Key
|
|
134
|
+
case "l2t2KeyShift":
|
|
135
|
+
result.svcMode = MCScalabilityMode.l2t2KeyShift
|
|
136
|
+
case "l2t2h":
|
|
137
|
+
result.svcMode = MCScalabilityMode.l2t2h
|
|
138
|
+
case "l2t3":
|
|
139
|
+
result.svcMode = MCScalabilityMode.l2t3
|
|
140
|
+
case "l2t3h":
|
|
141
|
+
result.svcMode = MCScalabilityMode.l2t3h
|
|
142
|
+
case "l3t1":
|
|
143
|
+
result.svcMode = MCScalabilityMode.l3t1
|
|
144
|
+
case "l3t2":
|
|
145
|
+
result.svcMode = MCScalabilityMode.l3t2
|
|
146
|
+
case "l3t3":
|
|
147
|
+
result.svcMode = MCScalabilityMode.l3t3
|
|
148
|
+
case "l3t3Key":
|
|
149
|
+
result.svcMode = MCScalabilityMode.l3t3Key
|
|
150
|
+
case "none":
|
|
151
|
+
result.svcMode = MCScalabilityMode.none
|
|
152
|
+
case "s2t1":
|
|
153
|
+
result.svcMode = MCScalabilityMode.s2t1
|
|
154
|
+
case "s2t1h":
|
|
155
|
+
result.svcMode = MCScalabilityMode.s2t1h
|
|
156
|
+
case "s2t2":
|
|
157
|
+
result.svcMode = MCScalabilityMode.s2t2
|
|
158
|
+
case "s2t2h":
|
|
159
|
+
result.svcMode = MCScalabilityMode.s2t2h
|
|
160
|
+
case "s2t3":
|
|
161
|
+
result.svcMode = MCScalabilityMode.s2t3
|
|
162
|
+
case "s2t3h":
|
|
163
|
+
result.svcMode = MCScalabilityMode.s2t3h
|
|
164
|
+
case "s3t1":
|
|
165
|
+
result.svcMode = MCScalabilityMode.s3t1
|
|
166
|
+
case "s3t1h":
|
|
167
|
+
result.svcMode = MCScalabilityMode.s3t1h
|
|
168
|
+
case "s3t2":
|
|
169
|
+
result.svcMode = MCScalabilityMode.s3t2
|
|
170
|
+
case "s3t2h":
|
|
171
|
+
result.svcMode = MCScalabilityMode.s3t2h
|
|
172
|
+
case "s3t3":
|
|
173
|
+
result.svcMode = MCScalabilityMode.s3t3
|
|
174
|
+
case "s3t3h":
|
|
175
|
+
result.svcMode = MCScalabilityMode.s3t3h
|
|
176
|
+
default:
|
|
177
|
+
break
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if let upwardsLayerWaitTimeMs = connectOptions?["upwardsLayerWaitTimeMs"] as? NSNumber {
|
|
181
|
+
result.upwardsLayerWaitTimeMs = upwardsLayerWaitTimeMs
|
|
182
|
+
}
|
|
183
|
+
if let videoCodec = connectOptions?["videoCodec"] as? String {
|
|
184
|
+
result.videoCodec = videoCodec
|
|
185
|
+
}
|
|
186
|
+
return result
|
|
187
|
+
}
|
|
188
|
+
#endif
|
|
189
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// THEOplayerRCTView+Millicast.swift
|
|
2
|
+
|
|
3
|
+
import Foundation
|
|
4
|
+
import THEOplayerSDK
|
|
5
|
+
|
|
6
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
7
|
+
import THEOplayerMillicastIntegration
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
extension THEOplayerRCTView {
|
|
11
|
+
func initMillicastIntegration() {
|
|
12
|
+
guard let player = self.player else {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#if canImport(THEOplayerMillicastIntegration)
|
|
17
|
+
self.millicastIntegration = MillicastIntegrationFactory.createIntegration()
|
|
18
|
+
player.addIntegration(self.millicastIntegration!)
|
|
19
|
+
#endif
|
|
20
|
+
}
|
|
21
|
+
}
|