react-native-theoplayer 8.16.0 → 8.18.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.
Files changed (188) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +3 -1
  3. package/android/build.gradle +2 -1
  4. package/android/src/main/java/com/theoplayer/ReactTHEOplayerContext.kt +2 -1
  5. package/android/src/main/java/com/theoplayer/cmcd/CmcdTransmissionMode.kt +8 -0
  6. package/android/src/main/java/com/theoplayer/drm/ContentProtectionAdapter.kt +39 -9
  7. package/android/src/main/java/com/theoplayer/player/PlayerModule.kt +13 -0
  8. package/android/src/main/java/com/theoplayer/presentation/FullscreenLayoutObserver.kt +39 -0
  9. package/android/src/main/java/com/theoplayer/presentation/PipUtils.kt +8 -14
  10. package/android/src/main/java/com/theoplayer/presentation/PresentationManager.kt +103 -34
  11. package/android/src/main/java/com/theoplayer/source/SourceAdapter.kt +39 -10
  12. package/android/src/main/java/com/theoplayer/util/BridgeUtils.kt +2 -2
  13. package/ios/THEOplayerRCTPlayerAPI.swift +1 -0
  14. package/ios/THEOplayerRCTSourceDescriptionBuilder.swift +10 -1
  15. package/ios/ads/THEOplayerRCTAdsEventHandler.swift +2 -2
  16. package/ios/backgroundAudio/THEOplayerRCTView+BackgroundAudioConfig.swift +1 -1
  17. package/ios/pip/THEOplayerRCTPipControlsManager.swift +5 -1
  18. package/ios/pip/THEOplayerRCTView+PipConfig.swift +2 -1
  19. package/ios/theoAds/THEOplayerRCTSourceDescriptionBuilder+TheoAds.swift +3 -1
  20. package/lib/commonjs/api/barrel.js +11 -0
  21. package/lib/commonjs/api/barrel.js.map +1 -1
  22. package/lib/commonjs/api/event/TheoAdsEvent.js +15 -0
  23. package/lib/commonjs/api/event/TheoAdsEvent.js.map +1 -0
  24. package/lib/commonjs/api/event/barrel.js +11 -0
  25. package/lib/commonjs/api/event/barrel.js.map +1 -1
  26. package/lib/commonjs/api/player/PlayerEventMap.js +1 -0
  27. package/lib/commonjs/api/player/PlayerEventMap.js.map +1 -1
  28. package/lib/commonjs/api/player/THEOplayer.js.map +1 -1
  29. package/lib/commonjs/api/source/SourceDescription.js.map +1 -1
  30. package/lib/commonjs/api/source/ads/TheoAdDescription.js.map +1 -1
  31. package/lib/commonjs/api/source/barrel.js +15 -4
  32. package/lib/commonjs/api/source/barrel.js.map +1 -1
  33. package/lib/commonjs/api/source/cmcd/CmcdConfiguration.js +18 -0
  34. package/lib/commonjs/api/source/cmcd/CmcdConfiguration.js.map +1 -0
  35. package/lib/commonjs/api/source/cmcd/barrel.js +17 -0
  36. package/lib/commonjs/api/source/cmcd/barrel.js.map +1 -0
  37. package/lib/commonjs/api/theoads/TheoAdsAPI.js +6 -0
  38. package/lib/commonjs/api/theoads/TheoAdsAPI.js.map +1 -0
  39. package/lib/commonjs/api/theoads/barrel.js +50 -0
  40. package/lib/commonjs/api/theoads/barrel.js.map +1 -0
  41. package/lib/commonjs/api/theoads/interstitial/AdBreakInterstitial.js +6 -0
  42. package/lib/commonjs/api/theoads/interstitial/AdBreakInterstitial.js.map +1 -0
  43. package/lib/commonjs/api/theoads/interstitial/Interstitial.js +2 -0
  44. package/lib/commonjs/api/theoads/interstitial/Interstitial.js.map +1 -0
  45. package/lib/commonjs/api/theoads/interstitial/OverlayInterstitial.js +6 -0
  46. package/lib/commonjs/api/theoads/interstitial/OverlayInterstitial.js.map +1 -0
  47. package/lib/commonjs/internal/THEOplayerView.js +12 -2
  48. package/lib/commonjs/internal/THEOplayerView.js.map +1 -1
  49. package/lib/commonjs/internal/adapter/THEOplayerAdapter.js +5 -0
  50. package/lib/commonjs/internal/adapter/THEOplayerAdapter.js.map +1 -1
  51. package/lib/commonjs/internal/adapter/THEOplayerWebAdapter.js +37 -2
  52. package/lib/commonjs/internal/adapter/THEOplayerWebAdapter.js.map +1 -1
  53. package/lib/commonjs/internal/adapter/WebEventForwarder.js +6 -0
  54. package/lib/commonjs/internal/adapter/WebEventForwarder.js.map +1 -1
  55. package/lib/commonjs/internal/adapter/event/PlayerEvents.js +9 -1
  56. package/lib/commonjs/internal/adapter/event/PlayerEvents.js.map +1 -1
  57. package/lib/commonjs/internal/adapter/theoads/THEOAdsNativeAdapter.js +31 -0
  58. package/lib/commonjs/internal/adapter/theoads/THEOAdsNativeAdapter.js.map +1 -0
  59. package/lib/commonjs/internal/adapter/theoads/THEOAdsWebAdapter.js +22 -0
  60. package/lib/commonjs/internal/adapter/theoads/THEOAdsWebAdapter.js.map +1 -0
  61. package/lib/commonjs/internal/adapter/web/WebMediaSession.js +40 -27
  62. package/lib/commonjs/internal/adapter/web/WebMediaSession.js.map +1 -1
  63. package/lib/commonjs/internal/utils/Dimensions.js +7 -15
  64. package/lib/commonjs/internal/utils/Dimensions.js.map +1 -1
  65. package/lib/commonjs/manifest.json +1 -1
  66. package/lib/module/api/barrel.js +1 -0
  67. package/lib/module/api/barrel.js.map +1 -1
  68. package/lib/module/api/event/TheoAdsEvent.js +9 -0
  69. package/lib/module/api/event/TheoAdsEvent.js.map +1 -0
  70. package/lib/module/api/event/barrel.js +1 -0
  71. package/lib/module/api/event/barrel.js.map +1 -1
  72. package/lib/module/api/player/PlayerEventMap.js +1 -0
  73. package/lib/module/api/player/PlayerEventMap.js.map +1 -1
  74. package/lib/module/api/player/THEOplayer.js.map +1 -1
  75. package/lib/module/api/source/SourceDescription.js.map +1 -1
  76. package/lib/module/api/source/ads/TheoAdDescription.js.map +1 -1
  77. package/lib/module/api/source/barrel.js +1 -0
  78. package/lib/module/api/source/barrel.js.map +1 -1
  79. package/lib/module/api/source/cmcd/CmcdConfiguration.js +13 -0
  80. package/lib/module/api/source/cmcd/CmcdConfiguration.js.map +1 -0
  81. package/lib/module/api/source/cmcd/barrel.js +2 -0
  82. package/lib/module/api/source/cmcd/barrel.js.map +1 -0
  83. package/lib/module/api/theoads/TheoAdsAPI.js +2 -0
  84. package/lib/module/api/theoads/TheoAdsAPI.js.map +1 -0
  85. package/lib/module/api/theoads/barrel.js +5 -0
  86. package/lib/module/api/theoads/barrel.js.map +1 -0
  87. package/lib/module/api/theoads/interstitial/AdBreakInterstitial.js +2 -0
  88. package/lib/module/api/theoads/interstitial/AdBreakInterstitial.js.map +1 -0
  89. package/lib/module/api/theoads/interstitial/Interstitial.js +2 -0
  90. package/lib/module/api/theoads/interstitial/Interstitial.js.map +1 -0
  91. package/lib/module/api/theoads/interstitial/OverlayInterstitial.js +2 -0
  92. package/lib/module/api/theoads/interstitial/OverlayInterstitial.js.map +1 -0
  93. package/lib/module/internal/THEOplayerView.js +12 -2
  94. package/lib/module/internal/THEOplayerView.js.map +1 -1
  95. package/lib/module/internal/adapter/THEOplayerAdapter.js +5 -0
  96. package/lib/module/internal/adapter/THEOplayerAdapter.js.map +1 -1
  97. package/lib/module/internal/adapter/THEOplayerWebAdapter.js +38 -3
  98. package/lib/module/internal/adapter/THEOplayerWebAdapter.js.map +1 -1
  99. package/lib/module/internal/adapter/WebEventForwarder.js +8 -2
  100. package/lib/module/internal/adapter/WebEventForwarder.js.map +1 -1
  101. package/lib/module/internal/adapter/event/PlayerEvents.js +7 -0
  102. package/lib/module/internal/adapter/event/PlayerEvents.js.map +1 -1
  103. package/lib/module/internal/adapter/theoads/THEOAdsNativeAdapter.js +24 -0
  104. package/lib/module/internal/adapter/theoads/THEOAdsNativeAdapter.js.map +1 -0
  105. package/lib/module/internal/adapter/theoads/THEOAdsWebAdapter.js +15 -0
  106. package/lib/module/internal/adapter/theoads/THEOAdsWebAdapter.js.map +1 -0
  107. package/lib/module/internal/adapter/web/WebMediaSession.js +40 -27
  108. package/lib/module/internal/adapter/web/WebMediaSession.js.map +1 -1
  109. package/lib/module/internal/utils/Dimensions.js +8 -16
  110. package/lib/module/internal/utils/Dimensions.js.map +1 -1
  111. package/lib/module/manifest.json +1 -1
  112. package/lib/typescript/api/barrel.d.ts +1 -0
  113. package/lib/typescript/api/barrel.d.ts.map +1 -1
  114. package/lib/typescript/api/event/TheoAdsEvent.d.ts +35 -0
  115. package/lib/typescript/api/event/TheoAdsEvent.d.ts.map +1 -0
  116. package/lib/typescript/api/event/barrel.d.ts +1 -0
  117. package/lib/typescript/api/event/barrel.d.ts.map +1 -1
  118. package/lib/typescript/api/pip/PiPConfiguration.d.ts +8 -1
  119. package/lib/typescript/api/pip/PiPConfiguration.d.ts.map +1 -1
  120. package/lib/typescript/api/player/PlayerEventMap.d.ts +6 -0
  121. package/lib/typescript/api/player/PlayerEventMap.d.ts.map +1 -1
  122. package/lib/typescript/api/player/THEOplayer.d.ts +6 -1
  123. package/lib/typescript/api/player/THEOplayer.d.ts.map +1 -1
  124. package/lib/typescript/api/source/SourceDescription.d.ts +6 -0
  125. package/lib/typescript/api/source/SourceDescription.d.ts.map +1 -1
  126. package/lib/typescript/api/source/ads/TheoAdDescription.d.ts +16 -0
  127. package/lib/typescript/api/source/ads/TheoAdDescription.d.ts.map +1 -1
  128. package/lib/typescript/api/source/barrel.d.ts +1 -0
  129. package/lib/typescript/api/source/barrel.d.ts.map +1 -1
  130. package/lib/typescript/api/source/cmcd/CmcdConfiguration.d.ts +79 -0
  131. package/lib/typescript/api/source/cmcd/CmcdConfiguration.d.ts.map +1 -0
  132. package/lib/typescript/api/source/cmcd/barrel.d.ts +2 -0
  133. package/lib/typescript/api/source/cmcd/barrel.d.ts.map +1 -0
  134. package/lib/typescript/api/theoads/TheoAdsAPI.d.ts +30 -0
  135. package/lib/typescript/api/theoads/TheoAdsAPI.d.ts.map +1 -0
  136. package/lib/typescript/api/theoads/barrel.d.ts +5 -0
  137. package/lib/typescript/api/theoads/barrel.d.ts.map +1 -0
  138. package/lib/typescript/api/theoads/interstitial/AdBreakInterstitial.d.ts +34 -0
  139. package/lib/typescript/api/theoads/interstitial/AdBreakInterstitial.d.ts.map +1 -0
  140. package/lib/typescript/api/theoads/interstitial/Interstitial.d.ts +33 -0
  141. package/lib/typescript/api/theoads/interstitial/Interstitial.d.ts.map +1 -0
  142. package/lib/typescript/api/theoads/interstitial/OverlayInterstitial.d.ts +49 -0
  143. package/lib/typescript/api/theoads/interstitial/OverlayInterstitial.d.ts.map +1 -0
  144. package/lib/typescript/internal/THEOplayerView.d.ts.map +1 -1
  145. package/lib/typescript/internal/adapter/THEOplayerAdapter.d.ts +3 -1
  146. package/lib/typescript/internal/adapter/THEOplayerAdapter.d.ts.map +1 -1
  147. package/lib/typescript/internal/adapter/THEOplayerWebAdapter.d.ts +5 -2
  148. package/lib/typescript/internal/adapter/THEOplayerWebAdapter.d.ts.map +1 -1
  149. package/lib/typescript/internal/adapter/WebEventForwarder.d.ts +1 -0
  150. package/lib/typescript/internal/adapter/WebEventForwarder.d.ts.map +1 -1
  151. package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts +7 -0
  152. package/lib/typescript/internal/adapter/event/PlayerEvents.d.ts.map +1 -1
  153. package/lib/typescript/internal/adapter/theoads/THEOAdsNativeAdapter.d.ts +9 -0
  154. package/lib/typescript/internal/adapter/theoads/THEOAdsNativeAdapter.d.ts.map +1 -0
  155. package/lib/typescript/internal/adapter/theoads/THEOAdsWebAdapter.d.ts +10 -0
  156. package/lib/typescript/internal/adapter/theoads/THEOAdsWebAdapter.d.ts.map +1 -0
  157. package/lib/typescript/internal/adapter/web/WebMediaSession.d.ts +4 -6
  158. package/lib/typescript/internal/adapter/web/WebMediaSession.d.ts.map +1 -1
  159. package/lib/typescript/internal/utils/Dimensions.d.ts +1 -1
  160. package/lib/typescript/internal/utils/Dimensions.d.ts.map +1 -1
  161. package/package.json +3 -2
  162. package/react-native-theoplayer.podspec +7 -7
  163. package/src/api/barrel.ts +1 -0
  164. package/src/api/event/TheoAdsEvent.ts +41 -0
  165. package/src/api/event/barrel.ts +1 -0
  166. package/src/api/pip/PiPConfiguration.ts +9 -1
  167. package/src/api/player/PlayerEventMap.ts +7 -0
  168. package/src/api/player/THEOplayer.ts +7 -1
  169. package/src/api/source/SourceDescription.ts +7 -0
  170. package/src/api/source/ads/TheoAdDescription.ts +19 -0
  171. package/src/api/source/barrel.ts +1 -0
  172. package/src/api/source/cmcd/CmcdConfiguration.ts +84 -0
  173. package/src/api/source/cmcd/barrel.ts +1 -0
  174. package/src/api/theoads/TheoAdsAPI.ts +33 -0
  175. package/src/api/theoads/barrel.ts +4 -0
  176. package/src/api/theoads/interstitial/AdBreakInterstitial.ts +39 -0
  177. package/src/api/theoads/interstitial/Interstitial.ts +36 -0
  178. package/src/api/theoads/interstitial/OverlayInterstitial.ts +55 -0
  179. package/src/internal/THEOplayerView.tsx +10 -2
  180. package/src/internal/adapter/THEOplayerAdapter.ts +8 -0
  181. package/src/internal/adapter/THEOplayerWebAdapter.ts +50 -5
  182. package/src/internal/adapter/WebEventForwarder.ts +18 -0
  183. package/src/internal/adapter/event/PlayerEvents.ts +11 -0
  184. package/src/internal/adapter/theoads/THEOAdsNativeAdapter.ts +26 -0
  185. package/src/internal/adapter/theoads/THEOAdsWebAdapter.ts +22 -0
  186. package/src/internal/adapter/web/WebMediaSession.ts +39 -30
  187. package/src/internal/utils/Dimensions.ts +8 -16
  188. package/src/manifest.json +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,38 @@ 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
+ ## [8.18.0] - 25-04-02
9
+
10
+ ### Changed
11
+
12
+ - No longer showing poster after setting the source when the player has been configured to use autoplay. With autoplay enabled, displaying the poster will only result in a brief flash of the image.
13
+ - Changed the fullscreen screen dimension calculation on Android, taking into account edgeToEdge layouts.
14
+
15
+ ### Added
16
+
17
+ - Exposed THEOads API through the Player API.
18
+ - Added support for Common Media Client Data (CMCD) on all platforms. More info on the [documentation](./doc/cmcd.md) page.
19
+
20
+ ### Fixed
21
+
22
+ - Fixed an issue on Android where the transition to a PiP window would not focus on the player window.
23
+
24
+ ## [8.17.0] - 25-03-20
25
+
26
+ ### Fixed
27
+
28
+ - Fixed an issue on Web where the metadata on the lockscreen was not showing correctly or missing occasionally.
29
+ - Fixed a crash on Android when setting a source with THEOads without the Media3 extension being enabled.
30
+ - Fixed an issue on iOS where the AdTapped and AdClicked events were not correctly cleaned up.
31
+ - Fixed an issue on Android where the player would sometimes crash when passing a DRM protected source without an `integration` property.
32
+
33
+ ### Added
34
+
35
+ - Added SeekTo functionality to the web lockscreen controls, allowing to drag the lockscreen slider and seek to a specific time value.
36
+ - Added the IMA DAI `streamActivityMonitorId` property as a configuration on the SGAI `TheoAdDescription` for web.
37
+ - Added the THEOads API for Web.
38
+ - Added `retrievePodIdURI` property to `TheoAdsDescription` for Android and iOS.
39
+
8
40
  ## [8.16.0] - 25-02-28
9
41
 
10
42
  ### Changed
@@ -18,6 +50,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
18
50
  - Fixed an issue on Android, where the player would sometimes not initialise correctly in case New Architecture was not being used, resulting in a black screen.
19
51
  - Fixed an issue on iOS Safari browsers, where the `presentationmodechange` event would not be dispatched when entering or exiting fullscreen.
20
52
 
53
+
21
54
  ## [8.15.0] - 25-02-12
22
55
 
23
56
  ### Changed
package/README.md CHANGED
@@ -105,7 +105,7 @@ please reach out to us for support.
105
105
  </tr>
106
106
  <tr>
107
107
  <td><strong>Advertising Integration</strong></td>
108
- <td colspan="3">Google IMA, Google DAI</td>
108
+ <td colspan="3">Google IMA, Google DAI, THEOads</td>
109
109
  </tr>
110
110
  <tr>
111
111
  <td><strong>Cast Integration</strong></td>
@@ -174,9 +174,11 @@ This section gives an overview of features, limitations and known issues:
174
174
 
175
175
  - [Adaptive Bitrate (ABR)](./doc/abr.md)
176
176
  - [Advertisements](./doc/ads.md)
177
+ - [Android Media3 Pipeline🔥](./doc/media3.md)
177
178
  - [Audio Control Management](./doc/audio-control.md)
178
179
  - [Background playback and notifications](./doc/background.md)
179
180
  - [Casting with Chromecast and Airplay](./doc/cast.md)
181
+ - [Common Media Client Data (CMCD)](./doc/cmcd.md)
180
182
  - [Digital Rights Management (DRM)](./doc/drm.md)
181
183
  - [Fullscreen presentation](./doc/fullscreen.md)
182
184
  - [Media Caching](./doc/media-caching.md)
@@ -59,8 +59,9 @@ android {
59
59
  def TimeUpdateRate = "com.theoplayer.TimeUpdateRate"
60
60
  buildConfigField TimeUpdateRate, "TIMEUPDATE_RATE", safeExtGet('THEOplayer_timeUpdateRate', "${TimeUpdateRate}.UNLIMITED")
61
61
 
62
- // Optionally re-parent player view on fullscreen event
62
+ // Optionally re-parent player view on fullscreen or PiP event
63
63
  buildConfigField "boolean", "REPARENT_ON_FULLSCREEN", "${safeExtGet('THEOplayer_reparent_on_fullscreen', 'true')}"
64
+ buildConfigField "boolean", "REPARENT_ON_PIP", "${safeExtGet('THEOplayer_reparent_on_PiP', 'false')}"
64
65
 
65
66
  // Optionally log events to logcat
66
67
  buildConfigField "boolean", "LOG_PLAYER_EVENTS", "${safeExtGet('THEOplayer_logPlayerEvents', 'false')}"
@@ -346,7 +346,8 @@ class ReactTHEOplayerContext private constructor(
346
346
  // return false -> the default pipeline will be used to play the selected source.
347
347
  //
348
348
  // @remark If the source contains THEOads, media3 is always enabled.
349
- configAdapter.useMedia3 || source.ads.any { it is TheoAdDescription }
349
+ configAdapter.useMedia3 ||
350
+ (BuildConfig.EXTENSION_THEOADS && source.ads.any { it is TheoAdDescription })
350
351
  }
351
352
  playerView.player.addIntegration(media3Integration)
352
353
  }
@@ -0,0 +1,8 @@
1
+ package com.theoplayer.cmcd
2
+
3
+ enum class CmcdTransmissionMode {
4
+ HTTP_HEADER,
5
+ QUERY_ARGUMENT,
6
+ JSON_OBJECT,
7
+ SDK_DEFAULT
8
+ }
@@ -12,7 +12,9 @@ import com.theoplayer.android.api.contentprotection.Response
12
12
  import com.theoplayer.android.api.source.drm.DRMConfiguration
13
13
  import com.theoplayer.android.api.source.drm.DRMIntegrationId
14
14
  import com.theoplayer.android.api.source.drm.KeySystemConfiguration
15
+ import com.theoplayer.android.api.source.drm.LicenseType
15
16
  import com.theoplayer.android.api.source.drm.preintegration.*
17
+ import com.theoplayer.util.BridgeUtils.fromJSONObjectToMap
16
18
  import org.json.JSONObject
17
19
 
18
20
  private const val TAG = "ContentProtection"
@@ -23,6 +25,7 @@ const val PROP_INTEGRATION_PARAMETERS: String = "integrationParameters"
23
25
  const val PROP_REQUEST: String = "request"
24
26
  const val PROP_KEYSYSTEM_ID: String = "keySystemId"
25
27
  const val PROP_DRM_CONFIG: String = "drmConfig"
28
+ const val PROP_PLAYREADY: String = "playready"
26
29
  const val PROP_WIDEVINE: String = "widevine"
27
30
  const val PROP_REQUEST_ID: String = "requestId"
28
31
  const val PROP_URL: String = "url"
@@ -33,6 +36,12 @@ const val PROP_HEADERS: String = "headers"
33
36
  const val PROP_BASE64_BODY: String = "base64body"
34
37
  const val PROP_LA_URL: String = "licenseAcquisitionURL"
35
38
  const val PROP_USE_CREDENTIALS: String = "useCredentials"
39
+ const val PROP_LICENSE_ACQUISITION_URL: String = "licenseAcquisitionURL"
40
+ const val PROP_LICENSE_TYPE: String = "licenseType"
41
+ const val PROP_LICENSE_TYPE_TEMPORARY: String = "temporary"
42
+ const val PROP_LICENSE_TYPE_PERSISTENT: String = "persistent"
43
+ const val PROP_QUERY_PARAMETERS: String = "queryParameters"
44
+ const val PROP_CERTIFICATE: String = "certificate"
36
45
 
37
46
  object ContentProtectionAdapter {
38
47
 
@@ -76,21 +85,42 @@ object ContentProtectionAdapter {
76
85
  // Custom integration through connector
77
86
  return DRMConfiguration.Builder().apply {
78
87
  if (!TextUtils.isEmpty(integration)) {
79
- this.customIntegrationId(integration)
88
+ customIntegrationId(integration)
89
+ }
90
+ if (jsonConfig.has(PROP_WIDEVINE)) {
91
+ widevine(keySystemConfigurationFromJson(jsonConfig.getJSONObject(PROP_WIDEVINE)))
92
+ }
93
+ if (jsonConfig.has(PROP_PLAYREADY)) {
94
+ playready(keySystemConfigurationFromJson(jsonConfig.getJSONObject(PROP_PLAYREADY)))
80
95
  }
81
- this.widevine(gson.fromJson(jsonConfig.optString("widevine"), KeySystemConfiguration::class.java))
82
- this.playready(gson.fromJson(jsonConfig.optString("playready"), KeySystemConfiguration::class.java))
83
96
  if (jsonConfig.has(PROP_INTEGRATION_PARAMETERS)) {
84
- this.integrationParameters(
85
- gson.fromJson<Map<String, Any>>(
86
- jsonConfig.getJSONObject(PROP_INTEGRATION_PARAMETERS).toString(),
87
- MutableMap::class.java
88
- )
89
- )
97
+ integrationParameters(fromJSONObjectToMap(jsonConfig.getJSONObject(PROP_INTEGRATION_PARAMETERS)))
98
+ }
99
+ }.build()
100
+ }
101
+
102
+ private fun keySystemConfigurationFromJson(config: JSONObject): KeySystemConfiguration {
103
+ return KeySystemConfiguration.Builder(config.optString(PROP_LICENSE_ACQUISITION_URL)).apply {
104
+ useCredentials(config.optBoolean(PROP_USE_CREDENTIALS))
105
+ licenseTypeFromString(config.optString(PROP_LICENSE_TYPE))?.let {
106
+ licenseType(it)
107
+ }
108
+ headers(fromJSONObjectToMap(config.optJSONObject(PROP_HEADERS)))
109
+ queryParameters(fromJSONObjectToMap(config.optJSONObject(PROP_QUERY_PARAMETERS)))
110
+ if (config.has(PROP_CERTIFICATE)) {
111
+ certificate(config.getString(PROP_CERTIFICATE).toByteArray())
90
112
  }
91
113
  }.build()
92
114
  }
93
115
 
116
+ private fun licenseTypeFromString(str: String?): LicenseType? {
117
+ return when (str) {
118
+ PROP_LICENSE_TYPE_PERSISTENT -> LicenseType.PERSISTENT
119
+ PROP_LICENSE_TYPE_TEMPORARY -> LicenseType.TEMPORARY
120
+ else -> null
121
+ }
122
+ }
123
+
94
124
  fun fromDRMConfiguration(config: DRMConfiguration): WritableMap {
95
125
  return Arguments.createMap().apply {
96
126
  putString(PROP_INTEGRATION, config.customIntegrationId ?: config.integration.integrationId)
@@ -263,4 +263,17 @@ class PlayerModule(context: ReactApplicationContext) : ReactContextBaseJavaModul
263
263
  )
264
264
  }
265
265
  }
266
+
267
+ @ReactMethod(isBlockingSynchronousMethod = true)
268
+ fun getUsableScreenDimensions(): WritableMap {
269
+ // Pass the dimensions of the top-most View in the view hierarchy.
270
+ val topView = reactApplicationContext.currentActivity?.window?.decorView?.rootView
271
+ reactApplicationContext.resources.displayMetrics.also {
272
+ val density = it.density
273
+ return Arguments.createMap().apply {
274
+ putDouble("width", (topView?.width ?: 0) / density.toDouble())
275
+ putDouble("height", (topView?.height ?: 0) / density.toDouble())
276
+ }
277
+ }
278
+ }
266
279
  }
@@ -0,0 +1,39 @@
1
+ package com.theoplayer.presentation
2
+
3
+ import android.util.Log
4
+ import android.view.ViewTreeObserver
5
+ import com.facebook.react.views.view.ReactViewGroup
6
+
7
+ private val TAG = "FSLayoutObserver"
8
+
9
+ /**
10
+ * FullScreenLayoutObserver makes sure that the React Native view does not get the layout
11
+ * defined in React-Native during fullscreen presentation mode. We want to enforce fullscreen
12
+ * position & size.
13
+ */
14
+ class FullScreenLayoutObserver {
15
+ private var globalLayoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null
16
+ private var attached: ReactViewGroup? = null
17
+
18
+ fun attach(viewGroup: ReactViewGroup?) {
19
+ if (attached != null) {
20
+ Log.w(TAG, "A previously attached ViewGroup was not properly detached.")
21
+ }
22
+
23
+ viewGroup?.let {
24
+ globalLayoutListener = ViewTreeObserver.OnGlobalLayoutListener {
25
+ it.post {
26
+ it.layout(0, 0, viewGroup.width, viewGroup.height)
27
+ }
28
+ }
29
+ it.viewTreeObserver.addOnGlobalLayoutListener(globalLayoutListener)
30
+ attached = viewGroup
31
+ }
32
+ }
33
+
34
+ fun remove() {
35
+ attached?.viewTreeObserver?.removeOnGlobalLayoutListener(globalLayoutListener)
36
+ attached = null
37
+ globalLayoutListener = null
38
+ }
39
+ }
@@ -12,9 +12,6 @@ import android.graphics.Rect
12
12
  import android.graphics.drawable.Icon
13
13
  import android.os.Build
14
14
  import android.util.Rational
15
- import android.view.SurfaceView
16
- import android.view.TextureView
17
- import android.view.View
18
15
  import android.view.ViewGroup
19
16
  import androidx.annotation.RequiresApi
20
17
  import com.facebook.react.uimanager.ThemedReactContext
@@ -108,7 +105,8 @@ class PipUtils(
108
105
  }
109
106
  try {
110
107
  reactContext.currentActivity?.unregisterReceiver(broadcastReceiver)
111
- } catch (ignore: IllegalArgumentException) { /*ignore*/}
108
+ } catch (ignore: IllegalArgumentException) { /*ignore*/
109
+ }
112
110
  enabled = false
113
111
  }
114
112
 
@@ -189,17 +187,13 @@ class PipUtils(
189
187
  }
190
188
 
191
189
  private fun getContentViewRect(view: ViewGroup): Rect? {
192
- for (i in 0 until view.childCount) {
193
- val child: View = view.getChildAt(i)
194
- if (child is ViewGroup) {
195
- return getContentViewRect(child)
196
- } else if (child as? SurfaceView != null || child as? TextureView != null) {
197
- val visibleRect = Rect()
198
- child.getGlobalVisibleRect(visibleRect)
199
- return visibleRect
190
+ return view.findViewById<ViewGroup>(com.theoplayer.android.R.id.theo_content_player_container)
191
+ ?.getChildAt(0) // AspectRatioView
192
+ ?.run {
193
+ Rect().apply {
194
+ getGlobalVisibleRect(this)
195
+ }
200
196
  }
201
- }
202
- return null
203
197
  }
204
198
 
205
199
  @RequiresApi(Build.VERSION_CODES.O)
@@ -14,9 +14,9 @@ import android.view.ViewParent
14
14
  import androidx.activity.ComponentActivity
15
15
  import androidx.core.view.WindowInsetsCompat
16
16
  import androidx.core.view.WindowInsetsControllerCompat
17
+ import androidx.core.view.children
17
18
  import androidx.lifecycle.Lifecycle
18
19
  import com.facebook.react.ReactRootView
19
- import com.facebook.react.runtime.ReactSurfaceView
20
20
  import com.facebook.react.uimanager.ThemedReactContext
21
21
  import com.facebook.react.views.view.ReactViewGroup
22
22
  import com.theoplayer.BuildConfig
@@ -35,9 +35,11 @@ class PresentationManager(
35
35
  private var supportsPip = false
36
36
  private var onUserLeaveHintReceiver: BroadcastReceiver? = null
37
37
  private var onPictureInPictureModeChanged: BroadcastReceiver? = null
38
- private var playerGroupParentNode: ViewGroup? = null
39
- private var playerGroupChildIndex: Int? = null
40
38
  private val pipUtils: PipUtils = PipUtils(viewCtx, reactContext)
39
+ private val fullScreenLayoutObserver = FullScreenLayoutObserver()
40
+ private val playerGroupRestoreOptions by lazy {
41
+ PlayerGroupRestoreOptions()
42
+ }
41
43
 
42
44
  var currentPresentationMode: PresentationMode = PresentationMode.INLINE
43
45
  private set
@@ -99,6 +101,7 @@ class PresentationManager(
99
101
  try {
100
102
  reactContext.currentActivity?.unregisterReceiver(onUserLeaveHintReceiver)
101
103
  reactContext.currentActivity?.unregisterReceiver(onPictureInPictureModeChanged)
104
+ fullScreenLayoutObserver.remove()
102
105
  pipUtils.destroy()
103
106
  } catch (ignore: Exception) {
104
107
  }
@@ -146,6 +149,9 @@ class PresentationManager(
146
149
  try {
147
150
  pipUtils.enable()
148
151
  reactContext.currentActivity?.enterPictureInPictureMode(pipUtils.getPipParams())
152
+ if (BuildConfig.REPARENT_ON_PIP) {
153
+ reparentPlayerToRoot()
154
+ }
149
155
  } catch (_: Exception) {
150
156
  onPipError()
151
157
  }
@@ -169,6 +175,9 @@ class PresentationManager(
169
175
  } else {
170
176
  PresentationModeChangePipContext.RESTORED
171
177
  }
178
+ if (BuildConfig.REPARENT_ON_PIP) {
179
+ reparentPlayerToOriginal()
180
+ }
172
181
  updatePresentationMode(PresentationMode.INLINE, PresentationModeChangeContext(pipCtx))
173
182
  pipUtils.disable()
174
183
  }
@@ -209,30 +218,23 @@ class PresentationManager(
209
218
  val activity = reactContext.currentActivity ?: return
210
219
  val window = activity.window
211
220
 
212
- // Get the player's ReactViewGroup parent, which contains THEOplayerView and its children (typically the UI).
213
- val reactPlayerGroup: ReactViewGroup? = getClosestParentOfType(this.viewCtx.playerView)
214
-
215
- // Get ReactNative's root node or the render hierarchy
216
- val root: ReactRootView? = getClosestParentOfType(reactPlayerGroup)
217
-
218
221
  if (fullscreen) {
222
+ // Hide system bars for immersive mode.
223
+ // {@link https://developer.android.com/develop/ui/views/layout/immersive}
219
224
  WindowInsetsControllerCompat(window, window.decorView).apply {
225
+ // Reveal hidden system bars on any system gestures.
220
226
  systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
221
- }.hide(WindowInsetsCompat.Type.systemBars())
222
- updatePresentationMode(PresentationMode.FULLSCREEN)
227
+ // Hide all system bars.
228
+ hide(WindowInsetsCompat.Type.systemBars())
229
+ }
223
230
 
224
- if (!BuildConfig.REPARENT_ON_FULLSCREEN) {
225
- return
231
+ // Delay the event making sure it does not arrive before animations ended.
232
+ viewCtx.playerView.postOnAnimation {
233
+ updatePresentationMode(PresentationMode.FULLSCREEN)
226
234
  }
227
- playerGroupParentNode = if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
228
- reactPlayerGroup?.parent as? ReactSurfaceView?
229
- } else {
230
- reactPlayerGroup?.parent as? ReactViewGroup?
231
- }?.also { parent ->
232
- playerGroupChildIndex = parent.indexOfChild(reactPlayerGroup)
233
- // Re-parent the playerViewGroup to the root node
234
- parent.removeView(reactPlayerGroup)
235
- root?.addView(reactPlayerGroup)
235
+
236
+ if (BuildConfig.REPARENT_ON_FULLSCREEN) {
237
+ reparentPlayerToRoot()
236
238
  }
237
239
  } else {
238
240
  WindowInsetsControllerCompat(window, window.decorView).show(
@@ -240,18 +242,54 @@ class PresentationManager(
240
242
  )
241
243
  updatePresentationMode(PresentationMode.INLINE)
242
244
 
243
- if (!BuildConfig.REPARENT_ON_FULLSCREEN) {
244
- return
245
+ if (BuildConfig.REPARENT_ON_FULLSCREEN) {
246
+ reparentPlayerToOriginal()
245
247
  }
246
- root?.run {
248
+ }
249
+ }
250
+
251
+ // region Re-parent playerViewGroup logic
252
+ private val reactPlayerGroup: ReactViewGroup?
253
+ get() = viewCtx.playerView.getClosestParentOfType()
254
+
255
+ private val rootView: ReactRootView?
256
+ get() {
257
+ val activity = reactContext.currentActivity ?: return null
258
+ // Try to search in parents and as a fallback option from root to bottom using depth-first order
259
+ return reactPlayerGroup?.getClosestParentOfType()
260
+ ?: (activity.window.decorView.rootView as? ViewGroup)
261
+ ?.getClosestParentOfType(false)
262
+ }
263
+
264
+ private fun reparentPlayerToRoot() {
265
+ reactPlayerGroup?.let { playerGroup ->
266
+ playerGroupRestoreOptions.parentNode = (playerGroup.parent as? ViewGroup)?.also { parent ->
267
+ playerGroupRestoreOptions.childIndex = parent.indexOfChild(playerGroup)
268
+
269
+ // Re-parent the playerViewGroup to the root node
270
+ parent.removeView(playerGroup)
271
+ rootView?.addView(playerGroup)
272
+
273
+ // Attach an observer that overrides the react-native lay-out and forces fullscreen.
274
+ fullScreenLayoutObserver.attach(playerGroup)
275
+ }
276
+ }
277
+ }
278
+
279
+ private fun reparentPlayerToOriginal() {
280
+ rootView?.run {
281
+ reactPlayerGroup?.let { playerGroup ->
282
+ // Remove forced layout observer
283
+ fullScreenLayoutObserver.remove()
284
+
247
285
  // Re-parent the playerViewGroup from the root node to its original parent
248
- removeView(reactPlayerGroup)
249
- playerGroupParentNode?.addView(reactPlayerGroup, playerGroupChildIndex ?: 0)
250
- playerGroupParentNode = null
251
- playerGroupChildIndex = null
286
+ removeView(playerGroup)
287
+ playerGroupRestoreOptions.parentNode?.addView(playerGroup, playerGroupRestoreOptions.childIndex ?: 0)
288
+ playerGroupRestoreOptions.reset()
252
289
  }
253
290
  }
254
291
  }
292
+ // endregion
255
293
 
256
294
  private fun updatePresentationMode(
257
295
  presentationMode: PresentationMode,
@@ -279,10 +317,41 @@ class PresentationManager(
279
317
  }
280
318
  }
281
319
 
282
- inline fun <reified T : View> getClosestParentOfType(view: View?): T? {
283
- var parent: ViewParent? = view?.parent
284
- while (parent != null && parent !is T) {
285
- parent = parent.parent
320
+ inline fun <reified T : View> ViewGroup.getClosestParentOfType(upward: Boolean = true): T? {
321
+ if (upward) {
322
+ // Search in the parent views of `this` view up to the root
323
+ var parent: ViewParent? = parent
324
+ while (parent != null && parent !is T) {
325
+ parent = parent.parent
326
+ }
327
+ return parent as? T
328
+ } else {
329
+ // Search in the children collection.
330
+ val viewStack = ArrayDeque(children.toList())
331
+ // Use Stack/LIFO instead of recursion
332
+ while (viewStack.isNotEmpty()) {
333
+ when (val view = viewStack.removeAt(0)) {
334
+ is T -> {
335
+ return view
336
+ }
337
+
338
+ is ViewGroup -> {
339
+ // Filling LIFO with all children of the ViewGroup: depth-first order
340
+ viewStack.addAll(0, view.children.toList())
341
+ }
342
+ }
343
+ }
344
+ // Found nothing
345
+ return null
346
+ }
347
+ }
348
+
349
+ private class PlayerGroupRestoreOptions {
350
+ var childIndex: Int? = null
351
+ var parentNode: ViewGroup? = null
352
+
353
+ fun reset() {
354
+ parentNode = null
355
+ childIndex = null
286
356
  }
287
- return parent as? T
288
357
  }
@@ -19,10 +19,12 @@ import com.theoplayer.android.api.player.track.texttrack.TextTrackKind
19
19
  import com.theoplayer.android.api.source.metadata.ChromecastMetadataImage
20
20
  import com.theoplayer.BuildConfig
21
21
  import com.theoplayer.android.api.ads.theoads.TheoAdsLayoutOverride
22
+ import com.theoplayer.android.api.cmcd.CMCDTransmissionMode
22
23
  import com.theoplayer.android.api.error.ErrorCode
23
24
  import com.theoplayer.android.api.source.AdIntegration
24
25
  import com.theoplayer.android.api.source.dash.DashPlaybackConfiguration
25
26
  import com.theoplayer.android.api.theolive.TheoLiveSource
27
+ import com.theoplayer.cmcd.CmcdTransmissionMode
26
28
  import com.theoplayer.drm.ContentProtectionAdapter
27
29
  import com.theoplayer.latency.parseLatencyConfiguration
28
30
  import com.theoplayer.util.BridgeUtils
@@ -63,6 +65,7 @@ private const val PROP_CUSTOM_ASSET_KEY = "customAssetKey"
63
65
  private const val PROP_OVERRIDE_LAYOUT = "overrideLayout"
64
66
  private const val PROP_NETWORK_CODE = "networkCode"
65
67
  private const val PROP_USE_ID3 = "useId3"
68
+ private const val PROP_RETRIEVE_POD_ID_URI = "retrievePodIdURI"
66
69
  private const val PROP_LATENCY_CONFIGURATION = "latencyConfiguration"
67
70
 
68
71
  private const val ERROR_IMA_NOT_ENABLED = "Google IMA support not enabled."
@@ -74,6 +77,9 @@ private const val PROP_SSAI_INTEGRATION_GOOGLE_DAI = "google-dai"
74
77
 
75
78
  private const val INTEGRATION_THEOLIVE = "theolive"
76
79
 
80
+ private const val PROP_CMCD = "cmcd"
81
+ private const val CMCD_TRANSMISSION_MODE = "transmissionMode"
82
+
77
83
  class SourceAdapter {
78
84
  private val gson = Gson()
79
85
 
@@ -94,6 +100,12 @@ class SourceAdapter {
94
100
  try {
95
101
  val jsonSourceObject = JSONObject(gson.toJson(source.toHashMap()))
96
102
 
103
+ // CMCD
104
+ var cmcdTransmissionMode: CMCDTransmissionMode? = null
105
+ if (jsonSourceObject.has(PROP_CMCD)) {
106
+ cmcdTransmissionMode = parseCmcdTransmissionMode(jsonSourceObject.getJSONObject(PROP_CMCD));
107
+ }
108
+
97
109
  // typed sources
98
110
  val typedSources = ArrayList<TypedSource>()
99
111
 
@@ -101,11 +113,11 @@ class SourceAdapter {
101
113
  val jsonSources = jsonSourceObject.optJSONArray(PROP_SOURCES)
102
114
  if (jsonSources != null) {
103
115
  for (i in 0 until jsonSources.length()) {
104
- typedSources.add(parseTypedSource(jsonSources[i] as JSONObject))
116
+ typedSources.add(parseTypedSource(jsonSources[i] as JSONObject, cmcdTransmissionMode))
105
117
  }
106
118
  } else {
107
119
  val jsonSource = jsonSourceObject.optJSONObject(PROP_SOURCES) ?: return null
108
- typedSources.add(parseTypedSource(jsonSource))
120
+ typedSources.add(parseTypedSource(jsonSource, cmcdTransmissionMode))
109
121
  }
110
122
 
111
123
  // poster
@@ -166,16 +178,16 @@ class SourceAdapter {
166
178
  }
167
179
 
168
180
  @Throws(THEOplayerException::class)
169
- private fun parseTypedSource(jsonTypedSource: JSONObject): TypedSource {
181
+ private fun parseTypedSource(jsonTypedSource: JSONObject, cmcdTransmissionMode: CMCDTransmissionMode? = null): TypedSource {
170
182
  // Some integrations do not support the Builder pattern
171
183
  return when (jsonTypedSource.optString(PROP_INTEGRATION)) {
172
184
  INTEGRATION_THEOLIVE -> parseTheoLiveSource(jsonTypedSource)
173
- else -> parseTypedSourceFromBuilder(jsonTypedSource)
185
+ else -> parseTypedSourceFromBuilder(jsonTypedSource, cmcdTransmissionMode)
174
186
  }
175
187
  }
176
188
 
177
189
  @Throws(THEOplayerException::class)
178
- private fun parseTypedSourceFromBuilder(jsonTypedSource: JSONObject): TypedSource {
190
+ private fun parseTypedSourceFromBuilder(jsonTypedSource: JSONObject, cmcdTransmissionMode: CMCDTransmissionMode? = null): TypedSource {
179
191
  try {
180
192
  var tsBuilder = TypedSource.Builder(jsonTypedSource.optString(PROP_SRC))
181
193
  val sourceType = parseSourceType(jsonTypedSource)
@@ -215,6 +227,9 @@ class SourceAdapter {
215
227
  tsBuilder.drm(drmConfig)
216
228
  }
217
229
  }
230
+ if (cmcdTransmissionMode != null) {
231
+ tsBuilder.cmcdTransmissionMode(cmcdTransmissionMode)
232
+ }
218
233
  return tsBuilder.build()
219
234
  } catch (e: THEOplayerException) {
220
235
  // Rethrow THEOplayerException
@@ -327,12 +342,13 @@ class SourceAdapter {
327
342
  }
328
343
  return TheoAdDescription(
329
344
  adTagParameters = parseAdTagParameters(jsonAdDescription.optJSONObject(PROP_AD_TAG_PARAMETERS)),
330
- backdropDoubleBox = jsonAdDescription.optString(PROP_BACKDROP_DOUBLE_BOX),
331
- backdropLShape = jsonAdDescription.optString(PROP_BACKDROP_LSHAPE),
332
- customAssetKey = jsonAdDescription.optString(PROP_CUSTOM_ASSET_KEY),
333
- networkCode = jsonAdDescription.optString(PROP_NETWORK_CODE),
345
+ backdropDoubleBox = jsonAdDescription.optString(PROP_BACKDROP_DOUBLE_BOX).takeIf { it.isNotEmpty() },
346
+ backdropLShape = jsonAdDescription.optString(PROP_BACKDROP_LSHAPE).takeIf { it.isNotEmpty() },
347
+ customAssetKey = jsonAdDescription.optString(PROP_CUSTOM_ASSET_KEY).takeIf { it.isNotEmpty() },
348
+ networkCode = jsonAdDescription.optString(PROP_NETWORK_CODE).takeIf { it.isNotEmpty() },
334
349
  overrideLayout = parseOverrideLayout(jsonAdDescription.optString(PROP_OVERRIDE_LAYOUT)),
335
- useId3 = jsonAdDescription.optBoolean(PROP_USE_ID3),
350
+ useId3 = jsonAdDescription.optBoolean(PROP_USE_ID3, false),
351
+ retrievePodIdURI = jsonAdDescription.optString(PROP_RETRIEVE_POD_ID_URI).takeIf { it.isNotEmpty() },
336
352
  )
337
353
  }
338
354
 
@@ -429,4 +445,17 @@ class SourceAdapter {
429
445
 
430
446
  return BridgeUtils.fromJSONObjectToBridge(json)
431
447
  }
448
+
449
+ private fun parseCmcdTransmissionMode(cmcdConfiguration : JSONObject) : CMCDTransmissionMode {
450
+ try {
451
+ val transmissionMode = cmcdConfiguration.optInt(CMCD_TRANSMISSION_MODE)
452
+ if (transmissionMode === CmcdTransmissionMode.QUERY_ARGUMENT.ordinal) {
453
+ return CMCDTransmissionMode.QUERY_ARGUMENT
454
+ }
455
+ return CMCDTransmissionMode.HTTP_HEADER
456
+ } catch (e: JSONException) {
457
+ e.printStackTrace()
458
+ return CMCDTransmissionMode.HTTP_HEADER
459
+ }
460
+ }
432
461
  }
@@ -8,9 +8,9 @@ import org.json.JSONException
8
8
  import org.json.JSONObject
9
9
 
10
10
  object BridgeUtils {
11
- fun fromJSONObjectToMap(json: JSONObject): Map<String, String> {
11
+ fun fromJSONObjectToMap(json: JSONObject?): Map<String, String> {
12
12
  return mutableMapOf<String, String>().apply {
13
- json.keys().forEach { key ->
13
+ json?.keys()?.forEach { key ->
14
14
  put(key, json.getString(key))
15
15
  }
16
16
  }
@@ -226,6 +226,7 @@ class THEOplayerRCTPlayerAPI: NSObject, RCTBridgeModule {
226
226
  private func parsePipConfig(configDict: NSDictionary) -> PipConfig {
227
227
  var pipConfig = PipConfig()
228
228
  pipConfig.canStartPictureInPictureAutomaticallyFromInline = configDict["startsAutomatically"] as? Bool ?? false
229
+ pipConfig.retainPresentationModeOnSourceChange = configDict["retainPipOnSourceChange"] as? Bool ?? false
229
230
  return pipConfig
230
231
  }
231
232
 
@@ -57,7 +57,9 @@ let SD_PROP_BACKDROP_L_SHAPE: String = "backdropLShape"
57
57
  let SD_PROP_OVERRIDE_LAYOUT: String = "overrideLayout"
58
58
  let SD_PROP_OVERRIDE_AD_SRC: String = "overrideAdSrc"
59
59
  let SD_PROP_USE_ID3: String = "useId3"
60
+ let SD_PROP_RETRIEVE_POD_ID_URI: String = "retrievePodIdURI"
60
61
  let SD_PROP_HLS_DATE_RANGE: String = "hlsDateRange"
62
+ let SD_PROP_CMCD: String = "cmcd"
61
63
 
62
64
  let EXTENSION_HLS: String = ".m3u8"
63
65
  let EXTENSION_MP4: String = ".mp4"
@@ -156,8 +158,15 @@ class THEOplayerRCTSourceDescriptionBuilder {
156
158
  if let metadataData = sourceData[SD_PROP_METADATA] as? [String:Any] {
157
159
  metadataDescription = THEOplayerRCTSourceDescriptionBuilder.buildMetaDataDescription(metadataData)
158
160
  }
161
+
162
+ // 6. configure CMCD
163
+ if let cmcd = sourceData[SD_PROP_CMCD] as? [String:Any] {
164
+ typedSources.forEach { typedSource in
165
+ typedSource.cmcd = true;
166
+ }
167
+ }
159
168
 
160
- // 6. construct the SourceDescription
169
+ // 7. construct the SourceDescription
161
170
  let sourceDescription = SourceDescription(sources: typedSources,
162
171
  textTracks: textTrackDescriptions,
163
172
  ads: adsDescriptions,