@granite-js/video 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/GraniteVideo.podspec +72 -0
- package/android/README.md +232 -0
- package/android/build.gradle +117 -0
- package/android/gradle.properties +8 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/run/granite/video/GraniteVideoModule.kt +70 -0
- package/android/src/main/java/run/granite/video/GraniteVideoPackage.kt +43 -0
- package/android/src/main/java/run/granite/video/GraniteVideoView.kt +384 -0
- package/android/src/main/java/run/granite/video/GraniteVideoViewManager.kt +318 -0
- package/android/src/main/java/run/granite/video/event/GraniteVideoEvents.kt +273 -0
- package/android/src/main/java/run/granite/video/event/VideoEventDispatcher.kt +66 -0
- package/android/src/main/java/run/granite/video/event/VideoEventListenerAdapter.kt +157 -0
- package/android/src/main/java/run/granite/video/provider/GraniteVideoProvider.kt +346 -0
- package/android/src/media3/AndroidManifest.xml +9 -0
- package/android/src/media3/java/run/granite/video/provider/media3/ExoPlayerProvider.kt +386 -0
- package/android/src/media3/java/run/granite/video/provider/media3/Media3ContentProvider.kt +29 -0
- package/android/src/media3/java/run/granite/video/provider/media3/Media3Initializer.kt +25 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/ExoPlayerFactory.kt +32 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/MediaSourceFactory.kt +61 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/TrackSelectorFactory.kt +26 -0
- package/android/src/media3/java/run/granite/video/provider/media3/factory/VideoSurfaceFactory.kt +62 -0
- package/android/src/media3/java/run/granite/video/provider/media3/listener/ExoPlayerEventListener.kt +104 -0
- package/android/src/media3/java/run/granite/video/provider/media3/scheduler/ProgressScheduler.kt +56 -0
- package/android/src/test/java/run/granite/video/GraniteVideoViewRobolectricTest.kt +598 -0
- package/android/src/test/java/run/granite/video/event/VideoEventListenerAdapterTest.kt +319 -0
- package/android/src/test/java/run/granite/video/helpers/FakeGraniteVideoProvider.kt +161 -0
- package/android/src/test/java/run/granite/video/helpers/TestProgressScheduler.kt +42 -0
- package/android/src/test/java/run/granite/video/provider/GraniteVideoRegistryTest.kt +232 -0
- package/android/src/test/java/run/granite/video/provider/ProviderContractTest.kt +174 -0
- package/android/src/test/java/run/granite/video/provider/media3/listener/ExoPlayerEventListenerTest.kt +243 -0
- package/android/src/test/resources/kotest.properties +2 -0
- package/dist/module/GraniteVideo.js +458 -0
- package/dist/module/GraniteVideo.js.map +1 -0
- package/dist/module/GraniteVideoNativeComponent.ts +265 -0
- package/dist/module/index.js +7 -0
- package/dist/module/index.js.map +1 -0
- package/dist/module/package.json +1 -0
- package/dist/module/types.js +4 -0
- package/dist/module/types.js.map +1 -0
- package/dist/typescript/GraniteVideo.d.ts +12 -0
- package/dist/typescript/GraniteVideoNativeComponent.d.ts +189 -0
- package/dist/typescript/index.d.ts +5 -0
- package/dist/typescript/types.d.ts +328 -0
- package/ios/GraniteVideoComponentsProvider.h +10 -0
- package/ios/GraniteVideoProvider.swift +280 -0
- package/ios/GraniteVideoView.h +15 -0
- package/ios/GraniteVideoView.mm +661 -0
- package/ios/Providers/AVPlayerProvider.swift +541 -0
- package/package.json +106 -0
- package/src/GraniteVideo.tsx +575 -0
- package/src/GraniteVideoNativeComponent.ts +265 -0
- package/src/index.ts +8 -0
- package/src/types.ts +464 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import React, { forwardRef, useRef, useImperativeHandle, useCallback } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Platform,
|
|
4
|
+
StyleSheet,
|
|
5
|
+
View,
|
|
6
|
+
Image,
|
|
7
|
+
findNodeHandle,
|
|
8
|
+
NativeModules,
|
|
9
|
+
type StyleProp,
|
|
10
|
+
type ViewStyle,
|
|
11
|
+
type NativeSyntheticEvent,
|
|
12
|
+
} from 'react-native';
|
|
13
|
+
import NativeGraniteVideoView, {
|
|
14
|
+
Commands,
|
|
15
|
+
type NativeProps,
|
|
16
|
+
OnVideoLoadStartEvent,
|
|
17
|
+
OnVideoLoadEvent,
|
|
18
|
+
OnVideoErrorEvent,
|
|
19
|
+
OnVideoProgressEvent,
|
|
20
|
+
OnVideoSeekEvent,
|
|
21
|
+
OnVideoBufferEvent,
|
|
22
|
+
OnVideoBandwidthUpdateEvent,
|
|
23
|
+
OnVideoPlaybackStateChangedEvent,
|
|
24
|
+
OnVideoPlaybackRateChangeEvent,
|
|
25
|
+
OnVideoVolumeChangeEvent,
|
|
26
|
+
OnVideoAudioFocusChangedEvent,
|
|
27
|
+
OnVideoPictureInPictureStatusChangedEvent,
|
|
28
|
+
OnVideoControlsVisibilityChangeEvent,
|
|
29
|
+
OnVideoExternalPlaybackChangeEvent,
|
|
30
|
+
OnVideoAspectRatioEvent,
|
|
31
|
+
TransferEndEvent,
|
|
32
|
+
} from './GraniteVideoNativeComponent';
|
|
33
|
+
import type { VideoRef, VideoSource, VideoProps, OnLoadData } from './types';
|
|
34
|
+
|
|
35
|
+
const { GraniteVideoModule } = NativeModules;
|
|
36
|
+
|
|
37
|
+
// For Fabric (New Architecture), the component is always available through codegenNativeComponent
|
|
38
|
+
// We don't need to check UIManager.getViewManagerConfig which is Old Architecture only
|
|
39
|
+
|
|
40
|
+
function normalizeSource(source: VideoSource | number): NativeProps['source'] | undefined {
|
|
41
|
+
if (typeof source === 'number') {
|
|
42
|
+
// require() - not yet supported in native
|
|
43
|
+
return undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
uri: source.uri,
|
|
48
|
+
type: source.type,
|
|
49
|
+
startPosition: source.startPosition,
|
|
50
|
+
cropStart: source.cropStart,
|
|
51
|
+
cropEnd: source.cropEnd,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function normalizeSelectedTrack(
|
|
56
|
+
track?: VideoProps['selectedAudioTrack']
|
|
57
|
+
): NativeProps['selectedAudioTrack'] | undefined {
|
|
58
|
+
if (!track) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
type: track.type,
|
|
63
|
+
value: track.value?.toString(),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function normalizeSelectedVideoTrack(
|
|
68
|
+
track?: VideoProps['selectedVideoTrack']
|
|
69
|
+
): NativeProps['selectedVideoTrack'] | undefined {
|
|
70
|
+
if (!track) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
type: track.type,
|
|
75
|
+
value: track.value,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function normalizeDrm(drm?: VideoProps['drm']): NativeProps['drm'] | undefined {
|
|
80
|
+
if (!drm) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
type: drm.type,
|
|
85
|
+
licenseServer: drm.licenseServer,
|
|
86
|
+
contentId: drm.contentId,
|
|
87
|
+
certificateUrl: drm.certificateUrl,
|
|
88
|
+
base64Certificate: drm.base64Certificate,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function normalizeBufferConfig(config?: VideoProps['bufferConfig']): NativeProps['bufferConfig'] | undefined {
|
|
93
|
+
if (!config) {
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
minBufferMs: config.minBufferMs,
|
|
98
|
+
maxBufferMs: config.maxBufferMs,
|
|
99
|
+
bufferForPlaybackMs: config.bufferForPlaybackMs,
|
|
100
|
+
bufferForPlaybackAfterRebufferMs: config.bufferForPlaybackAfterRebufferMs,
|
|
101
|
+
backBufferDurationMs: config.backBufferDurationMs,
|
|
102
|
+
cacheSizeMB: config.cacheSizeMB,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function getPosterUri(poster?: VideoProps['poster']): string | undefined {
|
|
107
|
+
if (!poster) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
if (typeof poster === 'string') {
|
|
111
|
+
return poster;
|
|
112
|
+
}
|
|
113
|
+
if (typeof poster === 'object' && 'uri' in poster && poster.uri) {
|
|
114
|
+
return poster.uri;
|
|
115
|
+
}
|
|
116
|
+
const resolved = Image.resolveAssetSource(poster as any);
|
|
117
|
+
return resolved?.uri;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const VideoBase = forwardRef<VideoRef, VideoProps>((props, ref) => {
|
|
121
|
+
const {
|
|
122
|
+
// Test ID
|
|
123
|
+
testID,
|
|
124
|
+
// Style
|
|
125
|
+
style,
|
|
126
|
+
// Source
|
|
127
|
+
source,
|
|
128
|
+
// Poster
|
|
129
|
+
poster,
|
|
130
|
+
posterResizeMode = 'contain',
|
|
131
|
+
// Playback Control
|
|
132
|
+
paused = false,
|
|
133
|
+
muted = false,
|
|
134
|
+
volume = 1.0,
|
|
135
|
+
rate = 1.0,
|
|
136
|
+
repeat = false,
|
|
137
|
+
playInBackground = false,
|
|
138
|
+
playWhenInactive = false,
|
|
139
|
+
automaticallyWaitsToMinimizeStalling = true,
|
|
140
|
+
shutterColor,
|
|
141
|
+
// Display
|
|
142
|
+
resizeMode = 'contain',
|
|
143
|
+
viewType = 'surface',
|
|
144
|
+
useTextureView = false,
|
|
145
|
+
useSecureView = false,
|
|
146
|
+
// Buffering
|
|
147
|
+
bufferConfig,
|
|
148
|
+
minLoadRetryCount = 3,
|
|
149
|
+
maxBitRate,
|
|
150
|
+
preferredForwardBufferDuration,
|
|
151
|
+
// Track Selection
|
|
152
|
+
selectedAudioTrack,
|
|
153
|
+
selectedTextTrack,
|
|
154
|
+
selectedVideoTrack,
|
|
155
|
+
// DRM
|
|
156
|
+
drm,
|
|
157
|
+
localSourceEncryptionKeyScheme,
|
|
158
|
+
// Ads
|
|
159
|
+
adTagUrl,
|
|
160
|
+
adLanguage,
|
|
161
|
+
// Controls
|
|
162
|
+
controls = false,
|
|
163
|
+
showNotificationControls = false,
|
|
164
|
+
disableFocus = false,
|
|
165
|
+
disableDisconnectError = false,
|
|
166
|
+
focusable = true,
|
|
167
|
+
hideShutterView = false,
|
|
168
|
+
preventsDisplaySleepDuringVideoPlayback = true,
|
|
169
|
+
// Fullscreen
|
|
170
|
+
fullscreen = false,
|
|
171
|
+
fullscreenAutorotate = true,
|
|
172
|
+
fullscreenOrientation = 'all',
|
|
173
|
+
// Picture in Picture
|
|
174
|
+
pictureInPicture = false,
|
|
175
|
+
// Content
|
|
176
|
+
contentStartTime,
|
|
177
|
+
allowsExternalPlayback = true,
|
|
178
|
+
audioOutput = 'speaker',
|
|
179
|
+
ignoreSilentSwitch = 'inherit',
|
|
180
|
+
mixWithOthers = 'inherit',
|
|
181
|
+
// Debug
|
|
182
|
+
debug,
|
|
183
|
+
// Events
|
|
184
|
+
onLoadStart,
|
|
185
|
+
onLoad,
|
|
186
|
+
onError,
|
|
187
|
+
onProgress,
|
|
188
|
+
onSeek,
|
|
189
|
+
onEnd,
|
|
190
|
+
onBuffer,
|
|
191
|
+
onBandwidthUpdate,
|
|
192
|
+
onPlaybackStateChanged,
|
|
193
|
+
onPlaybackRateChange,
|
|
194
|
+
onVolumeChange,
|
|
195
|
+
onIdle,
|
|
196
|
+
onReadyForDisplay,
|
|
197
|
+
onAudioFocusChanged,
|
|
198
|
+
onAudioBecomingNoisy,
|
|
199
|
+
onFullscreenPlayerWillPresent,
|
|
200
|
+
onFullscreenPlayerDidPresent,
|
|
201
|
+
onFullscreenPlayerWillDismiss,
|
|
202
|
+
onFullscreenPlayerDidDismiss,
|
|
203
|
+
onPictureInPictureStatusChanged,
|
|
204
|
+
onRestoreUserInterfaceForPictureInPictureStop,
|
|
205
|
+
onControlsVisibilityChange,
|
|
206
|
+
onExternalPlaybackChange,
|
|
207
|
+
onAspectRatio,
|
|
208
|
+
onTransferEnd,
|
|
209
|
+
} = props;
|
|
210
|
+
|
|
211
|
+
const nativeRef = useRef<React.ElementRef<typeof NativeGraniteVideoView>>(null);
|
|
212
|
+
|
|
213
|
+
// === Imperative Handle ===
|
|
214
|
+
useImperativeHandle(ref, () => ({
|
|
215
|
+
seek: (time: number, tolerance?: number) => {
|
|
216
|
+
if (nativeRef.current) {
|
|
217
|
+
Commands.seek(nativeRef.current, time, tolerance ?? 0);
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
pause: () => {
|
|
221
|
+
if (nativeRef.current) {
|
|
222
|
+
Commands.pause(nativeRef.current);
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
resume: () => {
|
|
226
|
+
if (nativeRef.current) {
|
|
227
|
+
Commands.resume(nativeRef.current);
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
setVolume: (vol: number) => {
|
|
231
|
+
if (nativeRef.current) {
|
|
232
|
+
Commands.adjustVolume(nativeRef.current, vol);
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
setFullScreen: (fs: boolean) => {
|
|
236
|
+
if (nativeRef.current) {
|
|
237
|
+
Commands.setFullScreen(nativeRef.current, fs);
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
presentFullscreenPlayer: () => {
|
|
241
|
+
if (nativeRef.current) {
|
|
242
|
+
Commands.setFullScreen(nativeRef.current, true);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
dismissFullscreenPlayer: () => {
|
|
246
|
+
if (nativeRef.current) {
|
|
247
|
+
Commands.setFullScreen(nativeRef.current, false);
|
|
248
|
+
}
|
|
249
|
+
},
|
|
250
|
+
enterPictureInPicture: () => {
|
|
251
|
+
if (nativeRef.current) {
|
|
252
|
+
Commands.enterPictureInPicture(nativeRef.current);
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
exitPictureInPicture: () => {
|
|
256
|
+
if (nativeRef.current) {
|
|
257
|
+
Commands.exitPictureInPicture(nativeRef.current);
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
setSource: (newSource: VideoSource) => {
|
|
261
|
+
if (nativeRef.current && newSource.uri) {
|
|
262
|
+
Commands.loadSource(nativeRef.current, newSource.uri);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
getCurrentPosition: async () => {
|
|
266
|
+
const handle = findNodeHandle(nativeRef.current);
|
|
267
|
+
if (handle && GraniteVideoModule?.getCurrentPosition) {
|
|
268
|
+
return GraniteVideoModule.getCurrentPosition(handle);
|
|
269
|
+
}
|
|
270
|
+
return 0;
|
|
271
|
+
},
|
|
272
|
+
save: async (options?: { type?: string }) => {
|
|
273
|
+
const handle = findNodeHandle(nativeRef.current);
|
|
274
|
+
if (handle && GraniteVideoModule?.save) {
|
|
275
|
+
return GraniteVideoModule.save(handle, options ?? {});
|
|
276
|
+
}
|
|
277
|
+
return { uri: '' };
|
|
278
|
+
},
|
|
279
|
+
restoreUserInterfaceForPictureInPictureStopCompleted: () => {
|
|
280
|
+
// iOS specific - handled internally
|
|
281
|
+
},
|
|
282
|
+
}));
|
|
283
|
+
|
|
284
|
+
// === Event Handlers ===
|
|
285
|
+
const handleLoadStart = useCallback(
|
|
286
|
+
(event: NativeSyntheticEvent<OnVideoLoadStartEvent>) => {
|
|
287
|
+
onLoadStart?.(event.nativeEvent);
|
|
288
|
+
},
|
|
289
|
+
[onLoadStart]
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
const handleLoad = useCallback(
|
|
293
|
+
(event: NativeSyntheticEvent<OnVideoLoadEvent>) => {
|
|
294
|
+
const nativeEvent = event.nativeEvent;
|
|
295
|
+
const loadData: OnLoadData = {
|
|
296
|
+
...nativeEvent,
|
|
297
|
+
naturalSize: {
|
|
298
|
+
...nativeEvent.naturalSize,
|
|
299
|
+
orientation: nativeEvent.naturalSize.orientation as OnLoadData['naturalSize']['orientation'],
|
|
300
|
+
},
|
|
301
|
+
audioTracks: [],
|
|
302
|
+
textTracks: [],
|
|
303
|
+
videoTracks: [],
|
|
304
|
+
};
|
|
305
|
+
onLoad?.(loadData);
|
|
306
|
+
},
|
|
307
|
+
[onLoad]
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
const handleError = useCallback(
|
|
311
|
+
(event: NativeSyntheticEvent<OnVideoErrorEvent>) => {
|
|
312
|
+
onError?.(event.nativeEvent);
|
|
313
|
+
},
|
|
314
|
+
[onError]
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
const handleProgress = useCallback(
|
|
318
|
+
(event: NativeSyntheticEvent<OnVideoProgressEvent>) => {
|
|
319
|
+
onProgress?.(event.nativeEvent);
|
|
320
|
+
},
|
|
321
|
+
[onProgress]
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
const handleSeek = useCallback(
|
|
325
|
+
(event: NativeSyntheticEvent<OnVideoSeekEvent>) => {
|
|
326
|
+
onSeek?.(event.nativeEvent);
|
|
327
|
+
},
|
|
328
|
+
[onSeek]
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
const handleEnd = useCallback(() => {
|
|
332
|
+
onEnd?.();
|
|
333
|
+
}, [onEnd]);
|
|
334
|
+
|
|
335
|
+
const handleBuffer = useCallback(
|
|
336
|
+
(event: NativeSyntheticEvent<OnVideoBufferEvent>) => {
|
|
337
|
+
onBuffer?.(event.nativeEvent);
|
|
338
|
+
},
|
|
339
|
+
[onBuffer]
|
|
340
|
+
);
|
|
341
|
+
|
|
342
|
+
const handleBandwidthUpdate = useCallback(
|
|
343
|
+
(event: NativeSyntheticEvent<OnVideoBandwidthUpdateEvent>) => {
|
|
344
|
+
onBandwidthUpdate?.(event.nativeEvent);
|
|
345
|
+
},
|
|
346
|
+
[onBandwidthUpdate]
|
|
347
|
+
);
|
|
348
|
+
|
|
349
|
+
const handlePlaybackStateChanged = useCallback(
|
|
350
|
+
(event: NativeSyntheticEvent<OnVideoPlaybackStateChangedEvent>) => {
|
|
351
|
+
onPlaybackStateChanged?.(event.nativeEvent);
|
|
352
|
+
},
|
|
353
|
+
[onPlaybackStateChanged]
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const handlePlaybackRateChange = useCallback(
|
|
357
|
+
(event: NativeSyntheticEvent<OnVideoPlaybackRateChangeEvent>) => {
|
|
358
|
+
onPlaybackRateChange?.(event.nativeEvent);
|
|
359
|
+
},
|
|
360
|
+
[onPlaybackRateChange]
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
const handleVolumeChange = useCallback(
|
|
364
|
+
(event: NativeSyntheticEvent<OnVideoVolumeChangeEvent>) => {
|
|
365
|
+
onVolumeChange?.(event.nativeEvent);
|
|
366
|
+
},
|
|
367
|
+
[onVolumeChange]
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
const handleIdle = useCallback(() => {
|
|
371
|
+
onIdle?.();
|
|
372
|
+
}, [onIdle]);
|
|
373
|
+
|
|
374
|
+
const handleReadyForDisplay = useCallback(() => {
|
|
375
|
+
onReadyForDisplay?.();
|
|
376
|
+
}, [onReadyForDisplay]);
|
|
377
|
+
|
|
378
|
+
const handleAudioFocusChanged = useCallback(
|
|
379
|
+
(event: NativeSyntheticEvent<OnVideoAudioFocusChangedEvent>) => {
|
|
380
|
+
onAudioFocusChanged?.(event.nativeEvent);
|
|
381
|
+
},
|
|
382
|
+
[onAudioFocusChanged]
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
const handleAudioBecomingNoisy = useCallback(() => {
|
|
386
|
+
onAudioBecomingNoisy?.();
|
|
387
|
+
}, [onAudioBecomingNoisy]);
|
|
388
|
+
|
|
389
|
+
const handleFullscreenPlayerWillPresent = useCallback(() => {
|
|
390
|
+
onFullscreenPlayerWillPresent?.();
|
|
391
|
+
}, [onFullscreenPlayerWillPresent]);
|
|
392
|
+
|
|
393
|
+
const handleFullscreenPlayerDidPresent = useCallback(() => {
|
|
394
|
+
onFullscreenPlayerDidPresent?.();
|
|
395
|
+
}, [onFullscreenPlayerDidPresent]);
|
|
396
|
+
|
|
397
|
+
const handleFullscreenPlayerWillDismiss = useCallback(() => {
|
|
398
|
+
onFullscreenPlayerWillDismiss?.();
|
|
399
|
+
}, [onFullscreenPlayerWillDismiss]);
|
|
400
|
+
|
|
401
|
+
const handleFullscreenPlayerDidDismiss = useCallback(() => {
|
|
402
|
+
onFullscreenPlayerDidDismiss?.();
|
|
403
|
+
}, [onFullscreenPlayerDidDismiss]);
|
|
404
|
+
|
|
405
|
+
const handlePictureInPictureStatusChanged = useCallback(
|
|
406
|
+
(event: NativeSyntheticEvent<OnVideoPictureInPictureStatusChangedEvent>) => {
|
|
407
|
+
onPictureInPictureStatusChanged?.(event.nativeEvent);
|
|
408
|
+
},
|
|
409
|
+
[onPictureInPictureStatusChanged]
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
const handleRestoreUserInterfaceForPictureInPictureStop = useCallback(() => {
|
|
413
|
+
onRestoreUserInterfaceForPictureInPictureStop?.();
|
|
414
|
+
}, [onRestoreUserInterfaceForPictureInPictureStop]);
|
|
415
|
+
|
|
416
|
+
const handleControlsVisibilityChange = useCallback(
|
|
417
|
+
(event: NativeSyntheticEvent<OnVideoControlsVisibilityChangeEvent>) => {
|
|
418
|
+
onControlsVisibilityChange?.(event.nativeEvent);
|
|
419
|
+
},
|
|
420
|
+
[onControlsVisibilityChange]
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
const handleExternalPlaybackChange = useCallback(
|
|
424
|
+
(event: NativeSyntheticEvent<OnVideoExternalPlaybackChangeEvent>) => {
|
|
425
|
+
onExternalPlaybackChange?.(event.nativeEvent);
|
|
426
|
+
},
|
|
427
|
+
[onExternalPlaybackChange]
|
|
428
|
+
);
|
|
429
|
+
|
|
430
|
+
const handleAspectRatio = useCallback(
|
|
431
|
+
(event: NativeSyntheticEvent<OnVideoAspectRatioEvent>) => {
|
|
432
|
+
onAspectRatio?.(event.nativeEvent);
|
|
433
|
+
},
|
|
434
|
+
[onAspectRatio]
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const handleTransferEnd = useCallback(
|
|
438
|
+
(event: NativeSyntheticEvent<TransferEndEvent>) => {
|
|
439
|
+
onTransferEnd?.(event.nativeEvent);
|
|
440
|
+
},
|
|
441
|
+
[onTransferEnd]
|
|
442
|
+
);
|
|
443
|
+
|
|
444
|
+
// === Render ===
|
|
445
|
+
const containerStyle: StyleProp<ViewStyle> = [styles.container, style];
|
|
446
|
+
|
|
447
|
+
return (
|
|
448
|
+
<View style={containerStyle} testID={testID}>
|
|
449
|
+
<NativeGraniteVideoView
|
|
450
|
+
ref={nativeRef}
|
|
451
|
+
style={styles.video}
|
|
452
|
+
source={normalizeSource(source)}
|
|
453
|
+
poster={getPosterUri(poster)}
|
|
454
|
+
posterResizeMode={posterResizeMode}
|
|
455
|
+
paused={paused}
|
|
456
|
+
muted={muted}
|
|
457
|
+
volume={volume}
|
|
458
|
+
rate={rate}
|
|
459
|
+
repeat={repeat}
|
|
460
|
+
playInBackground={playInBackground}
|
|
461
|
+
playWhenInactive={playWhenInactive}
|
|
462
|
+
automaticallyWaitsToMinimizeStalling={automaticallyWaitsToMinimizeStalling}
|
|
463
|
+
shutterColor={shutterColor}
|
|
464
|
+
resizeMode={resizeMode}
|
|
465
|
+
viewType={viewType}
|
|
466
|
+
useTextureView={useTextureView}
|
|
467
|
+
useSecureView={useSecureView}
|
|
468
|
+
bufferConfig={normalizeBufferConfig(bufferConfig)}
|
|
469
|
+
minLoadRetryCount={minLoadRetryCount}
|
|
470
|
+
maxBitRate={maxBitRate}
|
|
471
|
+
preferredForwardBufferDuration={preferredForwardBufferDuration}
|
|
472
|
+
selectedAudioTrack={normalizeSelectedTrack(selectedAudioTrack)}
|
|
473
|
+
selectedTextTrack={normalizeSelectedTrack(selectedTextTrack)}
|
|
474
|
+
selectedVideoTrack={normalizeSelectedVideoTrack(selectedVideoTrack)}
|
|
475
|
+
drm={normalizeDrm(drm)}
|
|
476
|
+
localSourceEncryptionKeyScheme={localSourceEncryptionKeyScheme}
|
|
477
|
+
adTagUrl={adTagUrl}
|
|
478
|
+
adLanguage={adLanguage}
|
|
479
|
+
controls={controls}
|
|
480
|
+
showNotificationControls={showNotificationControls}
|
|
481
|
+
disableFocus={disableFocus}
|
|
482
|
+
disableDisconnectError={disableDisconnectError}
|
|
483
|
+
focusable={focusable}
|
|
484
|
+
hideShutterView={hideShutterView}
|
|
485
|
+
preventsDisplaySleepDuringVideoPlayback={preventsDisplaySleepDuringVideoPlayback}
|
|
486
|
+
fullscreen={fullscreen}
|
|
487
|
+
fullscreenAutorotate={fullscreenAutorotate}
|
|
488
|
+
fullscreenOrientation={fullscreenOrientation}
|
|
489
|
+
pictureInPicture={pictureInPicture}
|
|
490
|
+
contentStartTime={contentStartTime}
|
|
491
|
+
allowsExternalPlayback={allowsExternalPlayback}
|
|
492
|
+
audioOutput={audioOutput}
|
|
493
|
+
ignoreSilentSwitch={ignoreSilentSwitch}
|
|
494
|
+
mixWithOthers={mixWithOthers}
|
|
495
|
+
enableDebug={debug?.enable}
|
|
496
|
+
enableDebugThread={debug?.thread}
|
|
497
|
+
onVideoLoadStart={handleLoadStart}
|
|
498
|
+
onVideoLoad={handleLoad}
|
|
499
|
+
onVideoError={handleError}
|
|
500
|
+
onVideoProgress={handleProgress}
|
|
501
|
+
onVideoSeek={handleSeek}
|
|
502
|
+
onVideoEnd={handleEnd}
|
|
503
|
+
onVideoBuffer={handleBuffer}
|
|
504
|
+
onVideoBandwidthUpdate={handleBandwidthUpdate}
|
|
505
|
+
onVideoPlaybackStateChanged={handlePlaybackStateChanged}
|
|
506
|
+
onVideoPlaybackRateChange={handlePlaybackRateChange}
|
|
507
|
+
onVideoVolumeChange={handleVolumeChange}
|
|
508
|
+
onVideoIdle={handleIdle}
|
|
509
|
+
onVideoReadyForDisplay={handleReadyForDisplay}
|
|
510
|
+
onVideoAudioFocusChanged={handleAudioFocusChanged}
|
|
511
|
+
onVideoAudioBecomingNoisy={handleAudioBecomingNoisy}
|
|
512
|
+
onVideoFullscreenPlayerWillPresent={handleFullscreenPlayerWillPresent}
|
|
513
|
+
onVideoFullscreenPlayerDidPresent={handleFullscreenPlayerDidPresent}
|
|
514
|
+
onVideoFullscreenPlayerWillDismiss={handleFullscreenPlayerWillDismiss}
|
|
515
|
+
onVideoFullscreenPlayerDidDismiss={handleFullscreenPlayerDidDismiss}
|
|
516
|
+
onVideoPictureInPictureStatusChanged={handlePictureInPictureStatusChanged}
|
|
517
|
+
onVideoRestoreUserInterfaceForPictureInPictureStop={handleRestoreUserInterfaceForPictureInPictureStop}
|
|
518
|
+
onVideoControlsVisibilityChange={handleControlsVisibilityChange}
|
|
519
|
+
onVideoExternalPlaybackChange={handleExternalPlaybackChange}
|
|
520
|
+
onVideoAspectRatio={handleAspectRatio}
|
|
521
|
+
onTransferEnd={handleTransferEnd}
|
|
522
|
+
/>
|
|
523
|
+
</View>
|
|
524
|
+
);
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
VideoBase.displayName = 'Video';
|
|
528
|
+
|
|
529
|
+
// Type for GraniteVideo with static properties
|
|
530
|
+
type VideoComponent = typeof VideoBase & {
|
|
531
|
+
isAvailable: boolean;
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// Static property to indicate availability
|
|
535
|
+
// For Fabric (New Architecture), the component is always available
|
|
536
|
+
(VideoBase as VideoComponent).isAvailable = true;
|
|
537
|
+
|
|
538
|
+
export const Video = VideoBase as VideoComponent;
|
|
539
|
+
|
|
540
|
+
const styles = StyleSheet.create({
|
|
541
|
+
container: {
|
|
542
|
+
overflow: 'hidden',
|
|
543
|
+
},
|
|
544
|
+
video: {
|
|
545
|
+
...StyleSheet.absoluteFillObject,
|
|
546
|
+
},
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
// === Static Methods ===
|
|
550
|
+
export async function clearCache(): Promise<void> {
|
|
551
|
+
if (GraniteVideoModule?.clearCache) {
|
|
552
|
+
return GraniteVideoModule.clearCache();
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export async function getWidevineLevel(): Promise<number> {
|
|
557
|
+
if (Platform.OS === 'android' && GraniteVideoModule?.getWidevineLevel) {
|
|
558
|
+
return GraniteVideoModule.getWidevineLevel();
|
|
559
|
+
}
|
|
560
|
+
return 0;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
export async function isCodecSupported(mimeType: string, width: number, height: number): Promise<boolean> {
|
|
564
|
+
if (GraniteVideoModule?.isCodecSupported) {
|
|
565
|
+
return GraniteVideoModule.isCodecSupported(mimeType, width, height);
|
|
566
|
+
}
|
|
567
|
+
return false;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
export async function isHEVCSupported(): Promise<boolean> {
|
|
571
|
+
if (GraniteVideoModule?.isHEVCSupported) {
|
|
572
|
+
return GraniteVideoModule.isHEVCSupported();
|
|
573
|
+
}
|
|
574
|
+
return false;
|
|
575
|
+
}
|