UNPKG

12.4 kBJavaScriptView Raw
1import React, {Component} from 'react';
2import PropTypes from 'prop-types';
3import {StyleSheet, requireNativeComponent, NativeModules, View, ViewPropTypes, Image, Platform, findNodeHandle} from 'react-native';
4import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
5import TextTrackType from './TextTrackType';
6import FilterType from './FilterType';
7import VideoResizeMode from './VideoResizeMode.js';
8
9const styles = StyleSheet.create({
10 base: {
11 overflow: 'hidden',
12 },
13});
14
15export { TextTrackType, FilterType };
16
17export default class Video extends Component {
18
19 constructor(props) {
20 super(props);
21
22 this.state = {
23 showPoster: true,
24 };
25 }
26
27 setNativeProps(nativeProps) {
28 this._root.setNativeProps(nativeProps);
29 }
30
31 toTypeString(x) {
32 switch (typeof x) {
33 case "object":
34 return x instanceof Date
35 ? x.toISOString()
36 : JSON.stringify(x); // object, null
37 case "undefined":
38 return "";
39 default: // boolean, number, string
40 return x.toString();
41 }
42 }
43
44 stringsOnlyObject(obj) {
45 const strObj = {};
46
47 Object.keys(obj).forEach(x => {
48 strObj[x] = this.toTypeString(obj[x]);
49 });
50
51 return strObj;
52 }
53
54 seek = (time, tolerance = 100) => {
55 if (isNaN(time)) throw new Error('Specified time is not a number');
56
57 if (Platform.OS === 'ios') {
58 this.setNativeProps({
59 seek: {
60 time,
61 tolerance
62 }
63 });
64 } else {
65 this.setNativeProps({ seek: time });
66 }
67 };
68
69 presentFullscreenPlayer = () => {
70 this.setNativeProps({ fullscreen: true });
71 };
72
73 dismissFullscreenPlayer = () => {
74 this.setNativeProps({ fullscreen: false });
75 };
76
77 save = async (options?) => {
78 return await NativeModules.VideoManager.save(options, findNodeHandle(this._root));
79 }
80
81 _assignRoot = (component) => {
82 this._root = component;
83 };
84
85 _onLoadStart = (event) => {
86 if (this.props.onLoadStart) {
87 this.props.onLoadStart(event.nativeEvent);
88 }
89 };
90
91 _onLoad = (event) => {
92 if (this.props.onLoad) {
93 this.props.onLoad(event.nativeEvent);
94 }
95 };
96
97 _onError = (event) => {
98 if (this.props.onError) {
99 this.props.onError(event.nativeEvent);
100 }
101 };
102
103 _onProgress = (event) => {
104 if (this.props.onProgress) {
105 this.props.onProgress(event.nativeEvent);
106 }
107 };
108
109 _onBandwidthUpdate = (event) => {
110 if (this.props.onBandwidthUpdate) {
111 this.props.onBandwidthUpdate(event.nativeEvent);
112 }
113 };
114
115 _onSeek = (event) => {
116 if (this.state.showPoster && !this.props.audioOnly) {
117 this.setState({showPoster: false});
118 }
119
120 if (this.props.onSeek) {
121 this.props.onSeek(event.nativeEvent);
122 }
123 };
124
125 _onEnd = (event) => {
126 if (this.props.onEnd) {
127 this.props.onEnd(event.nativeEvent);
128 }
129 };
130
131 _onTimedMetadata = (event) => {
132 if (this.props.onTimedMetadata) {
133 this.props.onTimedMetadata(event.nativeEvent);
134 }
135 };
136
137 _onFullscreenPlayerWillPresent = (event) => {
138 if (this.props.onFullscreenPlayerWillPresent) {
139 this.props.onFullscreenPlayerWillPresent(event.nativeEvent);
140 }
141 };
142
143 _onFullscreenPlayerDidPresent = (event) => {
144 if (this.props.onFullscreenPlayerDidPresent) {
145 this.props.onFullscreenPlayerDidPresent(event.nativeEvent);
146 }
147 };
148
149 _onFullscreenPlayerWillDismiss = (event) => {
150 if (this.props.onFullscreenPlayerWillDismiss) {
151 this.props.onFullscreenPlayerWillDismiss(event.nativeEvent);
152 }
153 };
154
155 _onFullscreenPlayerDidDismiss = (event) => {
156 if (this.props.onFullscreenPlayerDidDismiss) {
157 this.props.onFullscreenPlayerDidDismiss(event.nativeEvent);
158 }
159 };
160
161 _onReadyForDisplay = (event) => {
162 if (this.props.onReadyForDisplay) {
163 this.props.onReadyForDisplay(event.nativeEvent);
164 }
165 };
166
167 _onPlaybackStalled = (event) => {
168 if (this.props.onPlaybackStalled) {
169 this.props.onPlaybackStalled(event.nativeEvent);
170 }
171 };
172
173 _onPlaybackResume = (event) => {
174 if (this.props.onPlaybackResume) {
175 this.props.onPlaybackResume(event.nativeEvent);
176 }
177 };
178
179 _onPlaybackRateChange = (event) => {
180 if (this.state.showPoster && event.nativeEvent.playbackRate !== 0 && !this.props.audioOnly) {
181 this.setState({showPoster: false});
182 }
183
184 if (this.props.onPlaybackRateChange) {
185 this.props.onPlaybackRateChange(event.nativeEvent);
186 }
187 };
188
189 _onExternalPlaybackChange = (event) => {
190 if (this.props.onExternalPlaybackChange) {
191 this.props.onExternalPlaybackChange(event.nativeEvent);
192 }
193 }
194
195 _onAudioBecomingNoisy = () => {
196 if (this.props.onAudioBecomingNoisy) {
197 this.props.onAudioBecomingNoisy();
198 }
199 };
200
201 _onAudioFocusChanged = (event) => {
202 if (this.props.onAudioFocusChanged) {
203 this.props.onAudioFocusChanged(event.nativeEvent);
204 }
205 };
206
207 _onBuffer = (event) => {
208 if (this.props.onBuffer) {
209 this.props.onBuffer(event.nativeEvent);
210 }
211 };
212
213 render() {
214 const resizeMode = this.props.resizeMode;
215 const source = resolveAssetSource(this.props.source) || {};
216
217 let uri = source.uri || '';
218 if (uri && uri.match(/^\//)) {
219 uri = `file://${uri}`;
220 }
221
222 const isNetwork = !!(uri && uri.match(/^https?:/));
223 const isAsset = !!(uri && uri.match(/^(assets-library|ipod-library|file|content|ms-appx|ms-appdata):/));
224
225 let nativeResizeMode;
226 if (resizeMode === VideoResizeMode.stretch) {
227 nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleToFill;
228 } else if (resizeMode === VideoResizeMode.contain) {
229 nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleAspectFit;
230 } else if (resizeMode === VideoResizeMode.cover) {
231 nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleAspectFill;
232 } else {
233 nativeResizeMode = NativeModules.UIManager.RCTVideo.Constants.ScaleNone;
234 }
235
236 const nativeProps = Object.assign({}, this.props);
237 Object.assign(nativeProps, {
238 style: [styles.base, nativeProps.style],
239 resizeMode: nativeResizeMode,
240 src: {
241 uri,
242 isNetwork,
243 isAsset,
244 type: source.type || '',
245 mainVer: source.mainVer || 0,
246 patchVer: source.patchVer || 0,
247 requestHeaders: source.headers ? this.stringsOnlyObject(source.headers) : {}
248 },
249 onVideoLoadStart: this._onLoadStart,
250 onVideoLoad: this._onLoad,
251 onVideoError: this._onError,
252 onVideoProgress: this._onProgress,
253 onVideoSeek: this._onSeek,
254 onVideoEnd: this._onEnd,
255 onVideoBuffer: this._onBuffer,
256 onVideoBandwidthUpdate: this._onBandwidthUpdate,
257 onTimedMetadata: this._onTimedMetadata,
258 onVideoAudioBecomingNoisy: this._onAudioBecomingNoisy,
259 onVideoExternalPlaybackChange: this._onExternalPlaybackChange,
260 onVideoFullscreenPlayerWillPresent: this._onFullscreenPlayerWillPresent,
261 onVideoFullscreenPlayerDidPresent: this._onFullscreenPlayerDidPresent,
262 onVideoFullscreenPlayerWillDismiss: this._onFullscreenPlayerWillDismiss,
263 onVideoFullscreenPlayerDidDismiss: this._onFullscreenPlayerDidDismiss,
264 onReadyForDisplay: this._onReadyForDisplay,
265 onPlaybackStalled: this._onPlaybackStalled,
266 onPlaybackResume: this._onPlaybackResume,
267 onPlaybackRateChange: this._onPlaybackRateChange,
268 onAudioFocusChanged: this._onAudioFocusChanged,
269 onAudioBecomingNoisy: this._onAudioBecomingNoisy,
270 });
271
272 const posterStyle = {
273 ...StyleSheet.absoluteFillObject,
274 resizeMode: this.props.posterResizeMode || 'contain',
275 };
276
277 return (
278 <React.Fragment>
279 <RCTVideo ref={this._assignRoot} {...nativeProps} />
280 {this.props.poster &&
281 this.state.showPoster && (
282 <View style={nativeProps.style}>
283 <Image style={posterStyle} source={{ uri: this.props.poster }} />
284 </View>
285 )}
286 </React.Fragment>
287 );
288 }
289}
290
291Video.propTypes = {
292 filter: PropTypes.oneOf([
293 FilterType.NONE,
294 FilterType.INVERT,
295 FilterType.MONOCHROME,
296 FilterType.POSTERIZE,
297 FilterType.FALSE,
298 FilterType.MAXIMUMCOMPONENT,
299 FilterType.MINIMUMCOMPONENT,
300 FilterType.CHROME,
301 FilterType.FADE,
302 FilterType.INSTANT,
303 FilterType.MONO,
304 FilterType.NOIR,
305 FilterType.PROCESS,
306 FilterType.TONAL,
307 FilterType.TRANSFER,
308 FilterType.SEPIA
309 ]),
310 filterEnabled: PropTypes.bool,
311 /* Native only */
312 src: PropTypes.object,
313 seek: PropTypes.oneOfType([
314 PropTypes.number,
315 PropTypes.object
316 ]),
317 fullscreen: PropTypes.bool,
318 onVideoLoadStart: PropTypes.func,
319 onVideoLoad: PropTypes.func,
320 onVideoBuffer: PropTypes.func,
321 onVideoError: PropTypes.func,
322 onVideoProgress: PropTypes.func,
323 onVideoBandwidthUpdate: PropTypes.func,
324 onVideoSeek: PropTypes.func,
325 onVideoEnd: PropTypes.func,
326 onTimedMetadata: PropTypes.func,
327 onVideoAudioBecomingNoisy: PropTypes.func,
328 onVideoExternalPlaybackChange: PropTypes.func,
329 onVideoFullscreenPlayerWillPresent: PropTypes.func,
330 onVideoFullscreenPlayerDidPresent: PropTypes.func,
331 onVideoFullscreenPlayerWillDismiss: PropTypes.func,
332 onVideoFullscreenPlayerDidDismiss: PropTypes.func,
333
334 /* Wrapper component */
335 source: PropTypes.oneOfType([
336 PropTypes.shape({
337 uri: PropTypes.string
338 }),
339 // Opaque type returned by require('./video.mp4')
340 PropTypes.number
341 ]),
342 maxBitRate: PropTypes.number,
343 resizeMode: PropTypes.string,
344 poster: PropTypes.string,
345 posterResizeMode: Image.propTypes.resizeMode,
346 repeat: PropTypes.bool,
347 allowsExternalPlayback: PropTypes.bool,
348 selectedAudioTrack: PropTypes.shape({
349 type: PropTypes.string.isRequired,
350 value: PropTypes.oneOfType([
351 PropTypes.string,
352 PropTypes.number
353 ])
354 }),
355 selectedVideoTrack: PropTypes.shape({
356 type: PropTypes.string.isRequired,
357 value: PropTypes.oneOfType([
358 PropTypes.string,
359 PropTypes.number
360 ])
361 }),
362 selectedTextTrack: PropTypes.shape({
363 type: PropTypes.string.isRequired,
364 value: PropTypes.oneOfType([
365 PropTypes.string,
366 PropTypes.number
367 ])
368 }),
369 textTracks: PropTypes.arrayOf(
370 PropTypes.shape({
371 title: PropTypes.string,
372 uri: PropTypes.string.isRequired,
373 type: PropTypes.oneOf([
374 TextTrackType.SRT,
375 TextTrackType.TTML,
376 TextTrackType.VTT,
377 ]),
378 language: PropTypes.string.isRequired
379 })
380 ),
381 paused: PropTypes.bool,
382 muted: PropTypes.bool,
383 volume: PropTypes.number,
384 bufferConfig: PropTypes.shape({
385 minBufferMs: PropTypes.number,
386 maxBufferMs: PropTypes.number,
387 bufferForPlaybackMs: PropTypes.number,
388 bufferForPlaybackAfterRebufferMs: PropTypes.number,
389 }),
390 stereoPan: PropTypes.number,
391 rate: PropTypes.number,
392 playInBackground: PropTypes.bool,
393 playWhenInactive: PropTypes.bool,
394 ignoreSilentSwitch: PropTypes.oneOf(['ignore', 'obey']),
395 reportBandwidth: PropTypes.bool,
396 disableFocus: PropTypes.bool,
397 controls: PropTypes.bool,
398 audioOnly: PropTypes.bool,
399 currentTime: PropTypes.number,
400 fullscreenAutorotate: PropTypes.bool,
401 fullscreenOrientation: PropTypes.oneOf(['all','landscape','portrait']),
402 progressUpdateInterval: PropTypes.number,
403 useTextureView: PropTypes.bool,
404 hideShutterView: PropTypes.bool,
405 onLoadStart: PropTypes.func,
406 onLoad: PropTypes.func,
407 onBuffer: PropTypes.func,
408 onError: PropTypes.func,
409 onProgress: PropTypes.func,
410 onBandwidthUpdate: PropTypes.func,
411 onSeek: PropTypes.func,
412 onEnd: PropTypes.func,
413 onFullscreenPlayerWillPresent: PropTypes.func,
414 onFullscreenPlayerDidPresent: PropTypes.func,
415 onFullscreenPlayerWillDismiss: PropTypes.func,
416 onFullscreenPlayerDidDismiss: PropTypes.func,
417 onReadyForDisplay: PropTypes.func,
418 onPlaybackStalled: PropTypes.func,
419 onPlaybackResume: PropTypes.func,
420 onPlaybackRateChange: PropTypes.func,
421 onAudioFocusChanged: PropTypes.func,
422 onAudioBecomingNoisy: PropTypes.func,
423 onExternalPlaybackChange: PropTypes.func,
424
425 /* Required by react-native */
426 scaleX: PropTypes.number,
427 scaleY: PropTypes.number,
428 translateX: PropTypes.number,
429 translateY: PropTypes.number,
430 rotation: PropTypes.number,
431 ...ViewPropTypes,
432};
433
434const RCTVideo = requireNativeComponent('RCTVideo', Video, {
435 nativeOnly: {
436 src: true,
437 seek: true,
438 fullscreen: true,
439 },
440});