bitmovin-player-react-native 0.1.0 → 0.2.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.
Files changed (44) hide show
  1. package/README.md +163 -1
  2. package/RNBitmovinPlayer.podspec +3 -3
  3. package/android/src/main/java/com/bitmovin/player/reactnative/DrmModule.kt +191 -0
  4. package/android/src/main/java/com/bitmovin/player/reactnative/PlayerModule.kt +116 -101
  5. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerView.kt +69 -38
  6. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewManager.kt +10 -26
  7. package/android/src/main/java/com/bitmovin/player/reactnative/RNPlayerViewPackage.kt +3 -0
  8. package/android/src/main/java/com/bitmovin/player/reactnative/Registry.kt +11 -0
  9. package/android/src/main/java/com/bitmovin/player/reactnative/SourceModule.kt +178 -0
  10. package/android/src/main/java/com/bitmovin/player/reactnative/UuidModule.kt +20 -0
  11. package/android/src/main/java/com/bitmovin/player/reactnative/converter/JsonConverter.kt +128 -1
  12. package/android/src/main/java/com/bitmovin/player/reactnative/extensions/Events.kt +11 -2
  13. package/ios/DrmModule.m +16 -0
  14. package/ios/DrmModule.swift +442 -0
  15. package/ios/Event+JSON.swift +31 -0
  16. package/ios/PlayerModule.m +21 -25
  17. package/ios/PlayerModule.swift +122 -120
  18. package/ios/RCTConvert+BitmovinPlayer.swift +131 -12
  19. package/ios/RNPlayerView+PlayerListener.swift +12 -0
  20. package/ios/RNPlayerView.swift +5 -3
  21. package/ios/RNPlayerViewManager.m +3 -1
  22. package/ios/RNPlayerViewManager.swift +4 -21
  23. package/ios/Registry.swift +5 -0
  24. package/ios/SourceModule.m +30 -0
  25. package/ios/SourceModule.swift +187 -0
  26. package/ios/UuidModule.m +9 -0
  27. package/ios/UuidModule.swift +23 -0
  28. package/lib/index.d.ts +670 -235
  29. package/lib/index.js +257 -106
  30. package/lib/index.mjs +260 -112
  31. package/package.json +5 -4
  32. package/src/components/PlayerView/events.ts +24 -18
  33. package/src/components/PlayerView/index.tsx +29 -26
  34. package/src/drm/fairplayConfig.ts +90 -0
  35. package/src/drm/index.ts +178 -0
  36. package/src/drm/widevineConfig.ts +37 -0
  37. package/src/events.ts +67 -6
  38. package/src/hooks/useProxy.ts +19 -15
  39. package/src/index.ts +5 -3
  40. package/src/nativeInstance.ts +64 -0
  41. package/src/player.ts +51 -42
  42. package/src/source.ts +88 -8
  43. package/src/subtitleTrack.ts +60 -0
  44. package/src/utils.ts +15 -0
package/lib/index.mjs CHANGED
@@ -5,15 +5,253 @@ var __publicField = (obj, key, value) => {
5
5
  return value;
6
6
  };
7
7
 
8
- // src/player.ts
9
- import { NativeModules, Platform } from "react-native";
10
- var PlayerModule = NativeModules.PlayerModule;
11
- var Player = class {
8
+ // src/components/PlayerView/index.tsx
9
+ import React, { useRef, useEffect } from "react";
10
+ import {
11
+ Platform,
12
+ UIManager,
13
+ StyleSheet,
14
+ findNodeHandle as findNodeHandle2
15
+ } from "react-native";
16
+
17
+ // src/components/PlayerView/native.ts
18
+ import { requireNativeComponent } from "react-native";
19
+ var NativePlayerView = requireNativeComponent("NativePlayerView");
20
+
21
+ // src/hooks/useProxy.ts
22
+ import omit from "lodash.omit";
23
+ import { useCallback } from "react";
24
+ import { findNodeHandle } from "react-native";
25
+ function unwrapNativeEvent(event) {
26
+ return omit(event.nativeEvent, ["target"]);
27
+ }
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]);
35
+ }
36
+
37
+ // src/components/PlayerView/index.tsx
38
+ var styles = StyleSheet.create({
39
+ baseStyle: {
40
+ alignSelf: "stretch"
41
+ }
42
+ });
43
+ function dispatch(command, node, playerId) {
44
+ 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
+ }));
49
+ }
50
+ function PlayerView(props) {
51
+ const nativeView = useRef(null);
52
+ const proxy = useProxy(nativeView);
53
+ const style = StyleSheet.flatten([styles.baseStyle, props.style]);
54
+ useEffect(() => {
55
+ props.player.initialize();
56
+ const node = findNodeHandle2(nativeView.current);
57
+ dispatch("attachPlayer", node, props.player.nativeId);
58
+ }, [props.player]);
59
+ return /* @__PURE__ */ React.createElement(NativePlayerView, {
60
+ ref: nativeView,
61
+ style,
62
+ onDestroy: proxy(props.onDestroy),
63
+ onEvent: proxy(props.onEvent),
64
+ onMuted: proxy(props.onMuted),
65
+ onPaused: proxy(props.onPaused),
66
+ onPlay: proxy(props.onPlay),
67
+ onPlaybackFinished: proxy(props.onPlaybackFinished),
68
+ onPlayerActive: proxy(props.onPlayerActive),
69
+ onPlayerError: proxy(props.onPlayerError),
70
+ onPlayerWarning: proxy(props.onPlayerWarning),
71
+ onPlaying: proxy(props.onPlaying),
72
+ onReady: proxy(props.onReady),
73
+ onSeek: proxy(props.onSeek),
74
+ onSeeked: proxy(props.onSeeked),
75
+ onSourceError: proxy(props.onSourceError),
76
+ onSourceLoad: proxy(props.onSourceLoad),
77
+ onSourceLoaded: proxy(props.onSourceLoaded),
78
+ onSourceUnloaded: proxy(props.onSourceUnloaded),
79
+ onSourceWarning: proxy(props.onSourceWarning),
80
+ onSubtitleAdded: proxy(props.onSubtitleAdded),
81
+ onSubtitleChanged: proxy(props.onSubtitleChanged),
82
+ onSubtitleRemoved: proxy(props.onSubtitleRemoved),
83
+ onTimeChanged: proxy(props.onTimeChanged),
84
+ onUnmuted: proxy(props.onUnmuted)
85
+ });
86
+ }
87
+
88
+ // src/drm/index.ts
89
+ import { NativeModules as NativeModules2, Platform as Platform2 } from "react-native";
90
+ import BatchedBridge from "react-native/Libraries/BatchedBridge/BatchedBridge";
91
+
92
+ // src/nativeInstance.ts
93
+ import { NativeModules } from "react-native";
94
+ var Uuid = NativeModules.UuidModule;
95
+ var NativeInstance = class {
12
96
  constructor(config) {
13
97
  __publicField(this, "nativeId");
14
98
  __publicField(this, "config");
15
- __publicField(this, "load", (source) => {
16
- PlayerModule.loadSource(this.nativeId, source);
99
+ this.config = config;
100
+ this.nativeId = config?.nativeId ?? Uuid.generate();
101
+ }
102
+ };
103
+
104
+ // src/drm/index.ts
105
+ var DrmModule = NativeModules2.DrmModule;
106
+ var Drm = class extends NativeInstance {
107
+ constructor() {
108
+ super(...arguments);
109
+ __publicField(this, "isInitialized", false);
110
+ __publicField(this, "isDestroyed", false);
111
+ __publicField(this, "initialize", () => {
112
+ if (!this.isInitialized) {
113
+ BatchedBridge.registerCallableModule(`DRM-${this.nativeId}`, this);
114
+ DrmModule.initWithConfig(this.nativeId, this.config);
115
+ this.isInitialized = true;
116
+ }
117
+ });
118
+ __publicField(this, "destroy", () => {
119
+ if (!this.isDestroyed) {
120
+ DrmModule.destroy(this.nativeId);
121
+ this.isDestroyed = true;
122
+ }
123
+ });
124
+ __publicField(this, "onPrepareCertificate", (certificate) => {
125
+ if (this.config?.fairplay?.prepareCertificate) {
126
+ DrmModule.setPreparedCertificate(this.nativeId, this.config?.fairplay?.prepareCertificate?.(certificate));
127
+ }
128
+ });
129
+ __publicField(this, "onPrepareMessage", (message, assetId) => {
130
+ const config = Platform2.OS === "ios" ? this.config?.fairplay : this.config?.widevine;
131
+ if (config && config.prepareMessage) {
132
+ DrmModule.setPreparedMessage(this.nativeId, Platform2.OS === "ios" ? config.prepareMessage?.(message, assetId) : config.prepareMessage?.(message));
133
+ }
134
+ });
135
+ __publicField(this, "onPrepareSyncMessage", (syncMessage, assetId) => {
136
+ if (this.config?.fairplay?.prepareSyncMessage) {
137
+ DrmModule.setPreparedSyncMessage(this.nativeId, this.config?.fairplay?.prepareSyncMessage?.(syncMessage, assetId));
138
+ }
139
+ });
140
+ __publicField(this, "onPrepareLicense", (license) => {
141
+ const prepareLicense = Platform2.OS === "ios" ? this.config?.fairplay?.prepareLicense : this.config?.widevine?.prepareLicense;
142
+ if (prepareLicense) {
143
+ DrmModule.setPreparedLicense(this.nativeId, prepareLicense(license));
144
+ }
145
+ });
146
+ __publicField(this, "onPrepareLicenseServerUrl", (licenseServerUrl) => {
147
+ if (this.config?.fairplay?.prepareLicenseServerUrl) {
148
+ DrmModule.setPreparedLicenseServerUrl(this.nativeId, this.config?.fairplay?.prepareLicenseServerUrl?.(licenseServerUrl));
149
+ }
150
+ });
151
+ __publicField(this, "onPrepareContentId", (contentId) => {
152
+ if (this.config?.fairplay?.prepareContentId) {
153
+ DrmModule.setPreparedContentId(this.nativeId, this.config?.fairplay?.prepareContentId?.(contentId));
154
+ }
155
+ });
156
+ }
157
+ };
158
+
159
+ // src/hooks/usePlayer.ts
160
+ import { useRef as useRef2 } from "react";
161
+
162
+ // src/player.ts
163
+ import { NativeModules as NativeModules4, Platform as Platform3 } from "react-native";
164
+
165
+ // src/source.ts
166
+ import { NativeModules as NativeModules3 } from "react-native";
167
+ var SourceModule = NativeModules3.SourceModule;
168
+ var SourceType = /* @__PURE__ */ ((SourceType2) => {
169
+ SourceType2["NONE"] = "none";
170
+ SourceType2["HLS"] = "hls";
171
+ SourceType2["DASH"] = "dash";
172
+ SourceType2["PROGRESSIVE"] = "progressive";
173
+ return SourceType2;
174
+ })(SourceType || {});
175
+ var LoadingState = /* @__PURE__ */ ((LoadingState2) => {
176
+ LoadingState2[LoadingState2["UNLOADED"] = 0] = "UNLOADED";
177
+ LoadingState2[LoadingState2["LOADING"] = 1] = "LOADING";
178
+ LoadingState2[LoadingState2["LOADED"] = 2] = "LOADED";
179
+ return LoadingState2;
180
+ })(LoadingState || {});
181
+ var Source = class extends NativeInstance {
182
+ constructor() {
183
+ super(...arguments);
184
+ __publicField(this, "drm");
185
+ __publicField(this, "isInitialized", false);
186
+ __publicField(this, "isDestroyed", false);
187
+ __publicField(this, "initialize", () => {
188
+ if (!this.isInitialized) {
189
+ if (this.config?.drmConfig) {
190
+ this.drm = new Drm(this.config.drmConfig);
191
+ this.drm.initialize();
192
+ SourceModule.initWithDrmConfig(this.nativeId, this.drm.nativeId, this.config);
193
+ } else {
194
+ SourceModule.initWithConfig(this.nativeId, this.config);
195
+ }
196
+ this.isInitialized = true;
197
+ }
198
+ });
199
+ __publicField(this, "destroy", () => {
200
+ if (!this.isDestroyed) {
201
+ SourceModule.destroy(this.nativeId);
202
+ this.drm?.destroy();
203
+ this.isDestroyed = true;
204
+ }
205
+ });
206
+ __publicField(this, "duration", async () => {
207
+ return SourceModule.duration(this.nativeId);
208
+ });
209
+ __publicField(this, "isActive", async () => {
210
+ return SourceModule.isActive(this.nativeId);
211
+ });
212
+ __publicField(this, "isAttachedToPlayer", async () => {
213
+ return SourceModule.isAttachedToPlayer(this.nativeId);
214
+ });
215
+ __publicField(this, "metadata", async () => {
216
+ return SourceModule.getMetadata(this.nativeId);
217
+ });
218
+ __publicField(this, "setMetadata", (metadata) => {
219
+ SourceModule.setMetadata(this.nativeId, metadata);
220
+ });
221
+ __publicField(this, "loadingState", async () => {
222
+ return SourceModule.loadingState(this.nativeId);
223
+ });
224
+ }
225
+ };
226
+
227
+ // src/player.ts
228
+ var PlayerModule = NativeModules4.PlayerModule;
229
+ var Player = class extends NativeInstance {
230
+ constructor() {
231
+ super(...arguments);
232
+ __publicField(this, "source");
233
+ __publicField(this, "isInitialized", false);
234
+ __publicField(this, "isDestroyed", false);
235
+ __publicField(this, "initialize", () => {
236
+ if (!this.isInitialized) {
237
+ PlayerModule.initWithConfig(this.nativeId, this.config);
238
+ this.isInitialized = true;
239
+ }
240
+ });
241
+ __publicField(this, "destroy", () => {
242
+ if (!this.isDestroyed) {
243
+ PlayerModule.destroy(this.nativeId);
244
+ this.source?.destroy();
245
+ this.isDestroyed = true;
246
+ }
247
+ });
248
+ __publicField(this, "load", (sourceConfig) => {
249
+ this.loadSource(new Source(sourceConfig));
250
+ });
251
+ __publicField(this, "loadSource", (source) => {
252
+ source.initialize();
253
+ this.source = source;
254
+ PlayerModule.loadSource(this.nativeId, source.nativeId);
17
255
  });
18
256
  __publicField(this, "unload", () => {
19
257
  PlayerModule.unload(this.nativeId);
@@ -33,15 +271,9 @@ var Player = class {
33
271
  __publicField(this, "unmute", () => {
34
272
  PlayerModule.unmute(this.nativeId);
35
273
  });
36
- __publicField(this, "destroy", () => {
37
- PlayerModule.destroy(this.nativeId);
38
- });
39
274
  __publicField(this, "setVolume", (volume) => {
40
275
  PlayerModule.setVolume(this.nativeId, volume);
41
276
  });
42
- __publicField(this, "getSource", async () => {
43
- return PlayerModule.source(this.nativeId);
44
- });
45
277
  __publicField(this, "getVolume", async () => {
46
278
  return PlayerModule.getVolume(this.nativeId);
47
279
  });
@@ -64,128 +296,44 @@ var Player = class {
64
296
  return PlayerModule.isLive(this.nativeId);
65
297
  });
66
298
  __publicField(this, "isAirPlayActive", async () => {
67
- if (Platform.OS === "android") {
299
+ if (Platform3.OS === "android") {
68
300
  console.warn(`[Player ${this.nativeId}] Method isAirPlayActive is not available for Android. Only iOS devices.`);
69
301
  return false;
70
302
  }
71
303
  return PlayerModule.isAirPlayActive(this.nativeId);
72
304
  });
73
305
  __publicField(this, "isAirPlayAvailable", async () => {
74
- if (Platform.OS === "android") {
306
+ if (Platform3.OS === "android") {
75
307
  console.warn(`[Player ${this.nativeId}] Method isAirPlayAvailable is not available for Android. Only iOS devices.`);
76
308
  return false;
77
309
  }
78
310
  return PlayerModule.isAirPlayAvailable(this.nativeId);
79
311
  });
80
- this.config = config ?? null;
81
- this.nativeId = config?.nativeId ?? PlayerModule.generateUUIDv4();
82
- PlayerModule.initWithConfig(this.nativeId, this.config);
312
+ __publicField(this, "getAvailableSubtitles", async () => {
313
+ return PlayerModule.getAvailableSubtitles(this.nativeId);
314
+ });
83
315
  }
84
316
  };
85
317
 
86
- // src/source.ts
87
- var SourceType = /* @__PURE__ */ ((SourceType2) => {
88
- SourceType2["NONE"] = "none";
89
- SourceType2["HLS"] = "hls";
90
- SourceType2["DASH"] = "dash";
91
- SourceType2["PROGRESSIVE"] = "progressive";
92
- return SourceType2;
93
- })(SourceType || {});
94
- var LoadingState = /* @__PURE__ */ ((LoadingState2) => {
95
- LoadingState2[LoadingState2["UNLOADED"] = 0] = "UNLOADED";
96
- LoadingState2[LoadingState2["LOADING"] = 1] = "LOADING";
97
- LoadingState2[LoadingState2["LOADED"] = 2] = "LOADED";
98
- return LoadingState2;
99
- })(LoadingState || {});
100
-
101
318
  // src/hooks/usePlayer.ts
102
- import { useRef } from "react";
103
319
  function usePlayer(config) {
104
- return useRef(new Player(config)).current;
105
- }
106
-
107
- // src/components/PlayerView/index.tsx
108
- import React, { useRef as useRef3, useEffect } from "react";
109
- import {
110
- Platform as Platform2,
111
- UIManager,
112
- StyleSheet,
113
- findNodeHandle as findNodeHandle2
114
- } from "react-native";
115
-
116
- // src/components/PlayerView/native.ts
117
- import { requireNativeComponent } from "react-native";
118
- var NativePlayerView = requireNativeComponent("NativePlayerView");
119
-
120
- // src/hooks/useProxy.ts
121
- import omit from "lodash.omit";
122
- import { useCallback, useRef as useRef2 } from "react";
123
- import { findNodeHandle } from "react-native";
124
- function unwrapNativeEvent(event) {
125
- return omit(event.nativeEvent, ["target"]);
126
- }
127
- function useProxy(viewRef, proxy) {
128
- const proxyRef = useRef2(proxy);
129
- return useCallback((event) => {
130
- const node = event.target._nativeTag;
131
- if (node === findNodeHandle(viewRef.current)) {
132
- proxyRef.current?.(unwrapNativeEvent(event));
133
- }
134
- }, [viewRef]);
320
+ return useRef2(new Player(config)).current;
135
321
  }
136
322
 
137
- // src/components/PlayerView/index.tsx
138
- var styles = StyleSheet.create({
139
- baseStyle: {
140
- alignSelf: "stretch"
141
- }
142
- });
143
- function dispatch(command, node, playerId) {
144
- const commandId = Platform2.OS === "android" ? UIManager.NativePlayerView.Commands[command].toString() : UIManager.getViewManagerConfig("NativePlayerView").Commands[command];
145
- UIManager.dispatchViewManagerCommand(node, commandId, Platform2.select({
146
- ios: [playerId],
147
- android: [node, playerId]
148
- }));
149
- }
150
- function PlayerView(props) {
151
- const nativeView = useRef3(null);
152
- const style = StyleSheet.flatten([styles.baseStyle, props.style]);
153
- useEffect(() => {
154
- const node = findNodeHandle2(nativeView.current);
155
- dispatch("attachPlayer", node, props.player.nativeId);
156
- return () => {
157
- dispatch("detachPlayer", node, props.player.nativeId);
158
- };
159
- }, [props.player.nativeId]);
160
- return /* @__PURE__ */ React.createElement(NativePlayerView, {
161
- ref: nativeView,
162
- style,
163
- onEvent: useProxy(nativeView, props.onEvent),
164
- onPlayerActive: useProxy(nativeView, props.onPlayerActive),
165
- onPlayerError: useProxy(nativeView, props.onPlayerError),
166
- onPlayerWarning: useProxy(nativeView, props.onPlayerWarning),
167
- onDestroy: useProxy(nativeView, props.onDestroy),
168
- onMuted: useProxy(nativeView, props.onMuted),
169
- onUnmuted: useProxy(nativeView, props.onUnmuted),
170
- onReady: useProxy(nativeView, props.onReady),
171
- onPaused: useProxy(nativeView, props.onPaused),
172
- onPlay: useProxy(nativeView, props.onPlay),
173
- onPlaying: useProxy(nativeView, props.onPlaying),
174
- onPlaybackFinished: useProxy(nativeView, props.onPlaybackFinished),
175
- onSeek: useProxy(nativeView, props.onSeek),
176
- onSeeked: useProxy(nativeView, props.onSeeked),
177
- onTimeChanged: useProxy(nativeView, props.onTimeChanged),
178
- onSourceLoad: useProxy(nativeView, props.onSourceLoad),
179
- onSourceLoaded: useProxy(nativeView, props.onSourceLoaded),
180
- onSourceUnloaded: useProxy(nativeView, props.onSourceUnloaded),
181
- onSourceError: useProxy(nativeView, props.onSourceError),
182
- onSourceWarning: useProxy(nativeView, props.onSourceWarning)
183
- });
184
- }
323
+ // src/subtitleTrack.ts
324
+ var SubtitleFormat = /* @__PURE__ */ ((SubtitleFormat2) => {
325
+ SubtitleFormat2["CEA"] = "cea";
326
+ SubtitleFormat2["TTML"] = "ttml";
327
+ SubtitleFormat2["VTT"] = "vtt";
328
+ return SubtitleFormat2;
329
+ })(SubtitleFormat || {});
185
330
  export {
331
+ Drm,
186
332
  LoadingState,
187
333
  Player,
188
334
  PlayerView,
335
+ Source,
189
336
  SourceType,
337
+ SubtitleFormat,
190
338
  usePlayer
191
339
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bitmovin-player-react-native",
3
- "version": "0.1.0",
3
+ "version": "0.2.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",
@@ -29,7 +29,8 @@
29
29
  "build": "tsup ./src/index.ts --dts --target es2020 --format cjs,esm -d lib",
30
30
  "example": "yarn --cwd example",
31
31
  "pods": "cd example && pod-install --quiet",
32
- "bootstrap": "yarn && yarn example && yarn pods"
32
+ "bootstrap": "yarn && yarn example && yarn pods",
33
+ "prepare": "husky install"
33
34
  },
34
35
  "keywords": [
35
36
  "react-native",
@@ -48,8 +49,8 @@
48
49
  "@react-native-community/eslint-config": "3.0.2",
49
50
  "@types/jest": "26.0.24",
50
51
  "@types/lodash.omit": "4.5.0",
51
- "@types/react": "17.0.39",
52
- "@types/react-native": "0.67.7",
52
+ "@types/react": "18.0.15",
53
+ "@types/react-native": "0.69.2",
53
54
  "commitlint": "11.0.0",
54
55
  "eslint": "8.18.0",
55
56
  "eslint-config-prettier": "8.5.0",
@@ -1,25 +1,28 @@
1
1
  import { NativeSyntheticEvent } from 'react-native';
2
2
  import {
3
- Event,
4
- PlayerActiveEvent,
5
- PlayerErrorEvent,
6
- PlayerWarningEvent,
7
3
  DestroyEvent,
4
+ Event,
8
5
  MutedEvent,
9
- UnmutedEvent,
10
- ReadyEvent,
11
6
  PausedEvent,
12
7
  PlayEvent,
13
- PlayingEvent,
14
8
  PlaybackFinishedEvent,
9
+ PlayerActiveEvent,
10
+ PlayerErrorEvent,
11
+ PlayerWarningEvent,
12
+ PlayingEvent,
13
+ ReadyEvent,
15
14
  SeekEvent,
16
15
  SeekedEvent,
17
- TimeChangedEvent,
16
+ SourceErrorEvent,
18
17
  SourceLoadEvent,
19
18
  SourceLoadedEvent,
20
19
  SourceUnloadedEvent,
21
- SourceErrorEvent,
22
20
  SourceWarningEvent,
21
+ SubtitleAddedEvent,
22
+ SubtitleChangedEvent,
23
+ SubtitleRemovedEvent,
24
+ TimeChangedEvent,
25
+ UnmutedEvent,
23
26
  } from '../../events';
24
27
 
25
28
  /**
@@ -27,26 +30,29 @@ import {
27
30
  * Used to generate the specific events interface for each component.
28
31
  */
29
32
  interface EventProps {
30
- onEvent: Event;
31
- onPlayerActive: PlayerActiveEvent;
32
- onPlayerError: PlayerErrorEvent;
33
- onPlayerWarning: PlayerWarningEvent;
34
33
  onDestroy: DestroyEvent;
34
+ onEvent: Event;
35
35
  onMuted: MutedEvent;
36
- onUnmuted: UnmutedEvent;
37
- onReady: ReadyEvent;
38
36
  onPaused: PausedEvent;
39
37
  onPlay: PlayEvent;
40
- onPlaying: PlayingEvent;
41
38
  onPlaybackFinished: PlaybackFinishedEvent;
39
+ onPlayerActive: PlayerActiveEvent;
40
+ onPlayerError: PlayerErrorEvent;
41
+ onPlayerWarning: PlayerWarningEvent;
42
+ onPlaying: PlayingEvent;
43
+ onReady: ReadyEvent;
42
44
  onSeek: SeekEvent;
43
45
  onSeeked: SeekedEvent;
44
- onTimeChanged: TimeChangedEvent;
46
+ onSourceError: SourceErrorEvent;
45
47
  onSourceLoad: SourceLoadEvent;
46
48
  onSourceLoaded: SourceLoadedEvent;
47
49
  onSourceUnloaded: SourceUnloadedEvent;
48
- onSourceError: SourceErrorEvent;
49
50
  onSourceWarning: SourceWarningEvent;
51
+ onSubtitleAdded: SubtitleAddedEvent;
52
+ onSubtitleChanged: SubtitleChangedEvent;
53
+ onSubtitleRemoved: SubtitleRemovedEvent;
54
+ onTimeChanged: TimeChangedEvent;
55
+ onUnmuted: UnmutedEvent;
50
56
  }
51
57
 
52
58
  /**
@@ -66,41 +66,44 @@ function dispatch(command: string, node: number | null, playerId: string) {
66
66
  export function PlayerView(props: PlayerViewProps) {
67
67
  // Native view reference.
68
68
  const nativeView = useRef(null);
69
+ // Native events proxy helper.
70
+ const proxy = useProxy(nativeView);
69
71
  // Style resulting from merging `baseStyle` and `props.style`.
70
72
  const style = StyleSheet.flatten([styles.baseStyle, props.style]);
71
73
  useEffect(() => {
72
- // Attach `props.player` to native `PlayerView`.
74
+ // Initialize native player instance if needed.
75
+ props.player.initialize();
76
+ // Attach native player to native `PlayerView`.
73
77
  const node = findNodeHandle(nativeView.current);
74
78
  dispatch('attachPlayer', node, props.player.nativeId);
75
- return () => {
76
- // Detach `props.player` from native `PlayerView`.
77
- dispatch('detachPlayer', node, props.player.nativeId);
78
- };
79
- }, [props.player.nativeId]);
79
+ }, [props.player]);
80
80
  return (
81
81
  <NativePlayerView
82
82
  ref={nativeView}
83
83
  style={style}
84
- onEvent={useProxy(nativeView, props.onEvent)}
85
- onPlayerActive={useProxy(nativeView, props.onPlayerActive)}
86
- onPlayerError={useProxy(nativeView, props.onPlayerError)}
87
- onPlayerWarning={useProxy(nativeView, props.onPlayerWarning)}
88
- onDestroy={useProxy(nativeView, props.onDestroy)}
89
- onMuted={useProxy(nativeView, props.onMuted)}
90
- onUnmuted={useProxy(nativeView, props.onUnmuted)}
91
- onReady={useProxy(nativeView, props.onReady)}
92
- onPaused={useProxy(nativeView, props.onPaused)}
93
- onPlay={useProxy(nativeView, props.onPlay)}
94
- onPlaying={useProxy(nativeView, props.onPlaying)}
95
- onPlaybackFinished={useProxy(nativeView, props.onPlaybackFinished)}
96
- onSeek={useProxy(nativeView, props.onSeek)}
97
- onSeeked={useProxy(nativeView, props.onSeeked)}
98
- onTimeChanged={useProxy(nativeView, props.onTimeChanged)}
99
- onSourceLoad={useProxy(nativeView, props.onSourceLoad)}
100
- onSourceLoaded={useProxy(nativeView, props.onSourceLoaded)}
101
- onSourceUnloaded={useProxy(nativeView, props.onSourceUnloaded)}
102
- onSourceError={useProxy(nativeView, props.onSourceError)}
103
- onSourceWarning={useProxy(nativeView, props.onSourceWarning)}
84
+ onDestroy={proxy(props.onDestroy)}
85
+ onEvent={proxy(props.onEvent)}
86
+ onMuted={proxy(props.onMuted)}
87
+ onPaused={proxy(props.onPaused)}
88
+ onPlay={proxy(props.onPlay)}
89
+ onPlaybackFinished={proxy(props.onPlaybackFinished)}
90
+ onPlayerActive={proxy(props.onPlayerActive)}
91
+ onPlayerError={proxy(props.onPlayerError)}
92
+ onPlayerWarning={proxy(props.onPlayerWarning)}
93
+ onPlaying={proxy(props.onPlaying)}
94
+ onReady={proxy(props.onReady)}
95
+ onSeek={proxy(props.onSeek)}
96
+ onSeeked={proxy(props.onSeeked)}
97
+ onSourceError={proxy(props.onSourceError)}
98
+ onSourceLoad={proxy(props.onSourceLoad)}
99
+ onSourceLoaded={proxy(props.onSourceLoaded)}
100
+ onSourceUnloaded={proxy(props.onSourceUnloaded)}
101
+ onSourceWarning={proxy(props.onSourceWarning)}
102
+ onSubtitleAdded={proxy(props.onSubtitleAdded)}
103
+ onSubtitleChanged={proxy(props.onSubtitleChanged)}
104
+ onSubtitleRemoved={proxy(props.onSubtitleRemoved)}
105
+ onTimeChanged={proxy(props.onTimeChanged)}
106
+ onUnmuted={proxy(props.onUnmuted)}
104
107
  />
105
108
  );
106
109
  }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Represents a FairPlay Streaming DRM config.
3
+ */
4
+ export interface FairplayConfig {
5
+ /**
6
+ * The DRM license acquisition URL.
7
+ */
8
+ licenseUrl: string;
9
+ /**
10
+ * The URL to the FairPlay Streaming certificate of the license server.
11
+ */
12
+ certificateUrl?: string;
13
+ /**
14
+ * A dictionary to specify custom HTTP headers for the license request.
15
+ */
16
+ licenseRequestHeaders?: Record<string, string>;
17
+ /**
18
+ * A dictionary to specify custom HTTP headers for the certificate request.
19
+ */
20
+ certificateRequestHeaders?: Record<string, string>;
21
+ /**
22
+ * A block to prepare the loaded certificate before building SPC data and passing it into the
23
+ * system. This is needed if the server responds with anything else than the certificate, e.g. if
24
+ * the certificate is wrapped into a JSON object. The server response for the certificate request
25
+ * is passed as parameter “as is”.
26
+ *
27
+ * Note that both the passed `certificate` data and this block return value should be a Base64
28
+ * string. So use whatever solution suits you best to handle Base64 in React Native.
29
+ *
30
+ * @param certificate - Base64 encoded certificate data.
31
+ * @returns The processed Base64 encoded certificate.
32
+ */
33
+ prepareCertificate?: (certificate: string) => string;
34
+ /**
35
+ * A block to prepare the data which is sent as the body of the POST license request.
36
+ * As many DRM providers expect different, vendor-specific messages, this can be done using
37
+ * this user-defined block.
38
+ *
39
+ * Note that both the passed `message` data and this block return value should be a Base64 string.
40
+ * So use whatever solution suits you best to handle Base64 in React Native.
41
+ *
42
+ * @param message - Base64 encoded message data.
43
+ * @param assetId - Stream asset ID.
44
+ * @returns The processed Base64 encoded message.
45
+ */
46
+ prepareMessage?: (message: string, assetId: string) => string;
47
+ /**
48
+ * A block to prepare the data which is sent as the body of the POST request for syncing the DRM
49
+ * license information.
50
+ *
51
+ * Note that both the passed `syncMessage` data and this block return value should be a Base64
52
+ * string. So use whatever solution suits you best to handle Base64 in React Native.
53
+ *
54
+ * @param message - Base64 encoded message data.
55
+ * @param assetId - Asset ID.
56
+ * @returns The processed Base64 encoded sync message.
57
+ */
58
+ prepareSyncMessage?: (syncMessage: string, assetId: string) => string;
59
+ /**
60
+ * A block to prepare the loaded CKC Data before passing it to the system. This is needed if the
61
+ * server responds with anything else than the license, e.g. if the license is wrapped into a JSON
62
+ * object.
63
+ *
64
+ * Note that both the passed `license` data and this block return value should be a Base64 string.
65
+ * So use whatever solution suits you best to handle Base64 in React Native.
66
+ *
67
+ * @param license - Base64 encoded license data.
68
+ * @returns The processed Base64 encoded license.
69
+ */
70
+ prepareLicense?: (license: string) => string;
71
+ /**
72
+ * A block to prepare the URI (without the skd://) from the HLS manifest before passing it to the
73
+ * system.
74
+ *
75
+ * @param licenseServerUrl - License server URL string.
76
+ * @returns The processed license server URL string.
77
+ */
78
+ prepareLicenseServerUrl?: (licenseServerUrl: string) => string;
79
+ /**
80
+ * A block to prepare the `contentId`, which is sent to the FairPlay Streaming license server as
81
+ * request body, and which is used to build the SPC data. As many DRM providers expect different,
82
+ * vendor-specific messages, this can be done using this user-defined block. The parameter is the
83
+ * skd:// URI extracted from the HLS manifest (m3u8) and the return value should be the contentID
84
+ * as string.
85
+ *
86
+ * @param contentId - Extracted content id string.
87
+ * @returns The processed contentId.
88
+ */
89
+ prepareContentId?: (contentId: string) => string;
90
+ }