react-native-video-provider 0.2.5 → 0.2.6
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 +20 -2
- package/android/src/main/java/com/auvideo/PlayerCore.kt +22 -0
- package/lib/module/components/VideoControls.js +5 -9
- package/lib/module/components/VideoControls.js.map +1 -1
- package/lib/module/components/VideoPlayer.js +48 -2
- package/lib/module/components/VideoPlayer.js.map +1 -1
- package/lib/module/components/icons.js +32 -1
- package/lib/module/components/icons.js.map +1 -1
- package/lib/typescript/src/components/VideoControls.d.ts +12 -1
- package/lib/typescript/src/components/VideoControls.d.ts.map +1 -1
- package/lib/typescript/src/components/VideoPlayer.d.ts +21 -0
- package/lib/typescript/src/components/VideoPlayer.d.ts.map +1 -1
- package/lib/typescript/src/components/icons.d.ts +1 -0
- package/lib/typescript/src/components/icons.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/VideoControls.tsx +22 -9
- package/src/components/VideoPlayer.tsx +83 -3
- package/src/components/icons.tsx +35 -1
package/README.md
CHANGED
|
@@ -109,6 +109,14 @@ const { enter, toggle } = useFullscreen();
|
|
|
109
109
|
enter('landscape');
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
Set `fullscreenOrientation` to a **landscape** value and physically rotating
|
|
113
|
+
the device to landscape auto-enters fullscreen (rotating back to portrait
|
|
114
|
+
exits) — YouTube-style. Requires the app to allow landscape at the OS level:
|
|
115
|
+
|
|
116
|
+
```tsx
|
|
117
|
+
<VideoPlayer source={video} fullscreenOrientation="landscape" />
|
|
118
|
+
```
|
|
119
|
+
|
|
112
120
|
Or as a standing lock, independent of fullscreen:
|
|
113
121
|
|
|
114
122
|
```tsx
|
|
@@ -156,8 +164,18 @@ optional controls in one component. Handy props:
|
|
|
156
164
|
/>
|
|
157
165
|
```
|
|
158
166
|
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
Pass `live` for a live stream: the controls hide the seek bar/times and show
|
|
168
|
+
just mute + fullscreen. `liveIcon` renders a live indicator (e.g. a Lottie
|
|
169
|
+
badge), and `thumbnail` shows a poster over the video during the initial load:
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
<VideoPlayer
|
|
173
|
+
source={liveSource}
|
|
174
|
+
live
|
|
175
|
+
liveIcon={() => <LottieView source={liveAnim} autoPlay loop style={{ width: 44, height: 20 }} />}
|
|
176
|
+
thumbnail={() => <Image source={{ uri: poster }} style={StyleSheet.absoluteFill} resizeMode="cover" />}
|
|
177
|
+
/>
|
|
178
|
+
```
|
|
161
179
|
|
|
162
180
|
## Video feed (single engine, only the focused one plays)
|
|
163
181
|
|
|
@@ -284,9 +284,31 @@ object PlayerCore {
|
|
|
284
284
|
)
|
|
285
285
|
currentSurfaceId = surfaceId
|
|
286
286
|
pendingSurfaceId = null
|
|
287
|
+
reassertVideoOutput(pv, surfaceId, container)
|
|
287
288
|
listener?.onAttach(surfaceId)
|
|
288
289
|
}
|
|
289
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Re-parenting the PlayerView destroys and recreates its TextureView
|
|
293
|
+
* SurfaceTexture. VOD re-renders the last decoded frame onto the new
|
|
294
|
+
* surface immediately, but a LIVE stream (no buffered frame to re-render)
|
|
295
|
+
* can stay black. Re-binding the player to the view on the next frame
|
|
296
|
+
* forces the video output onto the fresh surface. No-op-safe for VOD.
|
|
297
|
+
*/
|
|
298
|
+
private fun reassertVideoOutput(
|
|
299
|
+
pv: PlayerView,
|
|
300
|
+
surfaceId: String,
|
|
301
|
+
container: AuVideoSurfaceView,
|
|
302
|
+
) {
|
|
303
|
+
mainHandler.post {
|
|
304
|
+
if (currentSurfaceId == surfaceId && pv.parent === container) {
|
|
305
|
+
val exo = player ?: return@post
|
|
306
|
+
pv.player = null
|
|
307
|
+
pv.player = exo
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
290
312
|
// --------------------------------------------------------------- events
|
|
291
313
|
|
|
292
314
|
private val playerListener = object : Player.Listener {
|
|
@@ -6,7 +6,7 @@ import { useVideoManager } from "../provider/VideoContext.js";
|
|
|
6
6
|
import { usePlayback } from "../hooks/usePlayback.js";
|
|
7
7
|
import { formatTime } from "../utils/formatTime.js";
|
|
8
8
|
import { GestureOverlay } from "./GestureOverlay.js";
|
|
9
|
-
import {
|
|
9
|
+
import { BackIcon } from "./icons.js";
|
|
10
10
|
import SvgIcons from "./SvgIcons.js";
|
|
11
11
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
12
12
|
/**
|
|
@@ -18,21 +18,17 @@ export function VideoControls({
|
|
|
18
18
|
doubleTapSeek = 10,
|
|
19
19
|
hideAfter = 3000,
|
|
20
20
|
showFullscreenButton = true,
|
|
21
|
+
live = false,
|
|
22
|
+
liveIcon,
|
|
21
23
|
onClose
|
|
22
24
|
}) {
|
|
23
25
|
const manager = useVideoManager();
|
|
24
|
-
const status = usePlayback(s => s.status);
|
|
25
26
|
const playing = usePlayback(s => s.playing);
|
|
26
27
|
const buffering = usePlayback(s => s.buffering);
|
|
27
|
-
const loading = usePlayback(s => s.loading);
|
|
28
28
|
const position = usePlayback(s => s.position);
|
|
29
29
|
const duration = usePlayback(s => s.duration);
|
|
30
30
|
const muted = usePlayback(s => s.muted);
|
|
31
31
|
const fullscreen = usePlayback(s => s.fullscreen);
|
|
32
|
-
|
|
33
|
-
// No known duration once loaded — a live stream or a video with no fixed
|
|
34
|
-
// end. There's nothing to seek, so hide the seek bar entirely.
|
|
35
|
-
const live = status !== 'idle' && !loading && duration <= 0;
|
|
36
32
|
const [visible, setVisible] = useState(true);
|
|
37
33
|
const hideTimer = useRef(null);
|
|
38
34
|
const trackWidth = useRef(0);
|
|
@@ -110,7 +106,7 @@ export function VideoControls({
|
|
|
110
106
|
style: styles.button,
|
|
111
107
|
onPress: onClose,
|
|
112
108
|
hitSlop: 8,
|
|
113
|
-
children: /*#__PURE__*/_jsx(
|
|
109
|
+
children: /*#__PURE__*/_jsx(BackIcon, {
|
|
114
110
|
size: 18,
|
|
115
111
|
color: "#fff"
|
|
116
112
|
})
|
|
@@ -139,7 +135,7 @@ export function VideoControls({
|
|
|
139
135
|
}), /*#__PURE__*/_jsx(View, {
|
|
140
136
|
style: styles.bottomRow,
|
|
141
137
|
children: live ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
142
|
-
children: [muteButton, /*#__PURE__*/_jsx(View, {
|
|
138
|
+
children: [liveIcon ? liveIcon() : null, muteButton, /*#__PURE__*/_jsx(View, {
|
|
143
139
|
style: styles.spacer
|
|
144
140
|
}), fullscreenButton]
|
|
145
141
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["useCallback","useEffect","useRef","useState","Pressable","StyleSheet","Text","View","useVideoManager","usePlayback","formatTime","GestureOverlay","
|
|
1
|
+
{"version":3,"names":["useCallback","useEffect","useRef","useState","Pressable","StyleSheet","Text","View","useVideoManager","usePlayback","formatTime","GestureOverlay","BackIcon","SvgIcons","jsx","_jsx","jsxs","_jsxs","Fragment","_Fragment","VideoControls","doubleTapSeek","hideAfter","showFullscreenButton","live","liveIcon","onClose","manager","playing","s","buffering","position","duration","muted","fullscreen","visible","setVisible","hideTimer","trackWidth","scheduleHide","current","clearTimeout","setTimeout","toggleVisible","v","onTrackLayout","e","nativeEvent","layout","width","onTrackPress","ratio","locationX","seek","progress","Math","min","muteButton","style","styles","button","onPress","unmute","mute","hitSlop","children","icon","size","fill","type","fullscreenButton","toggleFullscreen","absoluteFill","pointerEvents","onSingleTap","onDoubleTapLeft","seekBy","onDoubleTapRight","chrome","topRow","color","playButton","toggle","playIcon","bottomRow","spacer","time","track","onLayout","trackBg","trackFill","create","top","left","right","bottom","backgroundColor","justifyContent","flexDirection","padding","alignSelf","fontSize","alignItems","paddingHorizontal","paddingBottom","gap","fontVariant","flex","height","borderRadius"],"sourceRoot":"../../../src","sources":["components/VideoControls.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { forwardRef, useEffect, useImperativeHandle, useRef } from 'react';
|
|
4
|
-
import { AppState, StyleSheet, View } from 'react-native';
|
|
4
|
+
import { AppState, Dimensions, StyleSheet, View } from 'react-native';
|
|
5
5
|
import { useVideoManager } from "../provider/VideoContext.js";
|
|
6
|
+
import { usePlayback } from "../hooks/usePlayback.js";
|
|
6
7
|
import { useVideoEvents } from "../hooks/useVideoEvents.js";
|
|
7
8
|
import { VideoControls } from "./VideoControls.js";
|
|
8
9
|
import { VideoSurface } from "./VideoSurface.js";
|
|
@@ -32,6 +33,9 @@ export const VideoPlayer = /*#__PURE__*/forwardRef(({
|
|
|
32
33
|
fullscreenOrientation,
|
|
33
34
|
pauseOnFocusLost = true,
|
|
34
35
|
isFocused,
|
|
36
|
+
live = false,
|
|
37
|
+
liveIcon,
|
|
38
|
+
thumbnail,
|
|
35
39
|
onLoadComplete,
|
|
36
40
|
onBuffering,
|
|
37
41
|
onError,
|
|
@@ -41,6 +45,10 @@ export const VideoPlayer = /*#__PURE__*/forwardRef(({
|
|
|
41
45
|
const manager = useVideoManager();
|
|
42
46
|
const id = surfaceId ?? `player:${source.id}`;
|
|
43
47
|
|
|
48
|
+
// Poster is shown only during the initial load — `loading` is true from
|
|
49
|
+
// setSource until onLoad, and stays false for mid-stream buffering.
|
|
50
|
+
const loading = usePlayback(s => s.loading);
|
|
51
|
+
|
|
44
52
|
// Read the latest source without retriggering effects on every render
|
|
45
53
|
// (source is usually a fresh object literal each render).
|
|
46
54
|
const sourceRef = useRef(source);
|
|
@@ -92,6 +100,37 @@ export const VideoPlayer = /*#__PURE__*/forwardRef(({
|
|
|
92
100
|
manager.setFullscreenOrientation(fullscreenOrientation);
|
|
93
101
|
return () => manager.setFullscreenOrientation(null);
|
|
94
102
|
}, [manager, fullscreenOrientation]);
|
|
103
|
+
|
|
104
|
+
// Auto fullscreen on physical rotation (YouTube-style): when
|
|
105
|
+
// `fullscreenOrientation` is a landscape value, rotating the device to
|
|
106
|
+
// landscape enters fullscreen and rotating back to portrait exits.
|
|
107
|
+
// Enters with 'auto' (not a forced lock) so the device sensor keeps
|
|
108
|
+
// driving and rotating back can exit. Requires the app to actually allow
|
|
109
|
+
// landscape (Info.plist / AndroidManifest) — otherwise the window never
|
|
110
|
+
// reports landscape and this stays inert.
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
const wantsLandscape = fullscreenOrientation === 'landscape' || fullscreenOrientation === 'inverted-landscape';
|
|
113
|
+
if (!wantsLandscape) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
const onChange = ({
|
|
117
|
+
window
|
|
118
|
+
}) => {
|
|
119
|
+
const landscape = window.width > window.height;
|
|
120
|
+
const state = manager.store.getState();
|
|
121
|
+
// Ignore when another video owns the engine and we're not fullscreen.
|
|
122
|
+
if (state.surfaceId !== id && !state.fullscreen) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (landscape && !state.fullscreen) {
|
|
126
|
+
manager.enterFullscreen('auto');
|
|
127
|
+
} else if (!landscape && state.fullscreen) {
|
|
128
|
+
manager.exitFullscreen();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
const sub = Dimensions.addEventListener('change', onChange);
|
|
132
|
+
return () => sub.remove();
|
|
133
|
+
}, [manager, id, fullscreenOrientation]);
|
|
95
134
|
useEffect(() => {
|
|
96
135
|
if (!pauseOnFocusLost) {
|
|
97
136
|
return;
|
|
@@ -139,7 +178,14 @@ export const VideoPlayer = /*#__PURE__*/forwardRef(({
|
|
|
139
178
|
children: [/*#__PURE__*/_jsx(VideoSurface, {
|
|
140
179
|
surfaceId: id,
|
|
141
180
|
style: styles.surface
|
|
142
|
-
}),
|
|
181
|
+
}), thumbnail && loading ? /*#__PURE__*/_jsx(View, {
|
|
182
|
+
style: styles.surface,
|
|
183
|
+
pointerEvents: "none",
|
|
184
|
+
children: thumbnail()
|
|
185
|
+
}) : null, controls ? /*#__PURE__*/_jsx(VideoControls, {
|
|
186
|
+
live: live,
|
|
187
|
+
liveIcon: liveIcon
|
|
188
|
+
}) : null]
|
|
143
189
|
});
|
|
144
190
|
});
|
|
145
191
|
VideoPlayer.displayName = 'VideoPlayer';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["forwardRef","useEffect","useImperativeHandle","useRef","AppState","StyleSheet","View","useVideoManager","useVideoEvents","VideoControls","VideoSurface","jsx","_jsx","jsxs","_jsxs","VideoPlayer","source","autoplay","surfaceId","controls","resizeMode","repeat","muted","orientation","fullscreenOrientation","pauseOnFocusLost","isFocused","onLoadComplete","onBuffering","onError","style","rest","ref","manager","id","sourceRef","current","setSource","setResizeMode","undefined","setRepeat","mute","unmute","setOrientation","setFullscreenOrientation","
|
|
1
|
+
{"version":3,"names":["forwardRef","useEffect","useImperativeHandle","useRef","AppState","Dimensions","StyleSheet","View","useVideoManager","usePlayback","useVideoEvents","VideoControls","VideoSurface","jsx","_jsx","jsxs","_jsxs","VideoPlayer","source","autoplay","surfaceId","controls","resizeMode","repeat","muted","orientation","fullscreenOrientation","pauseOnFocusLost","isFocused","live","liveIcon","thumbnail","onLoadComplete","onBuffering","onError","style","rest","ref","manager","id","loading","s","sourceRef","current","setSource","setResizeMode","undefined","setRepeat","mute","unmute","setOrientation","setFullscreenOrientation","wantsLandscape","onChange","window","landscape","width","height","state","store","getState","fullscreen","enterFullscreen","exitFullscreen","sub","addEventListener","remove","next","pause","currentVideo","play","onLoad","onBuffer","e","buffering","styles","container","children","surface","pointerEvents","displayName","create","backgroundColor","overflow","position","top","left","right","bottom"],"sourceRoot":"../../../src","sources":["components/VideoPlayer.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import Svg, { Line, Path, Polygon, Rect } from 'react-native-svg';
|
|
3
|
+
import Svg, { ClipPath, Defs, G, Line, Path, Polygon, Rect } from 'react-native-svg';
|
|
4
4
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
5
|
export function PlayIcon({
|
|
6
6
|
size,
|
|
@@ -41,6 +41,37 @@ export function PauseIcon({
|
|
|
41
41
|
export function CloseIcon({
|
|
42
42
|
size,
|
|
43
43
|
color
|
|
44
|
+
}) {
|
|
45
|
+
return /*#__PURE__*/_jsxs(Svg, {
|
|
46
|
+
width: size || 20,
|
|
47
|
+
height: size || 21,
|
|
48
|
+
viewBox: "0 0 20 21",
|
|
49
|
+
fill: "none",
|
|
50
|
+
children: [/*#__PURE__*/_jsx(G, {
|
|
51
|
+
clipPath: "url(#clip0_25_6005)",
|
|
52
|
+
children: /*#__PURE__*/_jsx(Path, {
|
|
53
|
+
d: "M12.5 16.3069L6.25 10.0569L12.5 3.80688",
|
|
54
|
+
stroke: color || '#000000',
|
|
55
|
+
strokeWidth: 1.25,
|
|
56
|
+
strokeLinecap: "round",
|
|
57
|
+
strokeLinejoin: "round"
|
|
58
|
+
})
|
|
59
|
+
}), /*#__PURE__*/_jsx(Defs, {
|
|
60
|
+
children: /*#__PURE__*/_jsx(ClipPath, {
|
|
61
|
+
id: "clip0_25_6005",
|
|
62
|
+
children: /*#__PURE__*/_jsx(Rect, {
|
|
63
|
+
width: size || 20,
|
|
64
|
+
height: size || 21,
|
|
65
|
+
fill: "white",
|
|
66
|
+
transform: "translate(0 0.0568848)"
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
})]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export function BackIcon({
|
|
73
|
+
size,
|
|
74
|
+
color
|
|
44
75
|
}) {
|
|
45
76
|
return /*#__PURE__*/_jsxs(Svg, {
|
|
46
77
|
width: size,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["Svg","Line","Path","Polygon","Rect","jsx","_jsx","jsxs","_jsxs","PlayIcon","size","color","width","height","viewBox","fill","children","points","PauseIcon","x","y","CloseIcon","stroke","strokeWidth","strokeLinecap","
|
|
1
|
+
{"version":3,"names":["Svg","ClipPath","Defs","G","Line","Path","Polygon","Rect","jsx","_jsx","jsxs","_jsxs","PlayIcon","size","color","width","height","viewBox","fill","children","points","PauseIcon","x","y","CloseIcon","clipPath","d","stroke","strokeWidth","strokeLinecap","strokeLinejoin","id","transform","BackIcon","x1","y1","x2","y2","VolumeOnIcon","VolumeOffIcon","EnterFullscreenIcon","ExitFullscreenIcon"],"sourceRoot":"../../../src","sources":["components/icons.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
export interface VideoControlsProps {
|
|
2
3
|
/** Seconds jumped by double-tap. Default 10. */
|
|
3
4
|
doubleTapSeek?: number;
|
|
@@ -5,6 +6,16 @@ export interface VideoControlsProps {
|
|
|
5
6
|
hideAfter?: number;
|
|
6
7
|
/** Show the fullscreen toggle button. Default true. */
|
|
7
8
|
showFullscreenButton?: boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Mark this as a live stream: hides the seek bar/times and moves mute to
|
|
11
|
+
* the bottom-left. Default false.
|
|
12
|
+
*/
|
|
13
|
+
live?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Render a live indicator (e.g. a Lottie animation or a "LIVE" badge),
|
|
16
|
+
* shown in the control bar only while `live`.
|
|
17
|
+
*/
|
|
18
|
+
liveIcon?: () => ReactNode;
|
|
8
19
|
/** Called by the close (✕) button; button hidden when omitted. */
|
|
9
20
|
onClose?: () => void;
|
|
10
21
|
}
|
|
@@ -13,5 +24,5 @@ export interface VideoControlsProps {
|
|
|
13
24
|
* toggles, with tap-to-show / double-tap-to-seek gestures. Apps wanting a
|
|
14
25
|
* custom design can ignore this and build on usePlayback()/useVideo().
|
|
15
26
|
*/
|
|
16
|
-
export declare function VideoControls({ doubleTapSeek, hideAfter, showFullscreenButton, onClose, }: VideoControlsProps): import("react").JSX.Element;
|
|
27
|
+
export declare function VideoControls({ doubleTapSeek, hideAfter, showFullscreenButton, live, liveIcon, onClose, }: VideoControlsProps): import("react").JSX.Element;
|
|
17
28
|
//# sourceMappingURL=VideoControls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoControls.d.ts","sourceRoot":"","sources":["../../../../src/components/VideoControls.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VideoControls.d.ts","sourceRoot":"","sources":["../../../../src/components/VideoControls.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAgBf,MAAM,WAAW,kBAAkB;IACjC,gDAAgD;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,uDAAuD;IACvD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,SAAS,CAAC;IAC3B,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,aAAkB,EAClB,SAAgB,EAChB,oBAA2B,EAC3B,IAAY,EACZ,QAAQ,EACR,OAAO,GACR,EAAE,kBAAkB,+BAiJpB"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
1
2
|
import { type ViewProps } from 'react-native';
|
|
2
3
|
import { VideoManager } from '../core/VideoManager.js';
|
|
3
4
|
import type { VideoEventMap } from '../types/events.js';
|
|
@@ -30,6 +31,11 @@ export interface VideoPlayerProps extends ViewProps {
|
|
|
30
31
|
* `'portrait'` for a vertical video). Applied when fullscreen opens —
|
|
31
32
|
* including via the built-in controls' fullscreen button — and restored
|
|
32
33
|
* when it closes, so the rest of the app is unaffected.
|
|
34
|
+
*
|
|
35
|
+
* When set to a landscape value (`'landscape'` / `'inverted-landscape'`),
|
|
36
|
+
* physically rotating the device to landscape also auto-enters fullscreen,
|
|
37
|
+
* and rotating back to portrait exits it (YouTube-style). This needs the
|
|
38
|
+
* app to allow landscape orientations at the OS level.
|
|
33
39
|
*/
|
|
34
40
|
fullscreenOrientation?: OrientationLock;
|
|
35
41
|
/**
|
|
@@ -53,6 +59,21 @@ export interface VideoPlayerProps extends ViewProps {
|
|
|
53
59
|
* ```
|
|
54
60
|
*/
|
|
55
61
|
isFocused?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Mark this as a live stream: the built-in controls hide the seek bar/times
|
|
64
|
+
* and show `liveIcon` (if given). Default false.
|
|
65
|
+
*/
|
|
66
|
+
live?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Render a live indicator shown in the controls while `live` — e.g. a
|
|
69
|
+
* Lottie animation or a "LIVE" badge: `liveIcon={() => <LottieView … />}`.
|
|
70
|
+
*/
|
|
71
|
+
liveIcon?: () => ReactNode;
|
|
72
|
+
/**
|
|
73
|
+
* Render a poster shown over the video only during the initial load
|
|
74
|
+
* (before the first frame) — e.g. `thumbnail={() => <Image … />}`.
|
|
75
|
+
*/
|
|
76
|
+
thumbnail?: () => ReactNode;
|
|
56
77
|
/** Fires once metadata (duration, dimensions) is available. */
|
|
57
78
|
onLoadComplete?: (info: VideoEventMap['onLoad']) => void;
|
|
58
79
|
/** Fires whenever buffering starts or stops. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../../src/components/VideoPlayer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"VideoPlayer.d.ts","sourceRoot":"","sources":["../../../../src/components/VideoPlayer.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAsB,CAAC;AAIpD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAiB,CAAC;AACrD,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,UAAU,EACV,WAAW,EACZ,MAAM,mBAAgB,CAAC;AAIxB,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,MAAM,EAAE,WAAW,CAAC;IACpB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,iDAAiD;IACjD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,qBAAqB;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B;;;;;;;;;;OAUG;IACH,qBAAqB,CAAC,EAAE,eAAe,CAAC;IACxC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;;;;;;;;;;OAYG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,SAAS,CAAC;IAC3B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,SAAS,CAAC;IAC5B,+DAA+D;IAC/D,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC;IACzD,gDAAgD;IAChD,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;IAC3C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,WAAW,2GAyLvB,CAAC"}
|
|
@@ -5,6 +5,7 @@ interface IconProps {
|
|
|
5
5
|
export declare function PlayIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
6
6
|
export declare function PauseIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
7
7
|
export declare function CloseIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
8
|
+
export declare function BackIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
8
9
|
export declare function VolumeOnIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
9
10
|
export declare function VolumeOffIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
10
11
|
export declare function EnterFullscreenIcon({ size, color }: IconProps): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../../src/components/icons.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"icons.d.ts","sourceRoot":"","sources":["../../../../src/components/icons.tsx"],"names":[],"mappings":"AAUA,UAAU,SAAS;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAMlD;AAED,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAOnD;AAED,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAwBnD;AAED,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAelD;AAED,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAgBtD;AAED,wBAAgB,aAAa,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAiBvD;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAkB7D;AAED,wBAAgB,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,SAAS,+BAkB5D"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-video-provider",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Singleton-engine video library for React Native (one native player, many surfaces)",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
useCallback,
|
|
3
|
+
useEffect,
|
|
4
|
+
useRef,
|
|
5
|
+
useState,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from 'react';
|
|
2
8
|
import {
|
|
3
9
|
Pressable,
|
|
4
10
|
StyleSheet,
|
|
@@ -11,7 +17,7 @@ import { useVideoManager } from '../provider/VideoContext';
|
|
|
11
17
|
import { usePlayback } from '../hooks/usePlayback';
|
|
12
18
|
import { formatTime } from '../utils/formatTime';
|
|
13
19
|
import { GestureOverlay } from './GestureOverlay';
|
|
14
|
-
import {
|
|
20
|
+
import { BackIcon } from './icons';
|
|
15
21
|
import SvgIcons from './SvgIcons';
|
|
16
22
|
|
|
17
23
|
export interface VideoControlsProps {
|
|
@@ -21,6 +27,16 @@ export interface VideoControlsProps {
|
|
|
21
27
|
hideAfter?: number;
|
|
22
28
|
/** Show the fullscreen toggle button. Default true. */
|
|
23
29
|
showFullscreenButton?: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Mark this as a live stream: hides the seek bar/times and moves mute to
|
|
32
|
+
* the bottom-left. Default false.
|
|
33
|
+
*/
|
|
34
|
+
live?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Render a live indicator (e.g. a Lottie animation or a "LIVE" badge),
|
|
37
|
+
* shown in the control bar only while `live`.
|
|
38
|
+
*/
|
|
39
|
+
liveIcon?: () => ReactNode;
|
|
24
40
|
/** Called by the close (✕) button; button hidden when omitted. */
|
|
25
41
|
onClose?: () => void;
|
|
26
42
|
}
|
|
@@ -34,22 +50,18 @@ export function VideoControls({
|
|
|
34
50
|
doubleTapSeek = 10,
|
|
35
51
|
hideAfter = 3000,
|
|
36
52
|
showFullscreenButton = true,
|
|
53
|
+
live = false,
|
|
54
|
+
liveIcon,
|
|
37
55
|
onClose,
|
|
38
56
|
}: VideoControlsProps) {
|
|
39
57
|
const manager = useVideoManager();
|
|
40
|
-
const status = usePlayback((s) => s.status);
|
|
41
58
|
const playing = usePlayback((s) => s.playing);
|
|
42
59
|
const buffering = usePlayback((s) => s.buffering);
|
|
43
|
-
const loading = usePlayback((s) => s.loading);
|
|
44
60
|
const position = usePlayback((s) => s.position);
|
|
45
61
|
const duration = usePlayback((s) => s.duration);
|
|
46
62
|
const muted = usePlayback((s) => s.muted);
|
|
47
63
|
const fullscreen = usePlayback((s) => s.fullscreen);
|
|
48
64
|
|
|
49
|
-
// No known duration once loaded — a live stream or a video with no fixed
|
|
50
|
-
// end. There's nothing to seek, so hide the seek bar entirely.
|
|
51
|
-
const live = status !== 'idle' && !loading && duration <= 0;
|
|
52
|
-
|
|
53
65
|
const [visible, setVisible] = useState(true);
|
|
54
66
|
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
55
67
|
const trackWidth = useRef(0);
|
|
@@ -131,7 +143,7 @@ export function VideoControls({
|
|
|
131
143
|
<View style={styles.topRow}>
|
|
132
144
|
{onClose ? (
|
|
133
145
|
<Pressable style={styles.button} onPress={onClose} hitSlop={8}>
|
|
134
|
-
<
|
|
146
|
+
<BackIcon size={18} color="#fff" />
|
|
135
147
|
</Pressable>
|
|
136
148
|
) : (
|
|
137
149
|
<View />
|
|
@@ -159,6 +171,7 @@ export function VideoControls({
|
|
|
159
171
|
<View style={styles.bottomRow}>
|
|
160
172
|
{live ? (
|
|
161
173
|
<>
|
|
174
|
+
{liveIcon ? liveIcon() : null}
|
|
162
175
|
{muteButton}
|
|
163
176
|
<View style={styles.spacer} />
|
|
164
177
|
{fullscreenButton}
|
|
@@ -1,7 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useEffect,
|
|
4
|
+
useImperativeHandle,
|
|
5
|
+
useRef,
|
|
6
|
+
type ReactNode,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import {
|
|
9
|
+
AppState,
|
|
10
|
+
Dimensions,
|
|
11
|
+
StyleSheet,
|
|
12
|
+
View,
|
|
13
|
+
type ViewProps,
|
|
14
|
+
} from 'react-native';
|
|
3
15
|
import { VideoManager } from '../core/VideoManager';
|
|
4
16
|
import { useVideoManager } from '../provider/VideoContext';
|
|
17
|
+
import { usePlayback } from '../hooks/usePlayback';
|
|
5
18
|
import { useVideoEvents } from '../hooks/useVideoEvents';
|
|
6
19
|
import type { VideoEventMap } from '../types/events';
|
|
7
20
|
import type {
|
|
@@ -41,6 +54,11 @@ export interface VideoPlayerProps extends ViewProps {
|
|
|
41
54
|
* `'portrait'` for a vertical video). Applied when fullscreen opens —
|
|
42
55
|
* including via the built-in controls' fullscreen button — and restored
|
|
43
56
|
* when it closes, so the rest of the app is unaffected.
|
|
57
|
+
*
|
|
58
|
+
* When set to a landscape value (`'landscape'` / `'inverted-landscape'`),
|
|
59
|
+
* physically rotating the device to landscape also auto-enters fullscreen,
|
|
60
|
+
* and rotating back to portrait exits it (YouTube-style). This needs the
|
|
61
|
+
* app to allow landscape orientations at the OS level.
|
|
44
62
|
*/
|
|
45
63
|
fullscreenOrientation?: OrientationLock;
|
|
46
64
|
/**
|
|
@@ -64,6 +82,21 @@ export interface VideoPlayerProps extends ViewProps {
|
|
|
64
82
|
* ```
|
|
65
83
|
*/
|
|
66
84
|
isFocused?: boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Mark this as a live stream: the built-in controls hide the seek bar/times
|
|
87
|
+
* and show `liveIcon` (if given). Default false.
|
|
88
|
+
*/
|
|
89
|
+
live?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Render a live indicator shown in the controls while `live` — e.g. a
|
|
92
|
+
* Lottie animation or a "LIVE" badge: `liveIcon={() => <LottieView … />}`.
|
|
93
|
+
*/
|
|
94
|
+
liveIcon?: () => ReactNode;
|
|
95
|
+
/**
|
|
96
|
+
* Render a poster shown over the video only during the initial load
|
|
97
|
+
* (before the first frame) — e.g. `thumbnail={() => <Image … />}`.
|
|
98
|
+
*/
|
|
99
|
+
thumbnail?: () => ReactNode;
|
|
67
100
|
/** Fires once metadata (duration, dimensions) is available. */
|
|
68
101
|
onLoadComplete?: (info: VideoEventMap['onLoad']) => void;
|
|
69
102
|
/** Fires whenever buffering starts or stops. */
|
|
@@ -98,6 +131,9 @@ export const VideoPlayer = forwardRef<VideoManager, VideoPlayerProps>(
|
|
|
98
131
|
fullscreenOrientation,
|
|
99
132
|
pauseOnFocusLost = true,
|
|
100
133
|
isFocused,
|
|
134
|
+
live = false,
|
|
135
|
+
liveIcon,
|
|
136
|
+
thumbnail,
|
|
101
137
|
onLoadComplete,
|
|
102
138
|
onBuffering,
|
|
103
139
|
onError,
|
|
@@ -109,6 +145,10 @@ export const VideoPlayer = forwardRef<VideoManager, VideoPlayerProps>(
|
|
|
109
145
|
const manager = useVideoManager();
|
|
110
146
|
const id = surfaceId ?? `player:${source.id}`;
|
|
111
147
|
|
|
148
|
+
// Poster is shown only during the initial load — `loading` is true from
|
|
149
|
+
// setSource until onLoad, and stays false for mid-stream buffering.
|
|
150
|
+
const loading = usePlayback((s) => s.loading);
|
|
151
|
+
|
|
112
152
|
// Read the latest source without retriggering effects on every render
|
|
113
153
|
// (source is usually a fresh object literal each render).
|
|
114
154
|
const sourceRef = useRef(source);
|
|
@@ -168,6 +208,41 @@ export const VideoPlayer = forwardRef<VideoManager, VideoPlayerProps>(
|
|
|
168
208
|
return () => manager.setFullscreenOrientation(null);
|
|
169
209
|
}, [manager, fullscreenOrientation]);
|
|
170
210
|
|
|
211
|
+
// Auto fullscreen on physical rotation (YouTube-style): when
|
|
212
|
+
// `fullscreenOrientation` is a landscape value, rotating the device to
|
|
213
|
+
// landscape enters fullscreen and rotating back to portrait exits.
|
|
214
|
+
// Enters with 'auto' (not a forced lock) so the device sensor keeps
|
|
215
|
+
// driving and rotating back can exit. Requires the app to actually allow
|
|
216
|
+
// landscape (Info.plist / AndroidManifest) — otherwise the window never
|
|
217
|
+
// reports landscape and this stays inert.
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
const wantsLandscape =
|
|
220
|
+
fullscreenOrientation === 'landscape' ||
|
|
221
|
+
fullscreenOrientation === 'inverted-landscape';
|
|
222
|
+
if (!wantsLandscape) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const onChange = ({
|
|
226
|
+
window,
|
|
227
|
+
}: {
|
|
228
|
+
window: { width: number; height: number };
|
|
229
|
+
}) => {
|
|
230
|
+
const landscape = window.width > window.height;
|
|
231
|
+
const state = manager.store.getState();
|
|
232
|
+
// Ignore when another video owns the engine and we're not fullscreen.
|
|
233
|
+
if (state.surfaceId !== id && !state.fullscreen) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
if (landscape && !state.fullscreen) {
|
|
237
|
+
manager.enterFullscreen('auto');
|
|
238
|
+
} else if (!landscape && state.fullscreen) {
|
|
239
|
+
manager.exitFullscreen();
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const sub = Dimensions.addEventListener('change', onChange);
|
|
243
|
+
return () => sub.remove();
|
|
244
|
+
}, [manager, id, fullscreenOrientation]);
|
|
245
|
+
|
|
171
246
|
useEffect(() => {
|
|
172
247
|
if (!pauseOnFocusLost) {
|
|
173
248
|
return;
|
|
@@ -218,7 +293,12 @@ export const VideoPlayer = forwardRef<VideoManager, VideoPlayerProps>(
|
|
|
218
293
|
return (
|
|
219
294
|
<View style={[styles.container, style]} {...rest}>
|
|
220
295
|
<VideoSurface surfaceId={id} style={styles.surface} />
|
|
221
|
-
{
|
|
296
|
+
{thumbnail && loading ? (
|
|
297
|
+
<View style={styles.surface} pointerEvents="none">
|
|
298
|
+
{thumbnail()}
|
|
299
|
+
</View>
|
|
300
|
+
) : null}
|
|
301
|
+
{controls ? <VideoControls live={live} liveIcon={liveIcon} /> : null}
|
|
222
302
|
</View>
|
|
223
303
|
);
|
|
224
304
|
}
|
package/src/components/icons.tsx
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import Svg, {
|
|
1
|
+
import Svg, {
|
|
2
|
+
ClipPath,
|
|
3
|
+
Defs,
|
|
4
|
+
G,
|
|
5
|
+
Line,
|
|
6
|
+
Path,
|
|
7
|
+
Polygon,
|
|
8
|
+
Rect,
|
|
9
|
+
} from 'react-native-svg';
|
|
2
10
|
|
|
3
11
|
interface IconProps {
|
|
4
12
|
size: number;
|
|
@@ -23,6 +31,32 @@ export function PauseIcon({ size, color }: IconProps) {
|
|
|
23
31
|
}
|
|
24
32
|
|
|
25
33
|
export function CloseIcon({ size, color }: IconProps) {
|
|
34
|
+
return (
|
|
35
|
+
<Svg width={size || 20} height={size || 21} viewBox="0 0 20 21" fill="none">
|
|
36
|
+
<G clipPath="url(#clip0_25_6005)">
|
|
37
|
+
<Path
|
|
38
|
+
d="M12.5 16.3069L6.25 10.0569L12.5 3.80688"
|
|
39
|
+
stroke={color || '#000000'}
|
|
40
|
+
strokeWidth={1.25}
|
|
41
|
+
strokeLinecap="round"
|
|
42
|
+
strokeLinejoin="round"
|
|
43
|
+
/>
|
|
44
|
+
</G>
|
|
45
|
+
<Defs>
|
|
46
|
+
<ClipPath id="clip0_25_6005">
|
|
47
|
+
<Rect
|
|
48
|
+
width={size || 20}
|
|
49
|
+
height={size || 21}
|
|
50
|
+
fill="white"
|
|
51
|
+
transform="translate(0 0.0568848)"
|
|
52
|
+
/>
|
|
53
|
+
</ClipPath>
|
|
54
|
+
</Defs>
|
|
55
|
+
</Svg>
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function BackIcon({ size, color }: IconProps) {
|
|
26
60
|
return (
|
|
27
61
|
<Svg
|
|
28
62
|
width={size}
|