bitmovin-player-react-native 1.2.0 → 1.4.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 (115) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +47 -0
  4. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +32 -3
  5. package/build/adaptationConfig.d.ts +1 -1
  6. package/build/adaptationConfig.js.map +1 -1
  7. package/build/advertising.d.ts +54 -1
  8. package/build/advertising.d.ts.map +1 -1
  9. package/build/advertising.js.map +1 -1
  10. package/build/audioSession.d.ts +3 -3
  11. package/build/audioSession.js +2 -2
  12. package/build/audioSession.js.map +1 -1
  13. package/build/bitmovinCastManager.d.ts +3 -3
  14. package/build/bitmovinCastManager.js +2 -2
  15. package/build/bitmovinCastManager.js.map +1 -1
  16. package/build/bufferConfig.d.ts +2 -2
  17. package/build/bufferConfig.js.map +1 -1
  18. package/build/components/PlayerView/events.d.ts +16 -16
  19. package/build/components/PlayerView/events.js.map +1 -1
  20. package/build/components/PlayerView/nativeEvents.d.ts +16 -16
  21. package/build/components/PlayerView/nativeEvents.js.map +1 -1
  22. package/build/components/PlayerView/pictureInPictureConfig.d.ts +1 -1
  23. package/build/components/PlayerView/pictureInPictureConfig.js.map +1 -1
  24. package/build/components/PlayerView/playerViewConfig.d.ts +2 -2
  25. package/build/components/PlayerView/playerViewConfig.js.map +1 -1
  26. package/build/decoder/decoderConfig.d.ts +2 -2
  27. package/build/decoder/decoderConfig.js.map +1 -1
  28. package/build/drm/index.d.ts +2 -2
  29. package/build/drm/index.js.map +1 -1
  30. package/build/drm/widevineConfig.d.ts +5 -5
  31. package/build/drm/widevineConfig.js.map +1 -1
  32. package/build/events.d.ts +10 -10
  33. package/build/events.js.map +1 -1
  34. package/build/media.d.ts +4 -0
  35. package/build/media.d.ts.map +1 -1
  36. package/build/media.js.map +1 -1
  37. package/build/modules/PlayerModule.d.ts +12 -1
  38. package/build/modules/PlayerModule.d.ts.map +1 -1
  39. package/build/modules/PlayerModule.js.map +1 -1
  40. package/build/offline/offlineContentConfig.d.ts +1 -1
  41. package/build/offline/offlineContentConfig.js.map +1 -1
  42. package/build/offline/offlineContentManager.d.ts +2 -2
  43. package/build/offline/offlineContentManager.js +2 -2
  44. package/build/offline/offlineContentManager.js.map +1 -1
  45. package/build/offline/offlineContentManagerListener.d.ts +13 -13
  46. package/build/offline/offlineContentManagerListener.js +1 -1
  47. package/build/offline/offlineContentManagerListener.js.map +1 -1
  48. package/build/offline/offlineContentOptions.d.ts +2 -2
  49. package/build/offline/offlineContentOptions.js.map +1 -1
  50. package/build/offline/offlineDownloadRequest.d.ts +1 -1
  51. package/build/offline/offlineDownloadRequest.js.map +1 -1
  52. package/build/offline/offlineSourceOptions.d.ts +2 -2
  53. package/build/offline/offlineSourceOptions.js.map +1 -1
  54. package/build/offline/offlineState.d.ts +1 -1
  55. package/build/offline/offlineState.js +1 -1
  56. package/build/offline/offlineState.js.map +1 -1
  57. package/build/playbackConfig.d.ts +1 -1
  58. package/build/playbackConfig.js.map +1 -1
  59. package/build/player.d.ts +13 -11
  60. package/build/player.d.ts.map +1 -1
  61. package/build/player.js +42 -11
  62. package/build/player.js.map +1 -1
  63. package/build/source.d.ts +2 -2
  64. package/build/source.js +1 -1
  65. package/build/source.js.map +1 -1
  66. package/build/styleConfig.d.ts +5 -5
  67. package/build/styleConfig.js +1 -1
  68. package/build/styleConfig.js.map +1 -1
  69. package/build/subtitleFormat.d.ts +5 -5
  70. package/build/subtitleFormat.js +5 -5
  71. package/build/subtitleFormat.js.map +1 -1
  72. package/build/subtitleTrack.d.ts +1 -1
  73. package/build/subtitleTrack.js.map +1 -1
  74. package/build/tweaksConfig.d.ts +15 -15
  75. package/build/tweaksConfig.js.map +1 -1
  76. package/ios/FullscreenHandlerModule.swift +26 -3
  77. package/ios/LockedBox.swift +6 -0
  78. package/ios/PlayerModule.swift +40 -0
  79. package/ios/RCTConvert+BitmovinPlayer.swift +41 -0
  80. package/ios/RNBitmovinPlayer.podspec +1 -1
  81. package/package.json +1 -1
  82. package/plugin/build/withBitmovinConfig.js +8 -2
  83. package/plugin/src/withAppGradleDependencies.ts +24 -21
  84. package/plugin/src/withBitmovinAndroidConfig.ts +4 -1
  85. package/plugin/src/withBitmovinConfig.ts +12 -3
  86. package/plugin/src/withBitmovinIosConfig.ts +4 -1
  87. package/src/adaptationConfig.ts +1 -1
  88. package/src/advertising.ts +56 -1
  89. package/src/audioSession.ts +3 -3
  90. package/src/bitmovinCastManager.ts +3 -3
  91. package/src/bufferConfig.ts +2 -2
  92. package/src/components/PlayerView/events.ts +16 -16
  93. package/src/components/PlayerView/nativeEvents.ts +16 -16
  94. package/src/components/PlayerView/pictureInPictureConfig.ts +1 -1
  95. package/src/components/PlayerView/playerViewConfig.ts +2 -2
  96. package/src/decoder/decoderConfig.ts +2 -2
  97. package/src/drm/index.ts +2 -2
  98. package/src/drm/widevineConfig.ts +5 -5
  99. package/src/events.ts +10 -10
  100. package/src/media.ts +4 -0
  101. package/src/modules/PlayerModule.ts +18 -2
  102. package/src/offline/offlineContentConfig.ts +1 -1
  103. package/src/offline/offlineContentManager.ts +2 -2
  104. package/src/offline/offlineContentManagerListener.ts +13 -13
  105. package/src/offline/offlineContentOptions.ts +2 -2
  106. package/src/offline/offlineDownloadRequest.ts +1 -1
  107. package/src/offline/offlineSourceOptions.ts +2 -2
  108. package/src/offline/offlineState.ts +1 -1
  109. package/src/playbackConfig.ts +1 -1
  110. package/src/player.ts +47 -12
  111. package/src/source.ts +2 -2
  112. package/src/styleConfig.ts +5 -5
  113. package/src/subtitleFormat.ts +5 -5
  114. package/src/subtitleTrack.ts +1 -1
  115. package/src/tweaksConfig.ts +15 -15
@@ -1 +1 @@
1
- {"version":3,"file":"subtitleFormat.js","sourceRoot":"","sources":["../src/subtitleFormat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,cAqBX;AArBD,WAAY,cAAc;IACxB;;;OAGG;IACH,6BAAW,CAAA;IACX;;;OAGG;IACH,+BAAa,CAAA;IACb;;;OAGG;IACH,6BAAW,CAAA;IACX;;;OAGG;IACH,6BAAW,CAAA;AACb,CAAC,EArBW,cAAc,KAAd,cAAc,QAqBzB","sourcesContent":["/**\n * Supported subtitle/caption file formats.\n * @remarks Platform: Android, iOS, tvOS\n */\nexport enum SubtitleFormat {\n /**\n * Closed Captioning (CEA) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n CEA = 'cea',\n /**\n * Timed Text Markup Language (TTML) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n TTML = 'ttml',\n /**\n * Web Video Text Tracks Format (WebVTT) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n VTT = 'vtt',\n /**\n * SubRip (SRT) subtitle format.\n * @remarks Platform: Android, iOS, tvOS\n */\n SRT = 'srt',\n}\n"]}
1
+ {"version":3,"file":"subtitleFormat.js","sourceRoot":"","sources":["../src/subtitleFormat.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,CAAN,IAAY,cAqBX;AArBD,WAAY,cAAc;IACxB;;;OAGG;IACH,6BAAW,CAAA;IACX;;;OAGG;IACH,+BAAa,CAAA;IACb;;;OAGG;IACH,6BAAW,CAAA;IACX;;;OAGG;IACH,6BAAW,CAAA;AACb,CAAC,EArBW,cAAc,KAAd,cAAc,QAqBzB","sourcesContent":["/**\n * Supported subtitle/caption file formats.\n * @platform Android, iOS, tvOS\n */\nexport enum SubtitleFormat {\n /**\n * Closed Captioning (CEA) subtitle format.\n * @platform Android, iOS, tvOS\n */\n CEA = 'cea',\n /**\n * Timed Text Markup Language (TTML) subtitle format.\n * @platform Android, iOS, tvOS\n */\n TTML = 'ttml',\n /**\n * Web Video Text Tracks Format (WebVTT) subtitle format.\n * @platform Android, iOS, tvOS\n */\n VTT = 'vtt',\n /**\n * SubRip (SRT) subtitle format.\n * @platform Android, iOS, tvOS\n */\n SRT = 'srt',\n}\n"]}
@@ -2,7 +2,7 @@ import { MediaTrackRole } from './mediaTrackRole';
2
2
  import { SubtitleFormat } from './subtitleFormat';
3
3
  /**
4
4
  * Describes a subtitle track.
5
- * @remarks Platform: Android, iOS, tvOS
5
+ * @platform Android, iOS, tvOS
6
6
  */
7
7
  export interface SubtitleTrack {
8
8
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"subtitleTrack.js","sourceRoot":"","sources":["../src/subtitleTrack.ts"],"names":[],"mappings":"","sourcesContent":["import { MediaTrackRole } from './mediaTrackRole';\nimport { SubtitleFormat } from './subtitleFormat';\n\n/**\n * Describes a subtitle track.\n * @remarks Platform: Android, iOS, tvOS\n */\nexport interface SubtitleTrack {\n /**\n * The URL to the timed file, e.g. WebVTT file.\n */\n url?: string;\n /**\n * The label for this track.\n */\n label?: string;\n /**\n * The unique identifier for this track. If no value is provided, a random UUIDv4 will be generated for it.\n */\n identifier?: string;\n /**\n * Specifies the file format to be used by this track.\n */\n format?: SubtitleFormat;\n /**\n * If set to true, this track would be considered as default. Default is `false`.\n */\n isDefault?: boolean;\n /**\n * Tells if a subtitle track is forced. If set to `true` it means that the player should automatically\n * select and switch this subtitle according to the selected audio language. Forced subtitles do\n * not appear in `Player.getAvailableSubtitles`.\n *\n * Default is `false`.\n */\n isForced?: boolean;\n /**\n * The IETF BCP 47 language tag associated with this track, e.g. `pt`, `en`, `es` etc.\n */\n language?: string;\n /**\n * An array of {@link MediaTrackRole} objects, each describing a specific role or characteristic of the subtitle track.\n * This property provides a unified way to understand track purposes (e.g., for accessibility) across platforms.\n */\n roles?: MediaTrackRole[];\n}\n\n/**\n * A subtitle track that can be added to `SourceConfig.subtitleTracks`.\n */\nexport interface SideLoadedSubtitleTrack extends SubtitleTrack {\n url: string;\n label: string;\n language: string;\n format: SubtitleFormat;\n}\n"]}
1
+ {"version":3,"file":"subtitleTrack.js","sourceRoot":"","sources":["../src/subtitleTrack.ts"],"names":[],"mappings":"","sourcesContent":["import { MediaTrackRole } from './mediaTrackRole';\nimport { SubtitleFormat } from './subtitleFormat';\n\n/**\n * Describes a subtitle track.\n * @platform Android, iOS, tvOS\n */\nexport interface SubtitleTrack {\n /**\n * The URL to the timed file, e.g. WebVTT file.\n */\n url?: string;\n /**\n * The label for this track.\n */\n label?: string;\n /**\n * The unique identifier for this track. If no value is provided, a random UUIDv4 will be generated for it.\n */\n identifier?: string;\n /**\n * Specifies the file format to be used by this track.\n */\n format?: SubtitleFormat;\n /**\n * If set to true, this track would be considered as default. Default is `false`.\n */\n isDefault?: boolean;\n /**\n * Tells if a subtitle track is forced. If set to `true` it means that the player should automatically\n * select and switch this subtitle according to the selected audio language. Forced subtitles do\n * not appear in `Player.getAvailableSubtitles`.\n *\n * Default is `false`.\n */\n isForced?: boolean;\n /**\n * The IETF BCP 47 language tag associated with this track, e.g. `pt`, `en`, `es` etc.\n */\n language?: string;\n /**\n * An array of {@link MediaTrackRole} objects, each describing a specific role or characteristic of the subtitle track.\n * This property provides a unified way to understand track purposes (e.g., for accessibility) across platforms.\n */\n roles?: MediaTrackRole[];\n}\n\n/**\n * A subtitle track that can be added to `SourceConfig.subtitleTracks`.\n */\nexport interface SideLoadedSubtitleTrack extends SubtitleTrack {\n url: string;\n label: string;\n language: string;\n format: SubtitleFormat;\n}\n"]}
@@ -34,7 +34,7 @@ export interface TweaksConfig {
34
34
  * Default value in iOS is `1.0`.
35
35
  * Default value in Android is `0.2`.
36
36
  *
37
- * @remarks Platform: iOS, Android
37
+ * @platform iOS, Android
38
38
  */
39
39
  timeChangedInterval?: number;
40
40
  /**
@@ -48,7 +48,7 @@ export interface TweaksConfig {
48
48
  *
49
49
  * Default is false.
50
50
  *
51
- * @remarks Platform: iOS
51
+ * @platform iOS
52
52
  */
53
53
  isNativeHlsParsingEnabled?: boolean;
54
54
  /**
@@ -61,7 +61,7 @@ export interface TweaksConfig {
61
61
  *
62
62
  * Default is true.
63
63
  *
64
- * @remarks Platform: iOS
64
+ * @platform iOS
65
65
  */
66
66
  isCustomHlsLoadingEnabled?: boolean;
67
67
  /**
@@ -75,7 +75,7 @@ export interface TweaksConfig {
75
75
  *
76
76
  * Default is 0.5.
77
77
  *
78
- * @remarks Platform: iOS
78
+ * @platform iOS
79
79
  */
80
80
  seekToEndThreshold?: number;
81
81
  /**
@@ -84,7 +84,7 @@ export interface TweaksConfig {
84
84
  * - 'relaxed': Starts playback when enough media data is buffered and continuous playback without stalling can be ensured. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.
85
85
  * - 'aggressive': When the buffer is not empty, this setting will cause the player to start playback of available media immediately. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.
86
86
  *
87
- * @remarks Platform: iOS
87
+ * @platform iOS
88
88
  */
89
89
  playbackStartBehaviour?: 'relaxed' | 'aggressive';
90
90
  /**
@@ -93,20 +93,20 @@ export interface TweaksConfig {
93
93
  * - 'relaxed': The player will wait until the buffer is filled that it can, most likely, ensure continuous playback without another stalling right after playback continued.
94
94
  * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately.
95
95
  *
96
- * @remarks Platform: iOS
96
+ * @platform iOS
97
97
  */
98
98
  unstallingBehaviour?: 'relaxed' | 'aggressive';
99
99
  /**
100
100
  * Constantly aggregated and weighted bandwidth samples are summed up to this weight limit to calculate an bandwidth estimation. Remaining samples (i.e. that would lead to exceeding the limit) are dropped from memory as they are not relevant anymore.
101
101
  * Default is 2000.
102
102
  *
103
- * @remarks Platform: Android
103
+ * @platform Android
104
104
  */
105
105
  bandwidthEstimateWeightLimit?: number;
106
106
  /**
107
107
  * Some devices have an incorrect implementation of MediaCodec.setOutputSurface. This leads to failure when the surface changes. To prevent failure, the codec will be released and re-instantiated in those scenarios.
108
108
  *
109
- * @remarks Platform: Android
109
+ * @platform Android
110
110
  */
111
111
  devicesThatRequireSurfaceWorkaround?: {
112
112
  /**
@@ -131,14 +131,14 @@ export interface TweaksConfig {
131
131
  * - "en_us" is normalized to "en-us"
132
132
  * - "en-US-x-lvariant-POSIX" is normalized to "en-us-posix"
133
133
  *
134
- * @remarks Platform: Android
134
+ * @platform Android
135
135
  */
136
136
  languagePropertyNormalization?: boolean;
137
137
  /**
138
138
  * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the
139
139
  * playback window is moved forward on the timeline.
140
140
  *
141
- * @remarks Platform: Android
141
+ * @platform Android
142
142
  */
143
143
  localDynamicDashWindowUpdateInterval?: number;
144
144
  /**
@@ -146,7 +146,7 @@ export interface TweaksConfig {
146
146
  * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear
147
147
  * and encrypted sections of content. Default is false.
148
148
  *
149
- * @remarks Platform: Android
149
+ * @platform Android
150
150
  */
151
151
  useDrmSessionForClearPeriods?: boolean;
152
152
  /**
@@ -155,7 +155,7 @@ export interface TweaksConfig {
155
155
  * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of
156
156
  * decoders when transitioning between clear and encrypted sections of content. Default is false.
157
157
  *
158
- * @remarks Platform: Android
158
+ * @platform Android
159
159
  */
160
160
  useDrmSessionForClearSources?: boolean;
161
161
  /**
@@ -163,7 +163,7 @@ export interface TweaksConfig {
163
163
  * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities
164
164
  * with streams and thus can result in unstable or failing playback.
165
165
  *
166
- * @remarks Platform: Android
166
+ * @platform Android
167
167
  */
168
168
  useFiletypeExtractorFallbackForHls?: boolean;
169
169
  /**
@@ -176,7 +176,7 @@ export interface TweaksConfig {
176
176
  * Default is `true`.
177
177
  *
178
178
  * @deprecated To enable the Now Playing information use {@link MediaControlConfig.isEnabled}
179
- * @remarks Platform: iOS
179
+ * @platform iOS
180
180
  */
181
181
  updatesNowPlayingInfoCenter?: boolean;
182
182
  /**
@@ -190,7 +190,7 @@ export interface TweaksConfig {
190
190
  *
191
191
  * Default is `null` i.e not set
192
192
  *
193
- * @remarks Platform: Android
193
+ * @platform Android
194
194
  */
195
195
  forceReuseVideoCodecReasons?: ForceReuseVideoCodecReason[];
196
196
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tweaksConfig.js","sourceRoot":"","sources":["../src/tweaksConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAN,IAAY,0BAaX;AAbD,WAAY,0BAA0B;IACpC;;OAEG;IACH,qEAAuC,CAAA;IACvC;;OAEG;IACH,2EAA6C,CAAA;IAC7C;;OAEG;IACH,6EAA+C,CAAA;AACjD,CAAC,EAbW,0BAA0B,KAA1B,0BAA0B,QAarC","sourcesContent":["/**\n * When switching the video quality, the video decoder's configuration might change\n * as the player can't always know if the codec supports such configuration change, it destroys and recreates it.\n * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be slow.\n *\n * If a codec is know to support a given configuration change without issues,\n * the configuration can be added to the `TweaksConfig.forceReuseVideoCodecReasons`\n * to always reuse the video codec and avoid the black screen.\n */\nexport enum ForceReuseVideoCodecReason {\n /**\n * The new video quality color information is not compatible.\n */\n ColorInfoMismatch = 'ColorInfoMismatch',\n /**\n * The new video quality exceed the decoder's configured maximum sample size.\n */\n MaxInputSizeExceeded = 'MaxInputSizeExceeded',\n /**\n * The new video quality exceed the decoder's configured maximum resolution.\n */\n MaxResolutionExceeded = 'MaxResolutionExceeded',\n}\n\n/**\n * This configuration is used as an incubator for experimental features. Tweaks are not officially\n * supported and are not guaranteed to be stable, i.e. their naming, functionality and API can\n * change at any time within the tweaks or when being promoted to an official feature and moved\n * into its final configuration namespace.\n */\nexport interface TweaksConfig {\n /**\n * The frequency in seconds `onTimeChanged` is called with `TimeChangedEvent`s.\n *\n * Default value in iOS is `1.0`.\n * Default value in Android is `0.2`.\n *\n * @remarks Platform: iOS, Android\n */\n timeChangedInterval?: number;\n /**\n * If enabled, HLS playlists will be parsed and additional features and events are enabled. This includes:\n *\n * - MetadataEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35`\n * - MetadataParsedEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35`\n * - DrmDataParsedEvents when a `#EXT-X-KEY` is found\n * - `Player.availableVideoQualities` includes additional information\n * - Automatic retries when HLS playlist requests failed with non-2xx HTTP status code\n *\n * Default is false.\n *\n * @remarks Platform: iOS\n */\n isNativeHlsParsingEnabled?: boolean;\n /**\n * If enabled, playlists will be downloaded by the Bitmovin Player SDK instead of AVFoundation.\n * This enables additional features and events, like:\n *\n * - DownloadFinishedEvents for playlist downloads.\n * - SourceWarningEvents when no `#EXT-X-PLAYLIST-TYPE` is found If set to false, enabling\n * nativeHlsParsingEnabled won’t have any effect.\n *\n * Default is true.\n *\n * @remarks Platform: iOS\n */\n isCustomHlsLoadingEnabled?: boolean;\n /**\n * The threshold which will be applied when seeking to the end in seconds. This value will be used\n * to calculate the maximum seekable time when calling `player.seek(time:)` or `player.playlist.seek(source:time:)`,\n * so the maximum value will be duration - seekToEndThreshold.\n *\n * This is useful if the duration of the segments does not match the duration specified in the\n * manifest. In this case, if we try to seek to the end, AVPlayer could get stuck and might stall\n * forever Therefore increasing this value could help.\n *\n * Default is 0.5.\n *\n * @remarks Platform: iOS\n */\n seekToEndThreshold?: number;\n /**\n * Specifies the player behaviour when `Player.play` is called. Default is 'relaxed'.\n *\n * - 'relaxed': Starts playback when enough media data is buffered and continuous playback without stalling can be ensured. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.\n * - 'aggressive': When the buffer is not empty, this setting will cause the player to start playback of available media immediately. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.\n *\n * @remarks Platform: iOS\n */\n playbackStartBehaviour?: 'relaxed' | 'aggressive';\n /**\n * Specifies the player behaviour when stalling should be exited. Default is 'relaxed'.\n *\n * - 'relaxed': The player will wait until the buffer is filled that it can, most likely, ensure continuous playback without another stalling right after playback continued.\n * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately.\n *\n * @remarks Platform: iOS\n */\n unstallingBehaviour?: 'relaxed' | 'aggressive';\n /**\n * Constantly aggregated and weighted bandwidth samples are summed up to this weight limit to calculate an bandwidth estimation. Remaining samples (i.e. that would lead to exceeding the limit) are dropped from memory as they are not relevant anymore.\n * Default is 2000.\n *\n * @remarks Platform: Android\n */\n bandwidthEstimateWeightLimit?: number;\n /**\n * Some devices have an incorrect implementation of MediaCodec.setOutputSurface. This leads to failure when the surface changes. To prevent failure, the codec will be released and re-instantiated in those scenarios.\n *\n * @remarks Platform: Android\n */\n devicesThatRequireSurfaceWorkaround?: {\n /**\n * A device name as reported by Build.DEVICE.\n *\n * @see Build.DEVICE: https://developer.android.com/reference/kotlin/android/os/Build.html#DEVICE--\n */\n deviceNames?: string[];\n /**\n * A model name as reported by Build.MODEL.\n *\n * @see Build.MODEL: https://developer.android.com/reference/kotlin/android/os/Build.html#MODEL--\n */\n modelNames?: string[];\n };\n /**\n * Specifies if the language property on DASH Representations, HLS Renditions and SmoothStreaming QualityLevels is normalized.\n * If enabled, language properties are normalized to IETF BCP 47 language tags. Default is true.\n *\n * Examples:\n * - \"ENG\" is normalized to \"en\"\n * - \"en_us\" is normalized to \"en-us\"\n * - \"en-US-x-lvariant-POSIX\" is normalized to \"en-us-posix\"\n *\n * @remarks Platform: Android\n */\n languagePropertyNormalization?: boolean;\n /**\n * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the\n * playback window is moved forward on the timeline.\n *\n * @remarks Platform: Android\n */\n localDynamicDashWindowUpdateInterval?: number;\n /**\n * Specifies whether a DRM session should be used for clear tracks of type video and audio. Using\n * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear\n * and encrypted sections of content. Default is false.\n *\n * @remarks Platform: Android\n */\n useDrmSessionForClearPeriods?: boolean;\n /**\n * Specifies whether a DRM session should be used for clear tracks of type video and audio in a clear\n * source that follows after a DRM protected source. In addition, a DRM session will be used for clear\n * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of\n * decoders when transitioning between clear and encrypted sections of content. Default is false.\n *\n * @remarks Platform: Android\n */\n useDrmSessionForClearSources?: boolean;\n /**\n * Specifies if the player should always fall back to an extractor matching the file type, if no\n * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities\n * with streams and thus can result in unstable or failing playback.\n *\n * @remarks Platform: Android\n */\n useFiletypeExtractorFallbackForHls?: boolean;\n /**\n * Determines whether `AVKit` should update Now Playing information automatically when using System UI.\n *\n * - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled.\n * This prevents interference with manual updates you may want to perform.\n * - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates.\n *\n * Default is `true`.\n *\n * @deprecated To enable the Now Playing information use {@link MediaControlConfig.isEnabled}\n * @remarks Platform: iOS\n */\n updatesNowPlayingInfoCenter?: boolean;\n\n /**\n * When switching between video formats (eg: adapting between video qualities)\n * the codec might be recreated due to several reasons.\n * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be\n * slow.\n *\n * If a device is know to support video format changes and keep the current decoder without issues,\n * this set can be filled with multiple `ForceReuseVideoCodecReason` and avoid the black screen.\n *\n * Default is `null` i.e not set\n *\n * @remarks Platform: Android\n */\n forceReuseVideoCodecReasons?: ForceReuseVideoCodecReason[];\n}\n"]}
1
+ {"version":3,"file":"tweaksConfig.js","sourceRoot":"","sources":["../src/tweaksConfig.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,CAAN,IAAY,0BAaX;AAbD,WAAY,0BAA0B;IACpC;;OAEG;IACH,qEAAuC,CAAA;IACvC;;OAEG;IACH,2EAA6C,CAAA;IAC7C;;OAEG;IACH,6EAA+C,CAAA;AACjD,CAAC,EAbW,0BAA0B,KAA1B,0BAA0B,QAarC","sourcesContent":["/**\n * When switching the video quality, the video decoder's configuration might change\n * as the player can't always know if the codec supports such configuration change, it destroys and recreates it.\n * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be slow.\n *\n * If a codec is know to support a given configuration change without issues,\n * the configuration can be added to the `TweaksConfig.forceReuseVideoCodecReasons`\n * to always reuse the video codec and avoid the black screen.\n */\nexport enum ForceReuseVideoCodecReason {\n /**\n * The new video quality color information is not compatible.\n */\n ColorInfoMismatch = 'ColorInfoMismatch',\n /**\n * The new video quality exceed the decoder's configured maximum sample size.\n */\n MaxInputSizeExceeded = 'MaxInputSizeExceeded',\n /**\n * The new video quality exceed the decoder's configured maximum resolution.\n */\n MaxResolutionExceeded = 'MaxResolutionExceeded',\n}\n\n/**\n * This configuration is used as an incubator for experimental features. Tweaks are not officially\n * supported and are not guaranteed to be stable, i.e. their naming, functionality and API can\n * change at any time within the tweaks or when being promoted to an official feature and moved\n * into its final configuration namespace.\n */\nexport interface TweaksConfig {\n /**\n * The frequency in seconds `onTimeChanged` is called with `TimeChangedEvent`s.\n *\n * Default value in iOS is `1.0`.\n * Default value in Android is `0.2`.\n *\n * @platform iOS, Android\n */\n timeChangedInterval?: number;\n /**\n * If enabled, HLS playlists will be parsed and additional features and events are enabled. This includes:\n *\n * - MetadataEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35`\n * - MetadataParsedEvents carrying segment-specific metadata for custom HLS tags, like `#EXT-X-SCTE35`\n * - DrmDataParsedEvents when a `#EXT-X-KEY` is found\n * - `Player.availableVideoQualities` includes additional information\n * - Automatic retries when HLS playlist requests failed with non-2xx HTTP status code\n *\n * Default is false.\n *\n * @platform iOS\n */\n isNativeHlsParsingEnabled?: boolean;\n /**\n * If enabled, playlists will be downloaded by the Bitmovin Player SDK instead of AVFoundation.\n * This enables additional features and events, like:\n *\n * - DownloadFinishedEvents for playlist downloads.\n * - SourceWarningEvents when no `#EXT-X-PLAYLIST-TYPE` is found If set to false, enabling\n * nativeHlsParsingEnabled won’t have any effect.\n *\n * Default is true.\n *\n * @platform iOS\n */\n isCustomHlsLoadingEnabled?: boolean;\n /**\n * The threshold which will be applied when seeking to the end in seconds. This value will be used\n * to calculate the maximum seekable time when calling `player.seek(time:)` or `player.playlist.seek(source:time:)`,\n * so the maximum value will be duration - seekToEndThreshold.\n *\n * This is useful if the duration of the segments does not match the duration specified in the\n * manifest. In this case, if we try to seek to the end, AVPlayer could get stuck and might stall\n * forever Therefore increasing this value could help.\n *\n * Default is 0.5.\n *\n * @platform iOS\n */\n seekToEndThreshold?: number;\n /**\n * Specifies the player behaviour when `Player.play` is called. Default is 'relaxed'.\n *\n * - 'relaxed': Starts playback when enough media data is buffered and continuous playback without stalling can be ensured. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.\n * - 'aggressive': When the buffer is not empty, this setting will cause the player to start playback of available media immediately. If insufficient media data is buffered for playback to start, the player will act as if the buffer became empty during playback.\n *\n * @platform iOS\n */\n playbackStartBehaviour?: 'relaxed' | 'aggressive';\n /**\n * Specifies the player behaviour when stalling should be exited. Default is 'relaxed'.\n *\n * - 'relaxed': The player will wait until the buffer is filled that it can, most likely, ensure continuous playback without another stalling right after playback continued.\n * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately.\n *\n * @platform iOS\n */\n unstallingBehaviour?: 'relaxed' | 'aggressive';\n /**\n * Constantly aggregated and weighted bandwidth samples are summed up to this weight limit to calculate an bandwidth estimation. Remaining samples (i.e. that would lead to exceeding the limit) are dropped from memory as they are not relevant anymore.\n * Default is 2000.\n *\n * @platform Android\n */\n bandwidthEstimateWeightLimit?: number;\n /**\n * Some devices have an incorrect implementation of MediaCodec.setOutputSurface. This leads to failure when the surface changes. To prevent failure, the codec will be released and re-instantiated in those scenarios.\n *\n * @platform Android\n */\n devicesThatRequireSurfaceWorkaround?: {\n /**\n * A device name as reported by Build.DEVICE.\n *\n * @see Build.DEVICE: https://developer.android.com/reference/kotlin/android/os/Build.html#DEVICE--\n */\n deviceNames?: string[];\n /**\n * A model name as reported by Build.MODEL.\n *\n * @see Build.MODEL: https://developer.android.com/reference/kotlin/android/os/Build.html#MODEL--\n */\n modelNames?: string[];\n };\n /**\n * Specifies if the language property on DASH Representations, HLS Renditions and SmoothStreaming QualityLevels is normalized.\n * If enabled, language properties are normalized to IETF BCP 47 language tags. Default is true.\n *\n * Examples:\n * - \"ENG\" is normalized to \"en\"\n * - \"en_us\" is normalized to \"en-us\"\n * - \"en-US-x-lvariant-POSIX\" is normalized to \"en-us-posix\"\n *\n * @platform Android\n */\n languagePropertyNormalization?: boolean;\n /**\n * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the\n * playback window is moved forward on the timeline.\n *\n * @platform Android\n */\n localDynamicDashWindowUpdateInterval?: number;\n /**\n * Specifies whether a DRM session should be used for clear tracks of type video and audio. Using\n * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear\n * and encrypted sections of content. Default is false.\n *\n * @platform Android\n */\n useDrmSessionForClearPeriods?: boolean;\n /**\n * Specifies whether a DRM session should be used for clear tracks of type video and audio in a clear\n * source that follows after a DRM protected source. In addition, a DRM session will be used for clear\n * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of\n * decoders when transitioning between clear and encrypted sections of content. Default is false.\n *\n * @platform Android\n */\n useDrmSessionForClearSources?: boolean;\n /**\n * Specifies if the player should always fall back to an extractor matching the file type, if no\n * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities\n * with streams and thus can result in unstable or failing playback.\n *\n * @platform Android\n */\n useFiletypeExtractorFallbackForHls?: boolean;\n /**\n * Determines whether `AVKit` should update Now Playing information automatically when using System UI.\n *\n * - If set to `false`, the automatic updates of Now Playing Info sent by `AVKit` are disabled.\n * This prevents interference with manual updates you may want to perform.\n * - If set to `true`, the default behaviour is maintained, allowing `AVKit` to handle Now Playing updates.\n *\n * Default is `true`.\n *\n * @deprecated To enable the Now Playing information use {@link MediaControlConfig.isEnabled}\n * @platform iOS\n */\n updatesNowPlayingInfoCenter?: boolean;\n\n /**\n * When switching between video formats (eg: adapting between video qualities)\n * the codec might be recreated due to several reasons.\n * This behaviour can cause brief black screens when switching between video qualities as codec recreation can be\n * slow.\n *\n * If a device is know to support video format changes and keep the current decoder without issues,\n * this set can be filled with multiple `ForceReuseVideoCodecReason` and avoid the black screen.\n *\n * Default is `null` i.e not set\n *\n * @platform Android\n */\n forceReuseVideoCodecReasons?: ForceReuseVideoCodecReason[];\n}\n"]}
@@ -12,30 +12,46 @@ public class FullscreenHandlerModule: Module {
12
12
  /// ResultWaiter used for blocking thread while waiting for fullscreen state change
13
13
  private let waiter = ResultWaiter<Bool>()
14
14
 
15
+ private var fullscreenActiveUpdateBuffer = LockedBox(value: [NativeId: Bool]())
16
+
15
17
  public func definition() -> ModuleDefinition {
16
18
  Name("FullscreenHandlerModule")
17
19
 
18
20
  OnDestroy {
19
21
  fullscreenHandlers.removeAll()
20
22
  waiter.removeAll()
23
+ fullscreenActiveUpdateBuffer.update { value in
24
+ value.removeAll()
25
+ }
21
26
  }
22
27
 
23
28
  Events("onEnterFullscreen", "onExitFullscreen")
24
29
 
25
30
  AsyncFunction("registerHandler") { (nativeId: NativeId) in
26
31
  DispatchQueue.main.async { [weak self] in
27
- guard let self, self.fullscreenHandlers[nativeId] == nil else {
32
+ guard let self, fullscreenHandlers[nativeId] == nil else {
28
33
  return
29
34
  }
30
- self.fullscreenHandlers[nativeId] = FullscreenHandlerBridge(
35
+ let handler = FullscreenHandlerBridge(
31
36
  nativeId,
32
37
  moduleRegistry: appContext?.moduleRegistry
33
38
  )
39
+ if let isFullscreen = fullscreenActiveUpdateBuffer.value[nativeId] {
40
+ // Apply buffered value
41
+ handler.isFullscreenValueBox.update(isFullscreen)
42
+ fullscreenActiveUpdateBuffer.update { value in
43
+ value.removeValue(forKey: nativeId)
44
+ }
45
+ }
46
+ fullscreenHandlers[nativeId] = handler
34
47
  }
35
48
  }.runOnQueue(.main)
36
49
 
37
50
  AsyncFunction("destroy") { [weak self] (nativeId: NativeId) in
38
51
  self?.fullscreenHandlers.removeValue(forKey: nativeId)
52
+ self?.fullscreenActiveUpdateBuffer.update { value in
53
+ value.removeValue(forKey: nativeId)
54
+ }
39
55
  }.runOnQueue(.main)
40
56
 
41
57
  AsyncFunction("notifyFullscreenChanged") { [weak self] (id: Int, isFullscreenEnabled: Bool) in
@@ -43,7 +59,14 @@ public class FullscreenHandlerModule: Module {
43
59
  }
44
60
 
45
61
  AsyncFunction("setIsFullscreenActive") { [weak self] (nativeId: NativeId, isFullscreen: Bool) in
46
- self?.fullscreenHandlers[nativeId]?.isFullscreenValueBox.update(isFullscreen)
62
+ guard let handler = self?.fullscreenHandlers[nativeId] else {
63
+ // Buffer the value until the handler is registered
64
+ self?.fullscreenActiveUpdateBuffer.update { value in
65
+ value[nativeId] = isFullscreen
66
+ }
67
+ return
68
+ }
69
+ handler.isFullscreenValueBox.update(isFullscreen)
47
70
  }.runOnQueue(.main)
48
71
  }
49
72
 
@@ -18,4 +18,10 @@ internal class LockedBox<T> {
18
18
  self._value = value
19
19
  }
20
20
  }
21
+
22
+ func update(_ updateBlock: (inout T) -> Void) {
23
+ lock.withLock {
24
+ updateBlock(&self._value)
25
+ }
26
+ }
21
27
  }
@@ -1,7 +1,10 @@
1
1
  import BitmovinPlayer
2
2
  import ExpoModulesCore
3
3
 
4
+ // swiftlint:disable:next type_body_length
4
5
  public class PlayerModule: Module {
6
+ private let imaSettingsWaiter = ResultWaiter<[String: Any]>()
7
+
5
8
  // swiftlint:disable:next function_body_length
6
9
  public func definition() -> ModuleDefinition {
7
10
  Name("PlayerModule")
@@ -14,7 +17,9 @@ public class PlayerModule: Module {
14
17
  PlayerRegistry.getAllPlayers().forEach { $0.destroy() }
15
18
  PlayerRegistry.clear()
16
19
  }
20
+ imaSettingsWaiter.removeAll()
17
21
  }
22
+ Events("onImaBeforeInitialization")
18
23
  AsyncFunction("play") { (nativeId: NativeId) in
19
24
  PlayerRegistry.getPlayer(nativeId: nativeId)?.play()
20
25
  }.runOnQueue(.main)
@@ -169,6 +174,9 @@ public class PlayerModule: Module {
169
174
  AsyncFunction("skipAd") { (nativeId: NativeId) in
170
175
  PlayerRegistry.getPlayer(nativeId: nativeId)?.skipAd()
171
176
  }.runOnQueue(.main)
177
+ AsyncFunction("setPreparedImaSettings") { [weak self] (id: Int, settings: [String: Any]?) in
178
+ self?.imaSettingsWaiter.complete(id: id, with: settings ?? [:])
179
+ }
172
180
  AsyncFunction(
173
181
  "initializeWithConfig"
174
182
  ) { [weak self] (nativeId: NativeId, config: [String: Any]?, networkNativeId: NativeId?, _: String?) in // swiftlint:disable:this line_length
@@ -180,6 +188,7 @@ public class PlayerModule: Module {
180
188
  if let networkNativeId, let networkConfig = self?.setupNetworkConfig(nativeId: networkNativeId) {
181
189
  playerConfig.networkConfig = networkConfig
182
190
  }
191
+ self?.setupImaBeforeInitialization(nativeId: nativeId, config: config, playerConfig: playerConfig)
183
192
  let player = PlayerFactory.create(playerConfig: playerConfig)
184
193
  PlayerRegistry.register(player: player, nativeId: nativeId)
185
194
  }.runOnQueue(.main)
@@ -195,6 +204,7 @@ public class PlayerModule: Module {
195
204
  if let networkNativeId, let networkConfig = self?.setupNetworkConfig(nativeId: networkNativeId) {
196
205
  playerConfig.networkConfig = networkConfig
197
206
  }
207
+ self?.setupImaBeforeInitialization(nativeId: nativeId, config: config, playerConfig: playerConfig)
198
208
  let defaultMetadata = RCTConvert.analyticsDefaultMetadataFromAnalyticsConfig(analyticsConfig)
199
209
  let player = PlayerFactory.create(
200
210
  playerConfig: playerConfig,
@@ -235,4 +245,34 @@ public class PlayerModule: Module {
235
245
  }
236
246
  return networkModule.retrieve(nativeId)
237
247
  }
248
+
249
+ private func setupImaBeforeInitialization(
250
+ nativeId: NativeId,
251
+ config: [String: Any]?,
252
+ playerConfig: PlayerConfig
253
+ ) {
254
+ guard
255
+ let advertisingJson = config?["advertisingConfig"] as? [String: Any],
256
+ let imaJson = advertisingJson["ima"] as? [String: Any],
257
+ imaJson["beforeInitialization"] != nil
258
+ else {
259
+ return
260
+ }
261
+
262
+ playerConfig.advertisingConfig.beforeInitialization = { [weak self] settings in
263
+ guard let self else { return }
264
+ let (id, wait) = imaSettingsWaiter.make(timeout: 0.25)
265
+ let payload = RCTConvert.imaSettingsDictionary(settings)
266
+ self.sendEvent(
267
+ "onImaBeforeInitialization",
268
+ [
269
+ "nativeId": nativeId,
270
+ "id": id,
271
+ "settings": payload
272
+ ]
273
+ )
274
+ guard let updated = wait() else { return }
275
+ RCTConvert.applyImaSettings(settings, from: updated)
276
+ }
277
+ }
238
278
  }
@@ -247,6 +247,47 @@ extension RCTConvert {
247
247
  return AdvertisingConfig(schedule: schedule.compactMap { RCTConvert.adItem($0) })
248
248
  }
249
249
 
250
+ static func imaSettingsDictionary(_ settings: ImaSettings) -> [String: Any] {
251
+ var map: [String: Any] = [
252
+ "language": settings.language,
253
+ "maxRedirects": Int(settings.maxRedirects),
254
+ "enableBackgroundPlayback": settings.enableBackgroundPlayback,
255
+ "ppid": settings.ppid,
256
+ "playerVersion": settings.playerVersion,
257
+ "sessionId": settings.sessionId
258
+ ]
259
+ #if !os(tvOS)
260
+ map["sameAppKeyEnabled"] = settings.sameAppKeyEnabled
261
+ #endif
262
+ return map
263
+ }
264
+
265
+ static func applyImaSettings(_ settings: ImaSettings, from json: [String: Any]) {
266
+ if let ppid = json["ppid"] as? String {
267
+ settings.ppid = ppid
268
+ }
269
+ if let language = json["language"] as? String {
270
+ settings.language = language
271
+ }
272
+ if let redirects = json["maxRedirects"] as? NSNumber {
273
+ settings.maxRedirects = redirects.uintValue
274
+ }
275
+ if let enableBackgroundPlayback = json["enableBackgroundPlayback"] as? Bool {
276
+ settings.enableBackgroundPlayback = enableBackgroundPlayback
277
+ }
278
+ if let playerVersion = json["playerVersion"] as? String {
279
+ settings.playerVersion = playerVersion
280
+ }
281
+ if let sessionId = json["sessionId"] as? String {
282
+ settings.sessionId = sessionId
283
+ }
284
+ #if !os(tvOS)
285
+ if let sameAppKeyEnabled = json["sameAppKeyEnabled"] as? Bool {
286
+ settings.sameAppKeyEnabled = sameAppKeyEnabled
287
+ }
288
+ #endif
289
+ }
290
+
250
291
  static func adItem(_ json: Any?) -> AdItem? {
251
292
  guard
252
293
  let json = json as? [String: Any?],
@@ -28,7 +28,7 @@ Pod::Spec.new do |s|
28
28
  s.static_framework = true
29
29
 
30
30
  s.dependency 'ExpoModulesCore'
31
- s.dependency "BitmovinPlayer", "3.97.2"
31
+ s.dependency "BitmovinPlayer", "3.98.0"
32
32
  s.ios.dependency "GoogleAds-IMA-iOS-SDK", "3.26.1"
33
33
  s.tvos.dependency "GoogleAds-IMA-tvOS-SDK", "4.15.1"
34
34
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitmovin-player-react-native",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "Official React Native bindings for Bitmovin's mobile Player SDKs.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -43,8 +43,14 @@ const defaultFeatures = {
43
43
  const withBitmovinConfig = (config, options) => {
44
44
  const { playerLicenseKey, features } = options;
45
45
  const mergedFeatures = { ...defaultFeatures, ...(features || {}) };
46
- config = (0, withBitmovinIosConfig_1.default)(config, { playerLicenseKey, features: mergedFeatures });
47
- config = (0, withBitmovinAndroidConfig_1.default)(config, { playerLicenseKey, features: mergedFeatures });
46
+ config = (0, withBitmovinIosConfig_1.default)(config, {
47
+ playerLicenseKey,
48
+ features: mergedFeatures,
49
+ });
50
+ config = (0, withBitmovinAndroidConfig_1.default)(config, {
51
+ playerLicenseKey,
52
+ features: mergedFeatures,
53
+ });
48
54
  return config;
49
55
  };
50
56
  exports.default = withBitmovinConfig;
@@ -38,38 +38,39 @@ const withAppGradleDependencies: ConfigPlugin<PluginProps> = (
38
38
  return config;
39
39
  }
40
40
 
41
- const androidBlockStart = config.modResults.contents.search(/^android \{$/m);
41
+ const androidBlockStart =
42
+ config.modResults.contents.search(/^android \{$/m);
42
43
  if (androidBlockStart === -1) {
43
44
  WarningAggregator.addWarningAndroid(
44
- 'withAppGradleDependencies',
45
- `Cannot configure app/build.gradle as no android block start was found`
45
+ 'withAppGradleDependencies',
46
+ `Cannot configure app/build.gradle as no android block start was found`
46
47
  );
47
48
  return config;
48
49
  }
49
- const fromAndroid = config.modResults.contents.substring(
50
- androidBlockStart
51
- );
50
+ const fromAndroid = config.modResults.contents.substring(androidBlockStart);
52
51
  const androidBlockEnd = fromAndroid.search(/^\}$/m);
53
52
  if (androidBlockEnd === -1) {
54
53
  WarningAggregator.addWarningAndroid(
55
- 'withAppGradleDependencies',
56
- `Cannot configure app/build.gradle as no android block end was found`
54
+ 'withAppGradleDependencies',
55
+ `Cannot configure app/build.gradle as no android block end was found`
57
56
  );
58
57
  return config;
59
58
  }
60
59
  const androidPosition = androidBlockStart + androidBlockEnd;
61
- const compileOptions = []
62
- compileOptions.push(`${combinedProps.spacing}compileOptions {`)
63
- compileOptions.push('\n');
64
- compileOptions.push(`${combinedProps.spacing}setCoreLibraryDesugaringEnabled(true)`)
65
- compileOptions.push('\n');
66
- compileOptions.push(`${combinedProps.spacing}}`)
67
- compileOptions.push('\n');
68
- config.modResults.contents = [
69
- config.modResults.contents.slice(0, androidPosition),
70
- ... compileOptions,
71
- config.modResults.contents.slice(androidPosition),
72
- ].join('');
60
+ const compileOptions = [];
61
+ compileOptions.push(`${combinedProps.spacing}compileOptions {`);
62
+ compileOptions.push('\n');
63
+ compileOptions.push(
64
+ `${combinedProps.spacing}setCoreLibraryDesugaringEnabled(true)`
65
+ );
66
+ compileOptions.push('\n');
67
+ compileOptions.push(`${combinedProps.spacing}}`);
68
+ compileOptions.push('\n');
69
+ config.modResults.contents = [
70
+ config.modResults.contents.slice(0, androidPosition),
71
+ ...compileOptions,
72
+ config.modResults.contents.slice(androidPosition),
73
+ ].join('');
73
74
 
74
75
  const dependenciesBlockStart =
75
76
  config.modResults.contents.search(/^dependencies \{$/m);
@@ -94,7 +95,9 @@ const withAppGradleDependencies: ConfigPlugin<PluginProps> = (
94
95
  const position = dependenciesBlockStart + dependenciesBlockEnd;
95
96
  let insertedDependencies: string[] = [];
96
97
  insertedDependencies.push('\n');
97
- insertedDependencies.push(`${combinedProps.spacing}coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'`)
98
+ insertedDependencies.push(
99
+ `${combinedProps.spacing}coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'`
100
+ );
98
101
  insertedDependencies.push('\n');
99
102
  filteredDependencies.forEach((dependency) => {
100
103
  insertedDependencies.push(
@@ -9,7 +9,10 @@ import { BitmovinConfigOptions } from './withBitmovinConfig';
9
9
 
10
10
  type ManifestActivity = AndroidConfig.Manifest.ManifestActivity;
11
11
 
12
- const withBitmovinAndroidConfig: ConfigPlugin<BitmovinConfigOptions> = (config, options) => {
12
+ const withBitmovinAndroidConfig: ConfigPlugin<BitmovinConfigOptions> = (
13
+ config,
14
+ options
15
+ ) => {
13
16
  const { playerLicenseKey = '', features = {} } = options || {};
14
17
  const offlineFeatureConfig =
15
18
  typeof features.offline === 'object'
@@ -44,11 +44,20 @@ export interface BitmovinConfigOptions {
44
44
  * ]
45
45
  * };
46
46
  */
47
- const withBitmovinConfig: ConfigPlugin<BitmovinConfigOptions> = (config, options) => {
47
+ const withBitmovinConfig: ConfigPlugin<BitmovinConfigOptions> = (
48
+ config,
49
+ options
50
+ ) => {
48
51
  const { playerLicenseKey, features } = options;
49
52
  const mergedFeatures = { ...defaultFeatures, ...(features || {}) };
50
- config = withBitmovinIosConfig(config, { playerLicenseKey, features: mergedFeatures });
51
- config = withBitmovinAndroidConfig(config, { playerLicenseKey, features: mergedFeatures });
53
+ config = withBitmovinIosConfig(config, {
54
+ playerLicenseKey,
55
+ features: mergedFeatures,
56
+ });
57
+ config = withBitmovinAndroidConfig(config, {
58
+ playerLicenseKey,
59
+ features: mergedFeatures,
60
+ });
52
61
  return config;
53
62
  };
54
63
 
@@ -7,7 +7,10 @@ import { BitmovinConfigOptions } from './withBitmovinConfig';
7
7
 
8
8
  const isTV = !!process.env.EXPO_TV;
9
9
 
10
- const withBitmovinIosConfig: ConfigPlugin<BitmovinConfigOptions> = (config, options) => {
10
+ const withBitmovinIosConfig: ConfigPlugin<BitmovinConfigOptions> = (
11
+ config,
12
+ options
13
+ ) => {
11
14
  const { playerLicenseKey = '', features = {} } = options || {};
12
15
  const offlineFeatureConfig =
13
16
  typeof features.offline === 'object'
@@ -11,7 +11,7 @@ export interface AdaptationConfig {
11
11
  /**
12
12
  * The initial bandwidth estimate in bits per second the player uses to select the optimal media tracks before actual bandwidth data is available. Overriding this value should only be done in specific cases and will most of the time not result in better selection logic.
13
13
  *
14
- * @remarks Platform: Android
14
+ * @platform Android
15
15
  * @see https://cdn.bitmovin.com/player/android/3/docs/player-core/com.bitmovin.player.api.media/-adaptation-config/initial-bandwidth-estimate-override.html
16
16
  */
17
17
  initialBandwidthEstimateOverride?: number;
@@ -83,7 +83,7 @@ export interface AdItem {
83
83
  *
84
84
  * Default value is 0.0
85
85
  *
86
- * @remarks Platform: Android
86
+ * @platform Android
87
87
  */
88
88
  preloadOffset?: number;
89
89
  }
@@ -96,6 +96,61 @@ export interface AdvertisingConfig {
96
96
  * The ad items that are scheduled when a new playback session is started via `Player.load()`.
97
97
  */
98
98
  schedule: AdItem[];
99
+ /**
100
+ * Configuration to customize Google IMA SDK integration.
101
+ */
102
+ ima?: ImaAdvertisingConfig;
103
+ }
104
+
105
+ /**
106
+ * Configuration options applied to Google IMA SDK before initialization.
107
+ */
108
+ export interface ImaAdvertisingConfig {
109
+ /**
110
+ * Invoked before the IMA SDK initializes, allowing mutation of the SDK settings.
111
+ *
112
+ * @param settings - Current IMA settings received from the native SDK.
113
+ * @returns The settings object to apply. If omitted, the (mutated) `settings` argument is used.
114
+ */
115
+ beforeInitialization?: (settings: ImaSettings) => ImaSettings | void;
116
+ }
117
+
118
+ /**
119
+ * Represents Google IMA SDK wide settings.
120
+ */
121
+ export interface ImaSettings {
122
+ /**
123
+ * Publisher Provided Identification (PPID) sent with ad requests.
124
+ */
125
+ ppid?: string;
126
+ /**
127
+ * Language identifier in IETF BCP 47 format.
128
+ */
129
+ language: string;
130
+ /**
131
+ * Maximum allowed wrapper redirects. Default is `4`.
132
+ */
133
+ maxRedirects: number;
134
+ /**
135
+ * Enables background audio playback. Defaults to `false`.
136
+ *
137
+ * @platform iOS, tvOS
138
+ */
139
+ enableBackgroundPlayback?: boolean;
140
+ /**
141
+ * Player version identifier reported to IMA.
142
+ */
143
+ playerVersion?: string;
144
+ /**
145
+ * Session identifier used for frequency capping.
146
+ */
147
+ sessionId?: string;
148
+ /**
149
+ * Controls Same App Key usage.
150
+ *
151
+ * @platform iOS
152
+ */
153
+ sameAppKeyEnabled?: boolean;
99
154
  }
100
155
 
101
156
  /**