bitmovin-player-react-native 0.2.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,23 +3,42 @@
3
3
  Official React Native bindings for Bitmovin's mobile Player SDKs.
4
4
 
5
5
  [![npm](https://img.shields.io/npm/v/bitmovin-player-react-native)](https://www.npmjs.com/package/bitmovin-player-react-native)
6
- ![Supports Android and iOS](https://img.shields.io/badge/platforms-android%20%7C%20ios-lightgrey.svg)
6
+ ![Platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS%20%7C%20Android%20%7C%20Android%20TV-lightgrey.svg)
7
7
  [![MIT License](https://img.shields.io/badge/license-MIT-brightgreen.svg)](LICENSE)
8
8
  [![Bitmovin Community](https://img.shields.io/discourse/users?label=community&server=https%3A%2F%2Fcommunity.bitmovin.com)](https://community.bitmovin.com/?utm_source=github&utm_medium=bitmovin-player-react-native&utm_campaign=dev-community)
9
9
 
10
- > :warning: **Beta Version**: The library is under active development.
11
-
12
- - [Installation](#installation)
13
- - [Add package dependency](#add-package-dependency)
14
- - [Setup iOS Player SDK](#setup-ios-player-sdk)
15
- - [Setup Android Player SDK](#setup-android-player-sdk)
16
- - [Getting Started](#getting-started)
17
- - [Setting up a license key](#setting-up-a-license-key)
18
- - [Accessing native `Player` instances](#accessing-native-player-instances)
19
- - [Listening to events](#listening-to-events)
20
- - [Enabling DRM protection](#enabling-drm-protection)
21
- - [Adding external subtitle tracks](#adding-subtitle-tracks)
22
- - [Contributing](#contributing)
10
+ > The library is under active development.
11
+
12
+ - [Bitmovin Player React Native](#bitmovin-player-react-native)
13
+ - [Platform Support](#platform-support)
14
+ - [Installation](#installation)
15
+ - [Add package dependency](#add-package-dependency)
16
+ - [Setup iOS Player SDK](#setup-ios-player-sdk)
17
+ - [Setup Android Player SDK](#setup-android-player-sdk)
18
+ - [Getting Started](#getting-started)
19
+ - [Setting up a license key](#setting-up-a-license-key)
20
+ - [Configuring through code](#configuring-through-code)
21
+ - [Setting up a playback configurations](#setting-up-a-playback-configurations)
22
+ - [Configuring `Info.plist`](#configuring-infoplist)
23
+ - [Configuring `AndroidManifest.xml`](#configuring-androidmanifestxml)
24
+ - [Accessing native `Player` instances](#accessing-native-player-instances)
25
+ - [Listening to events](#listening-to-events)
26
+ - [Enabling DRM protection](#enabling-drm-protection)
27
+ - [Prepare hooks](#prepare-hooks)
28
+ - [Adding external subtitle tracks](#adding-external-subtitle-tracks)
29
+ - [Contributing](#contributing)
30
+
31
+ ## Platform Support
32
+
33
+ This library requires at least React Native 0.64+ and React 17+ to work properly. The currently supported platforms are:
34
+
35
+ - iOS 12.0+
36
+ - tvOS 12.0+
37
+ - Android API 16+
38
+ - Android TV API 17+
39
+ - Fire TV (just make sure the Android API level is at least 17+)
40
+
41
+ Please note that browsers and other browser-like environments such as webOS and Tizen are not supported.
23
42
 
24
43
  ## Installation
25
44
 
@@ -200,6 +219,42 @@ const player = new Player({
200
219
  });
201
220
  ```
202
221
 
222
+ ### Setting up a playback configurations
223
+
224
+ If needed, the default player behavior can be configured through the `playbackConfig` key when initialized.
225
+
226
+ ```typescript
227
+ // Simply pass the `playbackConfig` property to `PlayerConfig` when instantiating a player.
228
+
229
+ // With hooks
230
+ import { usePlayer } from 'bitmovin-player-react-native';
231
+ const player = usePlayer({
232
+ playbackConfig: {
233
+ // Specifies whether the playback starts immediately after loading a source or not. Default is false.
234
+ isAutoplayEnabled: true,
235
+ // Specifies if playback starts muted. Default is false.
236
+ isMuted: true,
237
+ // Specifies if time shift for live streams should be enabled. Default is true.
238
+ isTimeShiftEnabled: true,
239
+ // Whether background playback is enabled or not. Default is false.
240
+ // Only available for iOS.
241
+ isBackgroundPlaybackEnabled: true,
242
+ },
243
+ });
244
+
245
+ // Without hooks
246
+ import { Player } from 'bitmovin-player-react-native';
247
+ const player = new Player({
248
+ // Make sure to use React.createRef if instantiating inside a component.
249
+ playbackConfig: {
250
+ isAutoplayEnabled: true,
251
+ isMuted: true,
252
+ isTimeShiftEnabled: true,
253
+ isBackgroundPlaybackEnabled: true,
254
+ },
255
+ });
256
+ ```
257
+
203
258
  #### Configuring `Info.plist`
204
259
 
205
260
  Add the following lines to the `<dict>` section of your `ios/Info.plist`:
@@ -10,7 +10,7 @@ Pod::Spec.new do |s|
10
10
  s.license = package["license"]
11
11
  s.authors = package["author"]
12
12
 
13
- s.platforms = { :ios => "12.4" }
13
+ s.platforms = { :ios => "12.4", :tvos => "12.4" }
14
14
  s.source = {
15
15
  :git => "https://github.com/bitmovin/bitmovin-player-react-native.git",
16
16
  :tag => "v#{s.version}"
@@ -19,5 +19,5 @@ Pod::Spec.new do |s|
19
19
  s.source_files = "ios/**/*.{h,m,mm,swift}"
20
20
 
21
21
  s.dependency "React-Core"
22
- s.dependency "BitmovinPlayer", "3.23.0"
22
+ s.dependency "BitmovinPlayer", "3.28.0"
23
23
  end
@@ -50,6 +50,6 @@ android {
50
50
 
51
51
  dependencies {
52
52
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
53
- implementation 'com.bitmovin.player:player:3.20.0'
53
+ implementation 'com.bitmovin.player:player:3.24.2'
54
54
  implementation 'com.facebook.react:react-native:+'
55
55
  }
@@ -1,6 +1,10 @@
1
1
  package com.bitmovin.player.reactnative.converter
2
2
 
3
+ import com.bitmovin.player.api.DeviceDescription.DeviceName
4
+ import com.bitmovin.player.api.DeviceDescription.ModelName
5
+ import com.bitmovin.player.api.PlaybackConfig
3
6
  import com.bitmovin.player.api.PlayerConfig
7
+ import com.bitmovin.player.api.TweaksConfig
4
8
  import com.bitmovin.player.api.drm.WidevineConfig
5
9
  import com.bitmovin.player.api.event.PlayerEvent
6
10
  import com.bitmovin.player.api.event.SourceEvent
@@ -10,6 +14,7 @@ import com.bitmovin.player.api.source.Source
10
14
  import com.bitmovin.player.api.source.SourceConfig
11
15
  import com.bitmovin.player.api.source.SourceType
12
16
  import com.bitmovin.player.reactnative.extensions.getName
17
+ import com.bitmovin.player.reactnative.extensions.toList
13
18
  import com.facebook.react.bridge.*
14
19
  import java.util.UUID
15
20
 
@@ -25,10 +30,98 @@ class JsonConverter {
25
30
  */
26
31
  @JvmStatic
27
32
  fun toPlayerConfig(json: ReadableMap?): PlayerConfig {
28
- if (json != null && json.hasKey("licenseKey")) {
29
- return PlayerConfig(key = json.getString("licenseKey"))
33
+ if (json == null) return PlayerConfig()
34
+ val playerConfig = if (json.hasKey("licenseKey")) {
35
+ PlayerConfig(key = json.getString("licenseKey"))
36
+ } else {
37
+ PlayerConfig()
38
+ }
39
+ if (json.hasKey("playbackConfig")) {
40
+ toPlaybackConfig(json.getMap("playbackConfig"))?.let {
41
+ playerConfig.playbackConfig = it
42
+ }
43
+ }
44
+ if (json.hasKey("tweaksConfig")) {
45
+ toTweaksConfig(json.getMap("tweaksConfig"))?.let {
46
+ playerConfig.tweaksConfig = it
47
+ }
48
+ }
49
+ return playerConfig
50
+ }
51
+
52
+ /**
53
+ * Converts any JS object into a `PlaybackConfig` object.
54
+ * @param json JS object representing the `PlaybackConfig`.
55
+ * @return The generated `PlaybackConfig` if successful, `null` otherwise.
56
+ */
57
+ @JvmStatic
58
+ fun toPlaybackConfig(json: ReadableMap?): PlaybackConfig? {
59
+ if (json == null) {
60
+ return null
61
+ }
62
+ val playbackConfig = PlaybackConfig()
63
+ if (json.hasKey("isAutoplayEnabled")) {
64
+ playbackConfig.isAutoplayEnabled = json.getBoolean("isAutoplayEnabled")
65
+ }
66
+ if (json.hasKey("isMuted")) {
67
+ playbackConfig.isMuted = json.getBoolean("isMuted")
68
+ }
69
+ if (json.hasKey("isTimeShiftEnabled")) {
70
+ playbackConfig.isTimeShiftEnabled = json.getBoolean("isTimeShiftEnabled")
71
+ }
72
+ return playbackConfig
73
+ }
74
+
75
+ /**
76
+ * Converts any JS object into a `TweaksConfig` object.
77
+ * @param json JS object representing the `TweaksConfig`.
78
+ * @return The generated `TweaksConfig` if successful, `null` otherwise.
79
+ */
80
+ @JvmStatic
81
+ fun toTweaksConfig(json: ReadableMap?): TweaksConfig? {
82
+ if (json == null) {
83
+ return null
84
+ }
85
+ val tweaksConfig = TweaksConfig()
86
+ if (json.hasKey("timeChangedInterval")) {
87
+ tweaksConfig.timeChangedInterval = json.getDouble("timeChangedInterval")
88
+ }
89
+ if (json.hasKey("bandwidthEstimateWeightLimit")) {
90
+ tweaksConfig.bandwidthEstimateWeightLimit = json.getInt("bandwidthEstimateWeightLimit")
91
+ }
92
+ if (json.hasKey("devicesThatRequireSurfaceWorkaround")) {
93
+ val devices = json.getMap("devicesThatRequireSurfaceWorkaround")
94
+ val deviceNames = devices?.getArray("deviceNames")
95
+ ?.toList<String>()
96
+ ?.mapNotNull { it }
97
+ ?.map { DeviceName(it) }
98
+ ?: emptyList()
99
+ val modelNames = devices?.getArray("modelNames")
100
+ ?.toList<String>()
101
+ ?.mapNotNull { it }
102
+ ?.map { ModelName(it) }
103
+ ?: emptyList()
104
+ tweaksConfig.devicesThatRequireSurfaceWorkaround = deviceNames + modelNames
105
+ }
106
+ if (json.hasKey("languagePropertyNormalization")) {
107
+ tweaksConfig.languagePropertyNormalization = json.getBoolean("languagePropertyNormalization")
108
+ }
109
+ if (json.hasKey("localDynamicDashWindowUpdateInterval")) {
110
+ tweaksConfig.localDynamicDashWindowUpdateInterval = json.getDouble("localDynamicDashWindowUpdateInterval")
111
+ }
112
+ if (json.hasKey("shouldApplyTtmlRegionWorkaround")) {
113
+ tweaksConfig.shouldApplyTtmlRegionWorkaround = json.getBoolean("shouldApplyTtmlRegionWorkaround")
114
+ }
115
+ if (json.hasKey("useDrmSessionForClearPeriods")) {
116
+ tweaksConfig.useDrmSessionForClearPeriods = json.getBoolean("useDrmSessionForClearPeriods")
117
+ }
118
+ if (json.hasKey("useDrmSessionForClearSources")) {
119
+ tweaksConfig.useDrmSessionForClearSources = json.getBoolean("useDrmSessionForClearSources")
120
+ }
121
+ if (json.hasKey("useFiletypeExtractorFallbackForHls")) {
122
+ tweaksConfig.useFiletypeExtractorFallbackForHls = json.getBoolean("useFiletypeExtractorFallbackForHls")
30
123
  }
31
- return PlayerConfig()
124
+ return tweaksConfig
32
125
  }
33
126
 
34
127
  /**
@@ -0,0 +1,18 @@
1
+ package com.bitmovin.player.reactnative.extensions
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import com.facebook.react.bridge.ReadableMap
5
+
6
+ inline fun <reified T> ReadableArray.toList(): List<T?> = (0 until size()).map { i ->
7
+ getDynamic(i).let {
8
+ when (T::class) {
9
+ Boolean::class -> it.asBoolean() as T
10
+ String::class -> it.asString() as T
11
+ Double::class -> it.asDouble() as T
12
+ Int::class -> it.asInt() as T
13
+ ReadableArray::class -> it.asArray() as T
14
+ ReadableMap::class -> it.asMap() as T
15
+ else -> null
16
+ }
17
+ }
18
+ }
@@ -15,9 +15,88 @@ extension RCTConvert {
15
15
  if let licenseKey = json["licenseKey"] as? String {
16
16
  playerConfig.key = licenseKey
17
17
  }
18
+ if let playbackConfig = RCTConvert.playbackConfig(json["playbackConfig"]) {
19
+ playerConfig.playbackConfig = playbackConfig
20
+ }
21
+ if let tweaksConfig = RCTConvert.tweaksConfig(json["tweaksConfig"]) {
22
+ playerConfig.tweaksConfig = tweaksConfig
23
+ }
18
24
  return playerConfig
19
25
  }
20
26
 
27
+ /**
28
+ Utility method to instantiate a `PlaybackConfig` from a JS object.
29
+ - Parameter json: JS object.
30
+ - Returns: The produced `PlaybackConfig` object.
31
+ */
32
+ static func playbackConfig(_ json: Any?) -> PlaybackConfig? {
33
+ guard let json = json as? [String: Any?] else {
34
+ return nil
35
+ }
36
+ let playbackConfig = PlaybackConfig()
37
+ if let isAutoplayEnabled = json["isAutoplayEnabled"] as? Bool {
38
+ playbackConfig.isAutoplayEnabled = isAutoplayEnabled
39
+ }
40
+ if let isMuted = json["isMuted"] as? Bool {
41
+ playbackConfig.isMuted = isMuted
42
+ }
43
+ if let isTimeShiftEnabled = json["isTimeShiftEnabled"] as? Bool {
44
+ playbackConfig.isTimeShiftEnabled = isTimeShiftEnabled
45
+ }
46
+ if let isBackgroundPlaybackEnabled = json["isBackgroundPlaybackEnabled"] as? Bool {
47
+ playbackConfig.isBackgroundPlaybackEnabled = isBackgroundPlaybackEnabled
48
+ }
49
+ if let isPictureInPictureEnabled = json["isPictureInPictureEnabled"] as? Bool {
50
+ playbackConfig.isPictureInPictureEnabled = isPictureInPictureEnabled
51
+ }
52
+ return playbackConfig
53
+ }
54
+
55
+ /**
56
+ Utility method to instantiate a `TweaksConfig` from a JS object.
57
+ - Parameter json: JS object.
58
+ - Returns: The produced `TweaksConfig` object.
59
+ */
60
+ static func tweaksConfig(_ json: Any?) -> TweaksConfig? {
61
+ guard let json = json as? [String: Any?] else {
62
+ return nil
63
+ }
64
+ let tweaksConfig = TweaksConfig()
65
+ if let isNativeHlsParsingEnabled = json["isNativeHlsParsingEnabled"] as? Bool {
66
+ tweaksConfig.isNativeHlsParsingEnabled = isNativeHlsParsingEnabled
67
+ }
68
+ if let isCustomHlsLoadingEnabled = json["isCustomHlsLoadingEnabled"] as? Bool {
69
+ tweaksConfig.isCustomHlsLoadingEnabled = isCustomHlsLoadingEnabled
70
+ }
71
+ if let timeChangedInterval = json["timeChangedInterval"] as? NSNumber {
72
+ tweaksConfig.timeChangedInterval = timeChangedInterval.doubleValue
73
+ }
74
+ if let seekToEndThreshold = json["seekToEndThreshold"] as? NSNumber {
75
+ tweaksConfig.seekToEndThreshold = seekToEndThreshold.doubleValue
76
+ }
77
+ if let playbackStartBehaviour = json["playbackStartBehaviour"] as? String {
78
+ switch playbackStartBehaviour {
79
+ case "relaxed":
80
+ tweaksConfig.playbackStartBehaviour = .relaxed
81
+ case "aggressive":
82
+ tweaksConfig.playbackStartBehaviour = .aggressive
83
+ default:
84
+ break
85
+ }
86
+ }
87
+ if let unstallingBehaviour = json["unstallingBehaviour"] as? String {
88
+ switch unstallingBehaviour {
89
+ case "relaxed":
90
+ tweaksConfig.unstallingBehaviour = .relaxed
91
+ case "aggressive":
92
+ tweaksConfig.unstallingBehaviour = .aggressive
93
+ default:
94
+ break
95
+ }
96
+ }
97
+ return tweaksConfig
98
+ }
99
+
21
100
  /**
22
101
  Utility method to instantiate a `SourceConfig` from a JS object.
23
102
  - Parameter json: JS object
package/lib/index.d.ts CHANGED
@@ -570,7 +570,7 @@ declare class Drm extends NativeInstance<DrmConfig> {
570
570
  * @param message - Base64 encoded message data.
571
571
  * @param assetId - Optional asset ID. Only sent by iOS.
572
572
  */
573
- onPrepareMessage: (message: string, assetId?: string | undefined) => void;
573
+ onPrepareMessage: (message: string, assetId?: string) => void;
574
574
  /**
575
575
  * iOS only.
576
576
  *
@@ -740,6 +740,160 @@ declare class Source extends NativeInstance<SourceConfig> {
740
740
  loadingState: () => Promise<LoadingState>;
741
741
  }
742
742
 
743
+ /**
744
+ * This configuration is used as an incubator for experimental features. Tweaks are not officially
745
+ * supported and are not guaranteed to be stable, i.e. their naming, functionality and API can
746
+ * change at any time within the tweaks or when being promoted to an official feature and moved
747
+ * into its final configuration namespace.
748
+ */
749
+ interface TweaksConfig {
750
+ /**
751
+ * The frequency in seconds onTimeChanged is called with TimeChangedEvents.
752
+ *
753
+ * Default value in iOS is `1.0`.
754
+ * Default value in Android is `0.2`.
755
+ *
756
+ * @platform iOS, Android
757
+ */
758
+ timeChangedInterval?: number;
759
+ /**
760
+ * If enabled, HLS playlists will be parsed and additional features and events are enabled. This includes:
761
+ *
762
+ * - MetadataEvents carrying segment-specific metadata for custom HLS tags, like #EXT-X-SCTE35
763
+ * - MetadataParsedEvents carrying segment-specific metadata for custom HLS tags, like #EXT-X-SCTE35
764
+ * - DrmDataParsedEvents when a #EXT-X-KEY is found
765
+ * - Player.availableVideoQualities includes additional information
766
+ * - Automatic retries when HLS playlist requests failed with non-2xx HTTP status code
767
+ *
768
+ * Default is false.
769
+ *
770
+ * @platform iOS
771
+ */
772
+ isNativeHlsParsingEnabled?: boolean;
773
+ /**
774
+ * If enabled, playlists will be downloaded by the Bitmovin Player SDK instead of AVFoundation.
775
+ * This enables additional features and events, like:
776
+ *
777
+ * - DownloadFinishedEvents for playlist downloads.
778
+ * - SourceWarningEvents when no #EXT-X-PLAYLIST-TYPE is found If set to false, enabling
779
+ * nativeHlsParsingEnabled won’t have any effect.
780
+ *
781
+ * Default is true.
782
+ *
783
+ * @platform iOS
784
+ */
785
+ isCustomHlsLoadingEnabled?: boolean;
786
+ /**
787
+ * The threshold which will be applied when seeking to the end in seconds. This value will be used
788
+ * to calculate the maximum seekable time when calling player.seek(time:) or player.playlist.seek(source:time:),
789
+ * so the maximum value will be duration - seekToEndThreshold.
790
+ *
791
+ * This is useful if the duration of the segments does not match the duration specified in the
792
+ * manifest. In this case, if we try to seek to the end, AVPlayer could get stuck and might stall
793
+ * forever Therefore increasing this value could help.
794
+ *
795
+ * Default is 0.5.
796
+ *
797
+ * @platform iOS
798
+ */
799
+ seekToEndThreshold?: number;
800
+ /**
801
+ * Specifies the player behaviour when Player.play is called. Default is 'relaxed'.
802
+ *
803
+ * - '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.
804
+ * - '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.
805
+ *
806
+ * @platform iOS
807
+ */
808
+ playbackStartBehaviour?: 'relaxed' | 'aggressive';
809
+ /**
810
+ * Specifies the player behaviour when stalling should be exited. Default is 'relaxed'.
811
+ *
812
+ * - '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.
813
+ * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately.
814
+ *
815
+ * @platform iOS
816
+ */
817
+ unstallingBehaviour?: 'relaxed' | 'aggressive';
818
+ /**
819
+ * 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.
820
+ * Default is 2000.
821
+ *
822
+ * @platform Android
823
+ */
824
+ bandwidthEstimateWeightLimit?: number;
825
+ /**
826
+ * 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.
827
+ *
828
+ * @platform Android
829
+ */
830
+ devicesThatRequireSurfaceWorkaround?: {
831
+ /**
832
+ * A device name as reported by Build.DEVICE.
833
+ *
834
+ * @see Build.DEVICE: https://developer.android.com/reference/kotlin/android/os/Build.html#DEVICE--
835
+ */
836
+ deviceNames?: string[];
837
+ /**
838
+ * A model name as reported by Build.MODEL.
839
+ *
840
+ * @see Build.MODEL: https://developer.android.com/reference/kotlin/android/os/Build.html#MODEL--
841
+ */
842
+ modelNames?: string[];
843
+ };
844
+ /**
845
+ * Specifies if the language property on DASH Representations, HLS Renditions and SmoothStreaming QualityLevels is normalized.
846
+ * If enabled, language properties are normalized to IETF BCP 47 language tags. Default is true.
847
+ *
848
+ * Examples:
849
+ * - "ENG" is normalized to "en"
850
+ * - "en_us" is normalized to "en-us"
851
+ * - "en-US-x-lvariant-POSIX" is normalized to "en-us-posix"
852
+ *
853
+ * @platform Android
854
+ */
855
+ languagePropertyNormalization?: boolean;
856
+ /**
857
+ * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the
858
+ * playback window is moved forward on the timeline.
859
+ *
860
+ * @platform Android
861
+ */
862
+ localDynamicDashWindowUpdateInterval?: number;
863
+ /**
864
+ * Specifies whether default positioning values should be assumed when parsing TTML regions in case of
865
+ * unsupported TTML features. Default is true
866
+ *
867
+ * @platform Android
868
+ */
869
+ shouldApplyTtmlRegionWorkaround?: boolean;
870
+ /**
871
+ * Specifies whether a DRM session should be used for clear tracks of type video and audio. Using
872
+ * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear
873
+ * and encrypted sections of content. Default is false.
874
+ *
875
+ * @platform Android
876
+ */
877
+ useDrmSessionForClearPeriods?: boolean;
878
+ /**
879
+ * Specifies whether a DRM session should be used for clear tracks of type video and audio in a clear
880
+ * source that follows after a DRM protected source. In addition, a DRM session will be used for clear
881
+ * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of
882
+ * decoders when transitioning between clear and encrypted sections of content. Default is false.
883
+ *
884
+ * @platform Android
885
+ */
886
+ useDrmSessionForClearSources?: boolean;
887
+ /**
888
+ * Specifies if the player should always fall back to an extractor matching the file type, if no
889
+ * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities
890
+ * with streams and thus can result in unstable or failing playback.
891
+ *
892
+ * @platform Android
893
+ */
894
+ useFiletypeExtractorFallbackForHls?: boolean;
895
+ }
896
+
743
897
  /**
744
898
  * Object used to configure a new `Player` instance.
745
899
  */
@@ -763,6 +917,90 @@ interface PlayerConfig extends NativeInstanceConfig {
763
917
  * ```
764
918
  */
765
919
  licenseKey?: string;
920
+ /**
921
+ * Configures playback behaviour. A default PlaybackConfig is set initially.
922
+ */
923
+ playbackConfig?: PlaybackConfig;
924
+ /**
925
+ * Configures experimental features. A default TweaksConfig is set initially.
926
+ */
927
+ tweaksConfig?: TweaksConfig;
928
+ }
929
+ /**
930
+ * Configures the playback behaviour of the player.
931
+ */
932
+ interface PlaybackConfig {
933
+ /**
934
+ * Whether the player starts playing automatically after loading a source or not. Default is `false`.
935
+ * @example
936
+ * ```
937
+ * const player = new Player({
938
+ * playbackConfig: {
939
+ * isAutoplayEnabled: true,
940
+ * },
941
+ * });
942
+ * ```
943
+ */
944
+ isAutoplayEnabled?: boolean;
945
+ /**
946
+ * Whether the sound is muted on startup or not. Default value is `false`.
947
+ * @example
948
+ * ```
949
+ * const player = new Player({
950
+ * playbackConfig: {
951
+ * isMuted: true,
952
+ * },
953
+ * });
954
+ * ```
955
+ */
956
+ isMuted?: boolean;
957
+ /**
958
+ * Whether time shift / DVR for live streams is enabled or not. Default is `true`.
959
+ * @example
960
+ * ```
961
+ * const player = new Player({
962
+ * playbackConfig: {
963
+ * isTimeShiftEnabled: false,
964
+ * },
965
+ * });
966
+ * ```
967
+ */
968
+ isTimeShiftEnabled?: boolean;
969
+ /**
970
+ * Whether background playback is enabled or not.
971
+ * Default is `false`.
972
+ *
973
+ * When set to `true`, playback is not automatically paused
974
+ * anymore when the app moves to the background.
975
+ * When set to `true`, also make sure to properly configure your app to allow
976
+ * background playback.
977
+ *
978
+ * On tvOS, background playback is only supported for audio-only content.
979
+ *
980
+ * Default is `false`.
981
+ *
982
+ * @example
983
+ * ```
984
+ * const player = new Player({
985
+ * {
986
+ * isBackgroundPlaybackEnabled: true,
987
+ * }
988
+ * })
989
+ * ```
990
+ */
991
+ isBackgroundPlaybackEnabled?: boolean;
992
+ /**
993
+ * Whether the picture-in-picture mode option is enabled for iOS or not. Default is `false`.
994
+ * @example
995
+ * ```
996
+ * const player = new Player({
997
+ * playbackConfig: {
998
+ * isPictureInPictureEnabled: true,
999
+ * },
1000
+ * });
1001
+ * ```
1002
+ */
1003
+ isPictureInPictureEnabled?: boolean;
766
1004
  }
767
1005
  /**
768
1006
  * Loads, controls and renders audio and video content represented through `Source`s. A player
@@ -850,7 +1088,7 @@ declare class Player extends NativeInstance<PlayerConfig> {
850
1088
  *
851
1089
  * @param mode - The time mode to specify: an absolute UNIX timestamp ('absolute') or relative time ('relative').
852
1090
  */
853
- getCurrentTime: (mode?: "relative" | "absolute" | undefined) => Promise<number>;
1091
+ getCurrentTime: (mode?: 'relative' | 'absolute') => Promise<number>;
854
1092
  /**
855
1093
  * @returns The total duration in seconds of the current video or INFINITY if it’s a live stream.
856
1094
  */
@@ -918,4 +1156,4 @@ declare function PlayerView(props: PlayerViewProps): JSX.Element;
918
1156
  */
919
1157
  declare function usePlayer(config?: PlayerConfig): Player;
920
1158
 
921
- export { BasePlayerViewProps, DestroyEvent, Drm, DrmConfig, ErrorEvent, Event, EventSource, FairplayConfig, LoadingState, MutedEvent, PausedEvent, PlayEvent, PlaybackFinishedEvent, Player, PlayerActiveEvent, PlayerConfig, PlayerErrorEvent, PlayerView, PlayerViewProps, PlayerWarningEvent, PlayingEvent, ReadyEvent, SeekEvent, SeekedEvent, SideLoadedSubtitleTrack, Source, SourceConfig, SourceErrorEvent, SourceLoadEvent, SourceLoadedEvent, SourceType, SourceUnloadedEvent, SourceWarningEvent, SubtitleAddedEvent, SubtitleChangedEvent, SubtitleFormat, SubtitleRemovedEvent, SubtitleTrack, TimeChangedEvent, UnmutedEvent, WidevineConfig, usePlayer };
1159
+ export { BasePlayerViewProps, DestroyEvent, Drm, DrmConfig, ErrorEvent, Event, EventSource, FairplayConfig, LoadingState, MutedEvent, PausedEvent, PlayEvent, PlaybackConfig, PlaybackFinishedEvent, Player, PlayerActiveEvent, PlayerConfig, PlayerErrorEvent, PlayerView, PlayerViewProps, PlayerWarningEvent, PlayingEvent, ReadyEvent, SeekEvent, SeekedEvent, SideLoadedSubtitleTrack, Source, SourceConfig, SourceErrorEvent, SourceLoadEvent, SourceLoadedEvent, SourceType, SourceUnloadedEvent, SourceWarningEvent, SubtitleAddedEvent, SubtitleChangedEvent, SubtitleFormat, SubtitleRemovedEvent, SubtitleTrack, TimeChangedEvent, UnmutedEvent, WidevineConfig, usePlayer };
package/lib/index.js CHANGED
@@ -18,7 +18,10 @@ var __copyProps = (to, from, except, desc) => {
18
18
  }
19
19
  return to;
20
20
  };
21
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
22
25
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
23
26
  var __publicField = (obj, key, value) => {
24
27
  __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
@@ -55,12 +58,15 @@ function unwrapNativeEvent(event) {
55
58
  return (0, import_lodash.default)(event.nativeEvent, ["target"]);
56
59
  }
57
60
  function useProxy(viewRef) {
58
- return (0, import_react.useCallback)((callback) => (event) => {
59
- const node = event.target._nativeTag;
60
- if (node === (0, import_react_native2.findNodeHandle)(viewRef.current)) {
61
- callback?.(unwrapNativeEvent(event));
62
- }
63
- }, [viewRef]);
61
+ return (0, import_react.useCallback)(
62
+ (callback) => (event) => {
63
+ const node = event.target._nativeTag;
64
+ if (node === (0, import_react_native2.findNodeHandle)(viewRef.current)) {
65
+ callback?.(unwrapNativeEvent(event));
66
+ }
67
+ },
68
+ [viewRef]
69
+ );
64
70
  }
65
71
 
66
72
  // src/components/PlayerView/index.tsx
@@ -71,10 +77,14 @@ var styles = import_react_native3.StyleSheet.create({
71
77
  });
72
78
  function dispatch(command, node, playerId) {
73
79
  const commandId = import_react_native3.Platform.OS === "android" ? import_react_native3.UIManager.NativePlayerView.Commands[command].toString() : import_react_native3.UIManager.getViewManagerConfig("NativePlayerView").Commands[command];
74
- import_react_native3.UIManager.dispatchViewManagerCommand(node, commandId, import_react_native3.Platform.select({
75
- ios: [playerId],
76
- android: [node, playerId]
77
- }));
80
+ import_react_native3.UIManager.dispatchViewManagerCommand(
81
+ node,
82
+ commandId,
83
+ import_react_native3.Platform.select({
84
+ ios: [playerId],
85
+ android: [node, playerId]
86
+ })
87
+ );
78
88
  }
79
89
  function PlayerView(props) {
80
90
  const nativeView = (0, import_react2.useRef)(null);
@@ -152,18 +162,27 @@ var Drm = class extends NativeInstance {
152
162
  });
153
163
  __publicField(this, "onPrepareCertificate", (certificate) => {
154
164
  if (this.config?.fairplay?.prepareCertificate) {
155
- DrmModule.setPreparedCertificate(this.nativeId, this.config?.fairplay?.prepareCertificate?.(certificate));
165
+ DrmModule.setPreparedCertificate(
166
+ this.nativeId,
167
+ this.config?.fairplay?.prepareCertificate?.(certificate)
168
+ );
156
169
  }
157
170
  });
158
171
  __publicField(this, "onPrepareMessage", (message, assetId) => {
159
172
  const config = import_react_native5.Platform.OS === "ios" ? this.config?.fairplay : this.config?.widevine;
160
173
  if (config && config.prepareMessage) {
161
- DrmModule.setPreparedMessage(this.nativeId, import_react_native5.Platform.OS === "ios" ? config.prepareMessage?.(message, assetId) : config.prepareMessage?.(message));
174
+ DrmModule.setPreparedMessage(
175
+ this.nativeId,
176
+ import_react_native5.Platform.OS === "ios" ? config.prepareMessage?.(message, assetId) : config.prepareMessage?.(message)
177
+ );
162
178
  }
163
179
  });
164
180
  __publicField(this, "onPrepareSyncMessage", (syncMessage, assetId) => {
165
181
  if (this.config?.fairplay?.prepareSyncMessage) {
166
- DrmModule.setPreparedSyncMessage(this.nativeId, this.config?.fairplay?.prepareSyncMessage?.(syncMessage, assetId));
182
+ DrmModule.setPreparedSyncMessage(
183
+ this.nativeId,
184
+ this.config?.fairplay?.prepareSyncMessage?.(syncMessage, assetId)
185
+ );
167
186
  }
168
187
  });
169
188
  __publicField(this, "onPrepareLicense", (license) => {
@@ -174,12 +193,18 @@ var Drm = class extends NativeInstance {
174
193
  });
175
194
  __publicField(this, "onPrepareLicenseServerUrl", (licenseServerUrl) => {
176
195
  if (this.config?.fairplay?.prepareLicenseServerUrl) {
177
- DrmModule.setPreparedLicenseServerUrl(this.nativeId, this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl));
196
+ DrmModule.setPreparedLicenseServerUrl(
197
+ this.nativeId,
198
+ this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl)
199
+ );
178
200
  }
179
201
  });
180
202
  __publicField(this, "onPrepareContentId", (contentId) => {
181
203
  if (this.config?.fairplay?.prepareContentId) {
182
- DrmModule.setPreparedContentId(this.nativeId, this.config?.fairplay?.prepareContentId?.(contentId));
204
+ DrmModule.setPreparedContentId(
205
+ this.nativeId,
206
+ this.config?.fairplay?.prepareContentId?.(contentId)
207
+ );
183
208
  }
184
209
  });
185
210
  }
@@ -218,7 +243,11 @@ var Source = class extends NativeInstance {
218
243
  if (this.config?.drmConfig) {
219
244
  this.drm = new Drm(this.config.drmConfig);
220
245
  this.drm.initialize();
221
- SourceModule.initWithDrmConfig(this.nativeId, this.drm.nativeId, this.config);
246
+ SourceModule.initWithDrmConfig(
247
+ this.nativeId,
248
+ this.drm.nativeId,
249
+ this.config
250
+ );
222
251
  } else {
223
252
  SourceModule.initWithConfig(this.nativeId, this.config);
224
253
  }
@@ -326,14 +355,18 @@ var Player = class extends NativeInstance {
326
355
  });
327
356
  __publicField(this, "isAirPlayActive", async () => {
328
357
  if (import_react_native7.Platform.OS === "android") {
329
- console.warn(`[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`);
358
+ console.warn(
359
+ `[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`
360
+ );
330
361
  return false;
331
362
  }
332
363
  return PlayerModule.isAirPlayActive(this.nativeId);
333
364
  });
334
365
  __publicField(this, "isAirPlayAvailable", async () => {
335
366
  if (import_react_native7.Platform.OS === "android") {
336
- console.warn(`[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`);
367
+ console.warn(
368
+ `[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`
369
+ );
337
370
  return false;
338
371
  }
339
372
  return PlayerModule.isAirPlayAvailable(this.nativeId);
package/lib/index.mjs CHANGED
@@ -26,12 +26,15 @@ function unwrapNativeEvent(event) {
26
26
  return omit(event.nativeEvent, ["target"]);
27
27
  }
28
28
  function useProxy(viewRef) {
29
- return useCallback((callback) => (event) => {
30
- const node = event.target._nativeTag;
31
- if (node === findNodeHandle(viewRef.current)) {
32
- callback?.(unwrapNativeEvent(event));
33
- }
34
- }, [viewRef]);
29
+ return useCallback(
30
+ (callback) => (event) => {
31
+ const node = event.target._nativeTag;
32
+ if (node === findNodeHandle(viewRef.current)) {
33
+ callback?.(unwrapNativeEvent(event));
34
+ }
35
+ },
36
+ [viewRef]
37
+ );
35
38
  }
36
39
 
37
40
  // src/components/PlayerView/index.tsx
@@ -42,10 +45,14 @@ var styles = StyleSheet.create({
42
45
  });
43
46
  function dispatch(command, node, playerId) {
44
47
  const commandId = Platform.OS === "android" ? UIManager.NativePlayerView.Commands[command].toString() : UIManager.getViewManagerConfig("NativePlayerView").Commands[command];
45
- UIManager.dispatchViewManagerCommand(node, commandId, Platform.select({
46
- ios: [playerId],
47
- android: [node, playerId]
48
- }));
48
+ UIManager.dispatchViewManagerCommand(
49
+ node,
50
+ commandId,
51
+ Platform.select({
52
+ ios: [playerId],
53
+ android: [node, playerId]
54
+ })
55
+ );
49
56
  }
50
57
  function PlayerView(props) {
51
58
  const nativeView = useRef(null);
@@ -123,18 +130,27 @@ var Drm = class extends NativeInstance {
123
130
  });
124
131
  __publicField(this, "onPrepareCertificate", (certificate) => {
125
132
  if (this.config?.fairplay?.prepareCertificate) {
126
- DrmModule.setPreparedCertificate(this.nativeId, this.config?.fairplay?.prepareCertificate?.(certificate));
133
+ DrmModule.setPreparedCertificate(
134
+ this.nativeId,
135
+ this.config?.fairplay?.prepareCertificate?.(certificate)
136
+ );
127
137
  }
128
138
  });
129
139
  __publicField(this, "onPrepareMessage", (message, assetId) => {
130
140
  const config = Platform2.OS === "ios" ? this.config?.fairplay : this.config?.widevine;
131
141
  if (config && config.prepareMessage) {
132
- DrmModule.setPreparedMessage(this.nativeId, Platform2.OS === "ios" ? config.prepareMessage?.(message, assetId) : config.prepareMessage?.(message));
142
+ DrmModule.setPreparedMessage(
143
+ this.nativeId,
144
+ Platform2.OS === "ios" ? config.prepareMessage?.(message, assetId) : config.prepareMessage?.(message)
145
+ );
133
146
  }
134
147
  });
135
148
  __publicField(this, "onPrepareSyncMessage", (syncMessage, assetId) => {
136
149
  if (this.config?.fairplay?.prepareSyncMessage) {
137
- DrmModule.setPreparedSyncMessage(this.nativeId, this.config?.fairplay?.prepareSyncMessage?.(syncMessage, assetId));
150
+ DrmModule.setPreparedSyncMessage(
151
+ this.nativeId,
152
+ this.config?.fairplay?.prepareSyncMessage?.(syncMessage, assetId)
153
+ );
138
154
  }
139
155
  });
140
156
  __publicField(this, "onPrepareLicense", (license) => {
@@ -145,12 +161,18 @@ var Drm = class extends NativeInstance {
145
161
  });
146
162
  __publicField(this, "onPrepareLicenseServerUrl", (licenseServerUrl) => {
147
163
  if (this.config?.fairplay?.prepareLicenseServerUrl) {
148
- DrmModule.setPreparedLicenseServerUrl(this.nativeId, this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl));
164
+ DrmModule.setPreparedLicenseServerUrl(
165
+ this.nativeId,
166
+ this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl)
167
+ );
149
168
  }
150
169
  });
151
170
  __publicField(this, "onPrepareContentId", (contentId) => {
152
171
  if (this.config?.fairplay?.prepareContentId) {
153
- DrmModule.setPreparedContentId(this.nativeId, this.config?.fairplay?.prepareContentId?.(contentId));
172
+ DrmModule.setPreparedContentId(
173
+ this.nativeId,
174
+ this.config?.fairplay?.prepareContentId?.(contentId)
175
+ );
154
176
  }
155
177
  });
156
178
  }
@@ -189,7 +211,11 @@ var Source = class extends NativeInstance {
189
211
  if (this.config?.drmConfig) {
190
212
  this.drm = new Drm(this.config.drmConfig);
191
213
  this.drm.initialize();
192
- SourceModule.initWithDrmConfig(this.nativeId, this.drm.nativeId, this.config);
214
+ SourceModule.initWithDrmConfig(
215
+ this.nativeId,
216
+ this.drm.nativeId,
217
+ this.config
218
+ );
193
219
  } else {
194
220
  SourceModule.initWithConfig(this.nativeId, this.config);
195
221
  }
@@ -297,14 +323,18 @@ var Player = class extends NativeInstance {
297
323
  });
298
324
  __publicField(this, "isAirPlayActive", async () => {
299
325
  if (Platform3.OS === "android") {
300
- console.warn(`[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`);
326
+ console.warn(
327
+ `[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`
328
+ );
301
329
  return false;
302
330
  }
303
331
  return PlayerModule.isAirPlayActive(this.nativeId);
304
332
  });
305
333
  __publicField(this, "isAirPlayAvailable", async () => {
306
334
  if (Platform3.OS === "android") {
307
- console.warn(`[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`);
335
+ console.warn(
336
+ `[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`
337
+ );
308
338
  return false;
309
339
  }
310
340
  return PlayerModule.isAirPlayAvailable(this.nativeId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitmovin-player-react-native",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "Official React Native bindings for Bitmovin's mobile Player SDKs.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.mjs",
@@ -45,24 +45,28 @@
45
45
  "homepage": "https://github.com/bitmovin/bitmovin-player-react-native",
46
46
  "repository": "https://github.com/bitmovin/bitmovin-player-react-native",
47
47
  "devDependencies": {
48
+ "@babel/core": "7.19.3",
49
+ "@babel/runtime": "7.19.0",
48
50
  "@commitlint/config-conventional": "11.0.0",
49
- "@react-native-community/eslint-config": "3.0.2",
51
+ "@react-native-community/eslint-config": "3.1.0",
50
52
  "@types/jest": "26.0.24",
51
53
  "@types/lodash.omit": "4.5.0",
52
54
  "@types/react": "18.0.15",
53
55
  "@types/react-native": "0.69.2",
54
- "commitlint": "11.0.0",
55
- "eslint": "8.18.0",
56
+ "babel-plugin-module-resolver": "4.1.0",
57
+ "commitlint": "17.1.2",
58
+ "eslint": "8.24.0",
56
59
  "eslint-config-prettier": "8.5.0",
57
- "eslint-plugin-prettier": "4.0.0",
60
+ "eslint-plugin-prettier": "4.2.1",
58
61
  "husky": "8.0.1",
59
62
  "jest": "26.6.3",
60
63
  "lint-staged": "13.0.3",
61
- "pod-install": "0.1.37",
64
+ "metro-config": "0.72.3",
65
+ "metro-react-native-babel-preset": "0.72.3",
66
+ "pod-install": "0.1.38",
62
67
  "prettier": "2.7.1",
63
- "react": "17.0.2",
64
- "tsup": "5.12.9",
65
- "typescript": "4.5.5"
68
+ "tsup": "6.2.3",
69
+ "typescript": "4.8.4"
66
70
  },
67
71
  "dependencies": {
68
72
  "lodash.omit": "4.5.0"
package/src/player.ts CHANGED
@@ -2,6 +2,7 @@ import { NativeModules, Platform } from 'react-native';
2
2
  import NativeInstance, { NativeInstanceConfig } from './nativeInstance';
3
3
  import { Source, SourceConfig } from './source';
4
4
  import { SubtitleTrack } from './subtitleTrack';
5
+ import { TweaksConfig } from './tweaksConfig';
5
6
 
6
7
  const PlayerModule = NativeModules.PlayerModule;
7
8
 
@@ -28,6 +29,91 @@ export interface PlayerConfig extends NativeInstanceConfig {
28
29
  * ```
29
30
  */
30
31
  licenseKey?: string;
32
+ /**
33
+ * Configures playback behaviour. A default PlaybackConfig is set initially.
34
+ */
35
+ playbackConfig?: PlaybackConfig;
36
+ /**
37
+ * Configures experimental features. A default TweaksConfig is set initially.
38
+ */
39
+ tweaksConfig?: TweaksConfig;
40
+ }
41
+
42
+ /**
43
+ * Configures the playback behaviour of the player.
44
+ */
45
+ export interface PlaybackConfig {
46
+ /**
47
+ * Whether the player starts playing automatically after loading a source or not. Default is `false`.
48
+ * @example
49
+ * ```
50
+ * const player = new Player({
51
+ * playbackConfig: {
52
+ * isAutoplayEnabled: true,
53
+ * },
54
+ * });
55
+ * ```
56
+ */
57
+ isAutoplayEnabled?: boolean;
58
+ /**
59
+ * Whether the sound is muted on startup or not. Default value is `false`.
60
+ * @example
61
+ * ```
62
+ * const player = new Player({
63
+ * playbackConfig: {
64
+ * isMuted: true,
65
+ * },
66
+ * });
67
+ * ```
68
+ */
69
+ isMuted?: boolean;
70
+ /**
71
+ * Whether time shift / DVR for live streams is enabled or not. Default is `true`.
72
+ * @example
73
+ * ```
74
+ * const player = new Player({
75
+ * playbackConfig: {
76
+ * isTimeShiftEnabled: false,
77
+ * },
78
+ * });
79
+ * ```
80
+ */
81
+ isTimeShiftEnabled?: boolean;
82
+ /**
83
+ * Whether background playback is enabled or not.
84
+ * Default is `false`.
85
+ *
86
+ * When set to `true`, playback is not automatically paused
87
+ * anymore when the app moves to the background.
88
+ * When set to `true`, also make sure to properly configure your app to allow
89
+ * background playback.
90
+ *
91
+ * On tvOS, background playback is only supported for audio-only content.
92
+ *
93
+ * Default is `false`.
94
+ *
95
+ * @example
96
+ * ```
97
+ * const player = new Player({
98
+ * {
99
+ * isBackgroundPlaybackEnabled: true,
100
+ * }
101
+ * })
102
+ * ```
103
+ */
104
+ isBackgroundPlaybackEnabled?: boolean;
105
+ /**
106
+ * Whether the picture-in-picture mode option is enabled for iOS or not. Default is `false`.
107
+ * @example
108
+ * ```
109
+ * const player = new Player({
110
+ * playbackConfig: {
111
+ * isPictureInPictureEnabled: true,
112
+ * },
113
+ * });
114
+ * ```
115
+ */
116
+ isPictureInPictureEnabled?: boolean;
31
117
  }
32
118
 
33
119
  /**
@@ -0,0 +1,153 @@
1
+ /**
2
+ * This configuration is used as an incubator for experimental features. Tweaks are not officially
3
+ * supported and are not guaranteed to be stable, i.e. their naming, functionality and API can
4
+ * change at any time within the tweaks or when being promoted to an official feature and moved
5
+ * into its final configuration namespace.
6
+ */
7
+ export interface TweaksConfig {
8
+ /**
9
+ * The frequency in seconds onTimeChanged is called with TimeChangedEvents.
10
+ *
11
+ * Default value in iOS is `1.0`.
12
+ * Default value in Android is `0.2`.
13
+ *
14
+ * @platform iOS, Android
15
+ */
16
+ timeChangedInterval?: number;
17
+ /**
18
+ * If enabled, HLS playlists will be parsed and additional features and events are enabled. This includes:
19
+ *
20
+ * - MetadataEvents carrying segment-specific metadata for custom HLS tags, like #EXT-X-SCTE35
21
+ * - MetadataParsedEvents carrying segment-specific metadata for custom HLS tags, like #EXT-X-SCTE35
22
+ * - DrmDataParsedEvents when a #EXT-X-KEY is found
23
+ * - Player.availableVideoQualities includes additional information
24
+ * - Automatic retries when HLS playlist requests failed with non-2xx HTTP status code
25
+ *
26
+ * Default is false.
27
+ *
28
+ * @platform iOS
29
+ */
30
+ isNativeHlsParsingEnabled?: boolean;
31
+ /**
32
+ * If enabled, playlists will be downloaded by the Bitmovin Player SDK instead of AVFoundation.
33
+ * This enables additional features and events, like:
34
+ *
35
+ * - DownloadFinishedEvents for playlist downloads.
36
+ * - SourceWarningEvents when no #EXT-X-PLAYLIST-TYPE is found If set to false, enabling
37
+ * nativeHlsParsingEnabled won’t have any effect.
38
+ *
39
+ * Default is true.
40
+ *
41
+ * @platform iOS
42
+ */
43
+ isCustomHlsLoadingEnabled?: boolean;
44
+ /**
45
+ * The threshold which will be applied when seeking to the end in seconds. This value will be used
46
+ * to calculate the maximum seekable time when calling player.seek(time:) or player.playlist.seek(source:time:),
47
+ * so the maximum value will be duration - seekToEndThreshold.
48
+ *
49
+ * This is useful if the duration of the segments does not match the duration specified in the
50
+ * manifest. In this case, if we try to seek to the end, AVPlayer could get stuck and might stall
51
+ * forever Therefore increasing this value could help.
52
+ *
53
+ * Default is 0.5.
54
+ *
55
+ * @platform iOS
56
+ */
57
+ seekToEndThreshold?: number;
58
+ /**
59
+ * Specifies the player behaviour when Player.play is called. Default is 'relaxed'.
60
+ *
61
+ * - '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.
62
+ * - '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.
63
+ *
64
+ * @platform iOS
65
+ */
66
+ playbackStartBehaviour?: 'relaxed' | 'aggressive';
67
+ /**
68
+ * Specifies the player behaviour when stalling should be exited. Default is 'relaxed'.
69
+ *
70
+ * - '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.
71
+ * - 'aggressive': The player will try to unstall as soon as some media data became available and will start playback of this media immediately.
72
+ *
73
+ * @platform iOS
74
+ */
75
+ unstallingBehaviour?: 'relaxed' | 'aggressive';
76
+ /**
77
+ * 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.
78
+ * Default is 2000.
79
+ *
80
+ * @platform Android
81
+ */
82
+ bandwidthEstimateWeightLimit?: number;
83
+ /**
84
+ * 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.
85
+ *
86
+ * @platform Android
87
+ */
88
+ devicesThatRequireSurfaceWorkaround?: {
89
+ /**
90
+ * A device name as reported by Build.DEVICE.
91
+ *
92
+ * @see Build.DEVICE: https://developer.android.com/reference/kotlin/android/os/Build.html#DEVICE--
93
+ */
94
+ deviceNames?: string[];
95
+ /**
96
+ * A model name as reported by Build.MODEL.
97
+ *
98
+ * @see Build.MODEL: https://developer.android.com/reference/kotlin/android/os/Build.html#MODEL--
99
+ */
100
+ modelNames?: string[];
101
+ };
102
+ /**
103
+ * Specifies if the language property on DASH Representations, HLS Renditions and SmoothStreaming QualityLevels is normalized.
104
+ * If enabled, language properties are normalized to IETF BCP 47 language tags. Default is true.
105
+ *
106
+ * Examples:
107
+ * - "ENG" is normalized to "en"
108
+ * - "en_us" is normalized to "en-us"
109
+ * - "en-US-x-lvariant-POSIX" is normalized to "en-us-posix"
110
+ *
111
+ * @platform Android
112
+ */
113
+ languagePropertyNormalization?: boolean;
114
+ /**
115
+ * The interval in which dynamic DASH windows are updated locally. I.e. The rate by which the
116
+ * playback window is moved forward on the timeline.
117
+ *
118
+ * @platform Android
119
+ */
120
+ localDynamicDashWindowUpdateInterval?: number;
121
+ /**
122
+ * Specifies whether default positioning values should be assumed when parsing TTML regions in case of
123
+ * unsupported TTML features. Default is true
124
+ *
125
+ * @platform Android
126
+ */
127
+ shouldApplyTtmlRegionWorkaround?: boolean;
128
+ /**
129
+ * Specifies whether a DRM session should be used for clear tracks of type video and audio. Using
130
+ * DRM sessions for clear content avoids the recreation of decoders when transitioning between clear
131
+ * and encrypted sections of content. Default is false.
132
+ *
133
+ * @platform Android
134
+ */
135
+ useDrmSessionForClearPeriods?: boolean;
136
+ /**
137
+ * Specifies whether a DRM session should be used for clear tracks of type video and audio in a clear
138
+ * source that follows after a DRM protected source. In addition, a DRM session will be used for clear
139
+ * periods in a DRM protected source. Using DRM sessions for clear content avoids the recreation of
140
+ * decoders when transitioning between clear and encrypted sections of content. Default is false.
141
+ *
142
+ * @platform Android
143
+ */
144
+ useDrmSessionForClearSources?: boolean;
145
+ /**
146
+ * Specifies if the player should always fall back to an extractor matching the file type, if no
147
+ * matching extractor was found. If the fallback is applied, this will ignore potential incompatibilities
148
+ * with streams and thus can result in unstable or failing playback.
149
+ *
150
+ * @platform Android
151
+ */
152
+ useFiletypeExtractorFallbackForHls?: boolean;
153
+ }