Revert "chore: update basic example"

This reverts commit c991a0e8f7.
In order to avoid big conflicts with #3015
This commit is contained in:
KrzysztofMoch 2023-09-21 08:12:59 +02:00
parent 421712825b
commit 106257aa80
2 changed files with 358 additions and 451 deletions

View File

@ -1,6 +1,8 @@
'use strict'; 'use strict';
import React, {Component, createRef} from 'react'; import React, {
Component
} from 'react';
import { import {
StyleSheet, StyleSheet,
@ -12,11 +14,12 @@ import {
ToastAndroid, ToastAndroid,
} from 'react-native'; } from 'react-native';
import {Picker} from '@react-native-picker/picker'; import { Picker } from '@react-native-picker/picker'
import Video, {TextTrackType, VideoDecoderProperties} from 'react-native-video'; import Video, { VideoDecoderProperties, TextTrackType } from 'react-native-video';
class VideoPlayer extends Component { class VideoPlayer extends Component {
state = { state = {
rate: 1, rate: 1,
volume: 1, volume: 1,
@ -43,7 +46,7 @@ class VideoPlayer extends Component {
showRNVControls: false, showRNVControls: false,
}; };
seekerWidth = 0; seekerWidth = 0
srcList = [ srcList = [
require('./broadchurch.mp4'), require('./broadchurch.mp4'),
@ -57,95 +60,99 @@ class VideoPlayer extends Component {
}, },
{ {
description: '(hls|live) red bull tv', description: '(hls|live) red bull tv',
uri: 'https://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_928.m3u8', uri: 'https://rbmn-live.akamaized.net/hls/live/590964/BoRB-AT/master_928.m3u8'
}, },
{ {
description: '(mp4|subtitles) demo with sintel Subtitles', description: '(mp4|subtitles) demo with sintel Subtitles',
uri: 'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0', uri:
'http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,source,id,as&ip=0.0.0.0&ipbits=0&expire=19000000000&signature=51AF5F39AB0CEC3E5497CD9C900EBFEAECCCB5C7.8506521BFC350652163895D4C26DEE124209AA9E&key=ik0',
type: 'mpd', type: 'mpd',
}, },
{ {
description: 'invalid URL', description: 'invalid URL',
uri: 'mmt://www.youtube.com', uri:
'mmt://www.youtube.com',
type: 'mpd', type: 'mpd',
}, },
{description: '(no url) Stopped playback', uri: undefined}, { description: '(no url) Stopped playback', uri: undefined },
{ {
description: '(no view) no View', description: '(no view) no View',
noView: true, noView: true,
}, },
]; ]
video = createRef<Video>(); video: Video;
seekPanResponder: PanResponder | undefined; seekPanResponder: PanResponder | undefined;
popupInfo = () => { popupInfo = () => {
VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => { VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => {
VideoDecoderProperties.isHEVCSupported().then( VideoDecoderProperties.isHEVCSupported().then((hevcSupported: boolean) => {
(hevcSupported: boolean) => { VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then(
VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then( (avcSupported: boolean) => {
(avcSupported: boolean) => { this.toast(
this.toast( true,
true, 'Widevine level: ' +
'Widevine level: ' + widevineLevel +
widevineLevel + '\n hevc: ' +
'\n hevc: ' + (hevcSupported ? '' : 'NOT') +
(hevcSupported ? '' : 'NOT') + 'supported' +
'supported' + '\n avc: ' +
'\n avc: ' + (avcSupported ? '' : 'NOT') +
(avcSupported ? '' : 'NOT') + 'supported',
'supported', )
); },
}, )
); })
}, })
); }
});
};
onLoad = (data: any) => { onLoad = (data: any) => {
this.setState({duration: data.duration, loading: false}); this.setState({ duration: data.duration, loading: false, });
this.onAudioTracks(data); this.onAudioTracks(data)
this.onTextTracks(data); this.onTextTracks(data)
}; };
onProgress = (data: any) => { onProgress = (data: any) => {
if (!this.state.seeking) { if (!this.state.seeking) {
const position = this.calculateSeekerPosition(); const position = this.calculateSeekerPosition()
this.setSeekerPosition(position); this.setSeekerPosition(position)
} }
this.setState({currentTime: data.currentTime}); this.setState({ currentTime: data.currentTime })
}; };
onVideoLoadStart = () => { onVideoLoadStart = () => {
this.setState({isLoading: true}); console.log('onVideoLoadStart')
}; this.setState({ isLoading: true })
}
onAudioTracks = (data: any) => { onAudioTracks = (data: any) => {
const selectedTrack = data.audioTracks?.find((x: any) => { const selectedTrack = data.audioTracks?.find((x: any) => {
return x.selected; return x.selected
}); })
this.setState({ this.setState({
audioTracks: data.audioTracks, audioTracks: data.audioTracks,
}); })
if (selectedTrack?.language) { if (selectedTrack?.language) {
this.setState({ this.setState({
selectedAudioTrack: { selectedAudioTrack: {
type: 'language', type: 'language',
value: selectedTrack?.language, value: selectedTrack?.language,
}, },
}); })
} }
}; }
onTextTracks = (data: any) => { onTextTracks = (data: any) => {
const selectedTrack = data.textTracks?.find((x: any) => { const selectedTrack = data.textTracks?.find((x: any) => {
return x.selected; return x.selected
}); })
this.setState({ this.setState({
textTracks: data.textTracks, textTracks: data.textTracks,
}); })
if (selectedTrack?.language) { if (selectedTrack?.language) {
this.setState({ this.setState({
textTracks: data, textTracks: data,
@ -153,31 +160,38 @@ class VideoPlayer extends Component {
type: 'language', type: 'language',
value: selectedTrack?.language, value: selectedTrack?.language,
}, },
}); })
} }
}; }
onAspectRatio = (data: any) => { onAspectRatio = (data: any) => {
console.log('onAspectRadio called ' + JSON.stringify(data))
this.setState({ this.setState({
videoWidth: data.width, videoWidth: data.width,
videoHeight: data.height, videoHeight: data.height,
}); })
}; }
onVideoBuffer = (param: any) => { onVideoBuffer = (param: any) => {
this.setState({isLoading: param.isBuffering}); console.log('onVideoBuffer')
};
this.setState({ isLoading: param.isBuffering })
}
onReadyForDisplay = () => { onReadyForDisplay = () => {
this.setState({isLoading: false}); console.log('onReadyForDisplay')
};
this.setState({ isLoading: false })
}
onAudioBecomingNoisy = () => { onAudioBecomingNoisy = () => {
this.setState({paused: true}); this.setState({ paused: true })
}; };
onAudioFocusChanged = (event: {hasAudioFocus: boolean}) => { onAudioFocusChanged = (event: { hasAudioFocus: boolean }) => {
this.setState({paused: !event.hasAudioFocus}); this.setState({ paused: !event.hasAudioFocus })
}; };
getCurrentTimePercentage = () => { getCurrentTimePercentage = () => {
@ -188,18 +202,11 @@ class VideoPlayer extends Component {
}; };
renderRateControl(rate: number) { renderRateControl(rate: number) {
const isSelected = this.state.rate === rate; const isSelected = (this.state.rate === rate);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({ rate }) }}>
onPress={() => { <Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
this.setState({rate});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{rate} {rate}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
@ -207,43 +214,30 @@ class VideoPlayer extends Component {
} }
renderResizeModeControl(resizeMode: string) { renderResizeModeControl(resizeMode: string) {
const isSelected = this.state.resizeMode === resizeMode; const isSelected = (this.state.resizeMode === resizeMode);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({ resizeMode }) }}>
onPress={() => { <Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
this.setState({resizeMode});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{resizeMode} {resizeMode}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderVolumeControl(volume: number) { renderVolumeControl(volume: number) {
const isSelected = this.state.volume === volume; const isSelected = (this.state.volume === volume);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({ volume }) }}>
onPress={() => { <Text style={[styles.controlOption, { fontWeight: isSelected ? 'bold' : 'normal' }]}>
this.setState({volume});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{volume * 100}% {volume * 100}%
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
toast = (visible: boolean, message: string) => { toast = (visible: boolean, message: string) => {
if (visible) { if (visible) {
ToastAndroid.showWithGravityAndOffset( ToastAndroid.showWithGravityAndOffset(
@ -252,33 +246,35 @@ class VideoPlayer extends Component {
ToastAndroid.BOTTOM, ToastAndroid.BOTTOM,
25, 25,
50, 50,
); )
return null; return null
} }
return null; return null
}; }
onError = (err: any) => { onError = (err: any) => {
this.toast(true, 'error: ' + err?.error.errorCode); console.log(JSON.stringify(err?.error.errorCode))
}; this.toast(true, 'error: ' + err?.error.errorCode)
}
onEnd = () => { onEnd = () => {
this.channelUp(); this.channelUp()
}; };
toggleFullscreen() { toggleFullscreen() {
this.setState({fullscreen: !this.state.fullscreen}); this.setState({ fullscreen: !this.state.fullscreen })
} }
toggleControls() { toggleControls() {
this.setState({showRNVControls: !this.state.showRNVControls}); this.setState({ showRNVControls: !this.state.showRNVControls })
} }
toggleDecoration() { toggleDecoration() {
this.setState({decoration: !this.state.decoration}); this.setState({ decoration: !this.state.decoration })
if (this.state.decoration) { if (this.state.decoration) {
this.video.current?.dismissFullscreenPlayer(); this.video.dismissFullscreenPlayer()
} else { } else {
this.video.current?.presentFullscreenPlayer(); this.video.presentFullscreenPlayer()
} }
} }
@ -294,80 +290,86 @@ class VideoPlayer extends Component {
textTracks: [], textTracks: [],
selectedAudioTrack: undefined, selectedAudioTrack: undefined,
selectedTextTrack: undefined, selectedTextTrack: undefined,
}); })
} }
channelUp() { channelUp() {
this.goToChannel((this.state.srcListId + 1) % this.srcList.length); console.log('channel up')
this.goToChannel((this.state.srcListId + 1) % this.srcList.length)
} }
channelDown() { channelDown() {
this.goToChannel( console.log('channel down')
(this.state.srcListId + this.srcList.length - 1) % this.srcList.length, this.goToChannel((this.state.srcListId + this.srcList.length - 1) % this.srcList.length)
);
} }
componentDidMount() { componentDidMount() {
this.initSeekPanResponder(); this.initSeekPanResponder()
} }
renderDecorationsControl() { renderDecorationsControl() {
return ( return (
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.toggleDecoration(); this.toggleDecoration()
}}> }}
>
<Text style={[styles.controlOption]}>{'decoration'}</Text> <Text style={[styles.controlOption]}>{'decoration'}</Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderInfoControl() { renderInfoControl() {
return ( return (
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.popupInfo(); this.popupInfo()
}}> }}
>
<Text style={[styles.controlOption]}>{'decoderInfo'}</Text> <Text style={[styles.controlOption]}>{'decoderInfo'}</Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderFullScreenControl() { renderFullScreenControl() {
return ( return (
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.toggleFullscreen(); this.toggleFullscreen()
}}> }}
>
<Text style={[styles.controlOption]}>{'fullscreen'}</Text> <Text style={[styles.controlOption]}>{'fullscreen'}</Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderPause() { renderPause() {
return ( return (
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.setState({paused: !this.state.paused}); this.setState({ paused: !this.state.paused })
}}> }}
>
<Text style={[styles.controlOption]}> <Text style={[styles.controlOption]}>
{this.state.paused ? 'pause' : 'playing'} {this.state.paused ? 'pause' : 'playing'}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderRepeatModeControl() { renderRepeatModeControl() {
return ( return (
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.setState({loop: !this.state.loop}); this.setState({ loop: !this.state.loop })
}}> }}
>
<Text style={[styles.controlOption]}> <Text style={[styles.controlOption]}>
{this.state.loop ? 'loop enable' : 'loop disable'} {this.state.loop ? 'loop enable' : 'loop disable'}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderLeftControl() { renderLeftControl() {
@ -375,13 +377,14 @@ class VideoPlayer extends Component {
<View> <View>
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.channelDown(); this.channelDown()
}}> }}
>
<Text style={[styles.leftRightControlOption]}>{'ChDown'}</Text> <Text style={[styles.leftRightControlOption]}>{'ChDown'}</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
// onTimelineUpdated // onTimelineUpdated
); )
} }
renderRightControl() { renderRightControl() {
@ -389,12 +392,13 @@ class VideoPlayer extends Component {
<View> <View>
<TouchableOpacity <TouchableOpacity
onPress={() => { onPress={() => {
this.channelUp(); this.channelUp()
}}> }}
>
<Text style={[styles.leftRightControlOption]}>{'ChUp'}</Text> <Text style={[styles.leftRightControlOption]}>{'ChUp'}</Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); )
} }
/** /**
@ -411,11 +415,11 @@ class VideoPlayer extends Component {
*/ */
constrainToSeekerMinMax(val = 0) { constrainToSeekerMinMax(val = 0) {
if (val <= 0) { if (val <= 0) {
return 0; return 0
} else if (val >= this.seekerWidth) { } else if (val >= this.seekerWidth) {
return this.seekerWidth; return this.seekerWidth
} }
return val; return val
} }
/** /**
@ -426,17 +430,17 @@ class VideoPlayer extends Component {
* @param {float} position position in px of seeker handle} * @param {float} position position in px of seeker handle}
*/ */
setSeekerPosition(position = 0) { setSeekerPosition(position = 0) {
const state = this.state; const state = this.state
position = this.constrainToSeekerMinMax(position); position = this.constrainToSeekerMinMax(position)
state.seekerFillWidth = position; state.seekerFillWidth = position
state.seekerPosition = position; state.seekerPosition = position
if (!state.seeking) { if (!state.seeking) {
state.seekerOffset = position; state.seekerOffset = position
} }
this.setState(state); this.setState(state)
} }
/** /**
@ -446,8 +450,8 @@ class VideoPlayer extends Component {
* @return {float} position of seeker handle in px based on currentTime * @return {float} position of seeker handle in px based on currentTime
*/ */
calculateSeekerPosition() { calculateSeekerPosition() {
const percent = this.state.currentTime / this.state.duration; const percent = this.state.currentTime / this.state.duration
return this.seekerWidth * percent; return this.seekerWidth * percent
} }
/** /**
@ -457,8 +461,8 @@ class VideoPlayer extends Component {
* @return {float} time in ms based on seekerPosition. * @return {float} time in ms based on seekerPosition.
*/ */
calculateTimeFromSeekerPosition() { calculateTimeFromSeekerPosition() {
const percent = this.state.seekerPosition / this.seekerWidth; const percent = this.state.seekerPosition / this.seekerWidth
return this.state.duration * percent; return this.state.duration * percent
} }
/** /**
@ -476,20 +480,20 @@ class VideoPlayer extends Component {
* position in the onProgress listener. * position in the onProgress listener.
*/ */
onPanResponderGrant: (evt, gestureState) => { onPanResponderGrant: (evt, gestureState) => {
const state = this.state; const state = this.state
// this.clearControlTimeout() // this.clearControlTimeout()
const position = evt.nativeEvent.locationX; const position = evt.nativeEvent.locationX
this.setSeekerPosition(position); this.setSeekerPosition(position)
state.seeking = true; state.seeking = true
this.setState(state); this.setState(state)
}, },
/** /**
* When panning, update the seekbar position, duh. * When panning, update the seekbar position, duh.
*/ */
onPanResponderMove: (evt, gestureState) => { onPanResponderMove: (evt, gestureState) => {
const position = this.state.seekerOffset + gestureState.dx; const position = this.state.seekerOffset + gestureState.dx
this.setSeekerPosition(position); this.setSeekerPosition(position)
}, },
/** /**
@ -498,43 +502,41 @@ class VideoPlayer extends Component {
* onEnd callback * onEnd callback
*/ */
onPanResponderRelease: (evt, gestureState) => { onPanResponderRelease: (evt, gestureState) => {
const time = this.calculateTimeFromSeekerPosition(); const time = this.calculateTimeFromSeekerPosition()
const state = this.state; const state = this.state
if (time >= state.duration && !state.isLoading) { if (time >= state.duration && !state.isLoading) {
state.paused = true; state.paused = true
this.onEnd(); this.onEnd()
} else { } else {
this.video.current?.seek(time); this.video?.seek(time)
state.seeking = false; state.seeking = false
} }
this.setState(state); this.setState(state)
}, },
}); })
} }
renderSeekBar() { renderSeekBar() {
if (!this.seekPanResponder) { if (!this.seekPanResponder) {
return null; return null
} }
return ( return (
<View <View
style={styles.seekbarContainer} style={styles.seekbarContainer}
{...this.seekPanResponder.panHandlers} {...this.seekPanResponder.panHandlers}
{...styles.generalControls}> {...styles.generalControls}
>
<View <View
style={styles.seekbarTrack} style={styles.seekbarTrack}
onLayout={event => onLayout={(event) => (this.seekerWidth = event.nativeEvent.layout.width)}
(this.seekerWidth = event.nativeEvent.layout.width) pointerEvents={'none'}
} >
pointerEvents={'none'}>
<View <View
style={[ style={[
styles.seekbarFill, styles.seekbarFill,
{ {
width: width:
this.state.seekerFillWidth > 0 this.state.seekerFillWidth > 0 ? this.state.seekerFillWidth : 0,
? this.state.seekerFillWidth
: 0,
backgroundColor: '#FFF', backgroundColor: '#FFF',
}, },
]} ]}
@ -544,84 +546,65 @@ class VideoPlayer extends Component {
<View <View
style={[ style={[
styles.seekbarHandle, styles.seekbarHandle,
{ { left: this.state.seekerPosition > 0 ? this.state.seekerPosition : 0 },
left:
this.state.seekerPosition > 0 ? this.state.seekerPosition : 0,
},
]} ]}
pointerEvents={'none'}> pointerEvents={'none'}
>
<View <View
style={[styles.seekbarCircle, {backgroundColor: '#FFF'}]} style={[
styles.seekbarCircle,
{ backgroundColor: '#FFF' },
]}
pointerEvents={'none'} pointerEvents={'none'}
/> />
</View> </View>
</View> </View>
); )
} }
IndicatorLoadingView() { IndicatorLoadingView() {
if (this.state.isLoading) if (this.state.isLoading)
return ( return <ActivityIndicator color="#3235fd" size="large" style={styles.IndicatorStyle} />
<ActivityIndicator else return <View />
color="#3235fd"
size="large"
style={styles.IndicatorStyle}
/>
);
else return <View />;
} }
renderTopControl() { renderTopControl() {
return ( return (<>
<> <Text style={[styles.controlOption]}>
<Text style={[styles.controlOption]}> {this.srcList[this.state.srcListId]?.description || 'local file'}
{this.srcList[this.state.srcListId]?.description || 'local file'} </Text>
</Text> <View >
<View> <TouchableOpacity
<TouchableOpacity onPress={() => {
onPress={() => { this.toggleControls()
this.toggleControls(); }}
}}> >
<Text style={[styles.leftRightControlOption]}> <Text style={[styles.leftRightControlOption]}>{this.state.showRNVControls ? 'Hide controls' : 'Show controls'}</Text>
{this.state.showRNVControls ? 'Hide controls' : 'Show controls'} </TouchableOpacity>
</Text> </View>
</TouchableOpacity> </>)
</View>
</>
);
} }
renderOverlay() { renderOverlay() {
return ( return (
<> <>
{this.IndicatorLoadingView()} {this.IndicatorLoadingView()}
<View style={styles.topControls}> <View style={styles.topControls}>
<View style={styles.resizeModeControl}> <View style={styles.resizeModeControl}>{this.renderTopControl()}</View>
{this.renderTopControl()}
</View>
</View> </View>
{!this.state.showRNVControls ? ( {!this.state.showRNVControls ? (
<> <>
<View style={styles.leftControls}> <View style={styles.leftControls}>
<View style={styles.resizeModeControl}> <View style={styles.resizeModeControl}>{this.renderLeftControl()}</View>
{this.renderLeftControl()} </View><View style={styles.rightControls}>
</View> <View style={styles.resizeModeControl}>{this.renderRightControl()}</View>
</View> </View><View style={styles.bottomControls}>
<View style={styles.rightControls}>
<View style={styles.resizeModeControl}>
{this.renderRightControl()}
</View>
</View>
<View style={styles.bottomControls}>
<View style={styles.generalControls}> <View style={styles.generalControls}>
<View style={styles.generalControls}> <View style={styles.generalControls}>
<View style={styles.resizeModeControl}> <View style={styles.resizeModeControl}>{this.renderInfoControl()}</View>
{this.renderInfoControl()}
</View>
</View>
<View style={styles.resizeModeControl}>
{this.renderPause()}
</View> </View>
<View style={styles.resizeModeControl}>{this.renderPause()}</View>
<View style={styles.resizeModeControl}> <View style={styles.resizeModeControl}>
{this.renderRepeatModeControl()} {this.renderRepeatModeControl()}
</View> </View>
@ -663,20 +646,21 @@ class VideoPlayer extends Component {
style={styles.picker} style={styles.picker}
selectedValue={this.state.selectedAudioTrack?.value} selectedValue={this.state.selectedAudioTrack?.value}
onValueChange={(itemValue, itemIndex) => { onValueChange={(itemValue, itemIndex) => {
console.log('on audio value change ' + itemValue);
this.setState({ this.setState({
selectedAudioTrack: { selectedAudioTrack: {
type: 'language', type: 'language',
value: itemValue, value: itemValue,
}, },
}); });
}}> }}
{this.state.audioTracks.map(track => { >
{this.state.audioTracks.map((track) => {
return ( return (
<Picker.Item <Picker.Item
label={track.language} label={track.language}
value={track.language} value={track.language}
key={track.language} key={track.language} />
/>
); );
})} })}
</Picker> </Picker>
@ -689,58 +673,45 @@ class VideoPlayer extends Component {
style={styles.picker} style={styles.picker}
selectedValue={this.state.selectedTextTrack?.value} selectedValue={this.state.selectedTextTrack?.value}
onValueChange={(itemValue, itemIndex) => { onValueChange={(itemValue, itemIndex) => {
console.log('on value change ' + itemValue);
this.setState({ this.setState({
selectedTextTrack: { selectedTextTrack: {
type: 'language', type: 'language',
value: itemValue, value: itemValue,
}, },
}); });
}}> }}
>
<Picker.Item label={'none'} value={'none'} key={'none'} /> <Picker.Item label={'none'} value={'none'} key={'none'} />
{this.state.textTracks.map(track => ( {this.state.textTracks.map((track) => (
<Picker.Item <Picker.Item
label={track.language} label={track.language}
value={track.language} value={track.language}
key={track.language} key={track.language} />
/>
))} ))}
</Picker> </Picker>
)} )}
</View> </View>
</View> </View></>
</> ) : null
) : null} }
</> </>
); )
} }
renderVideoView() { renderVideoView() {
const viewStyle = this.state.fullscreen const viewStyle = this.state.fullscreen ? styles.fullScreen : styles.halfScreen
? styles.fullScreen
: styles.halfScreen;
return ( return (
<TouchableOpacity <TouchableOpacity style={viewStyle}>
style={viewStyle}
onPress={() => {
if (!this.video.current) {
return;
}
const {paused} = this.state;
const {pause, play} = this.video.current;
const shouldPlay = paused === true || paused === undefined;
shouldPlay ? play() : pause();
this.setState({paused: !shouldPlay});
}}>
<Video <Video
// paused={this.state.paused} ref={(ref: Video) => {
ref={this.video} this.video = ref
}}
source={this.srcList[this.state.srcListId]} source={this.srcList[this.state.srcListId]}
style={viewStyle} style={viewStyle}
rate={this.state.rate} rate={this.state.rate}
paused={this.state.paused}
volume={this.state.volume} volume={this.state.volume}
muted={this.state.muted} muted={this.state.muted}
fullscreen={this.state.fullscreen} fullscreen={this.state.fullscreen}
@ -765,21 +736,21 @@ class VideoPlayer extends Component {
playInBackground={false} playInBackground={false}
/> />
</TouchableOpacity> </TouchableOpacity>
); )
} }
render() { render() {
return ( return (
<View style={styles.container}> <View style={styles.container}>
{this.srcList[this.state.srcListId]?.noView {this.srcList[this.state.srcListId]?.noView ? null : this.renderVideoView()}
? null
: this.renderVideoView()}
{this.renderOverlay()} {this.renderOverlay()}
</View> </View>
); )
} }
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create({
container: { container: {
flex: 1, flex: 1,
@ -919,4 +890,4 @@ const styles = StyleSheet.create({
}, },
}); });
export default VideoPlayer; export default VideoPlayer

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
import React, {Component} from 'react'; import React, {
Component
} from 'react';
import { import {
Alert, Alert,
@ -10,25 +12,25 @@ import {
View, View,
} from 'react-native'; } from 'react-native';
import Video, {FilterType} from 'react-native-video'; import Video,{FilterType} from 'react-native-video';
const filterTypes = [ const filterTypes = [
FilterType.NONE, FilterType.NONE,
FilterType.INVERT, FilterType.INVERT,
FilterType.MONOCHROME, FilterType.MONOCHROME,
FilterType.POSTERIZE, FilterType.POSTERIZE,
FilterType.FALSE, FilterType.FALSE,
FilterType.MAXIMUMCOMPONENT, FilterType.MAXIMUMCOMPONENT,
FilterType.MINIMUMCOMPONENT, FilterType.MINIMUMCOMPONENT,
FilterType.CHROME, FilterType.CHROME,
FilterType.FADE, FilterType.FADE,
FilterType.INSTANT, FilterType.INSTANT,
FilterType.MONO, FilterType.MONO,
FilterType.NOIR, FilterType.NOIR,
FilterType.PROCESS, FilterType.PROCESS,
FilterType.TONAL, FilterType.TONAL,
FilterType.TRANSFER, FilterType.TRANSFER,
FilterType.SEPIA, FilterType.SEPIA
]; ];
class VideoPlayer extends Component { class VideoPlayer extends Component {
@ -38,7 +40,7 @@ class VideoPlayer extends Component {
this.onProgress = this.onProgress.bind(this); this.onProgress = this.onProgress.bind(this);
this.onBuffer = this.onBuffer.bind(this); this.onBuffer = this.onBuffer.bind(this);
} }
video = React.createRef<Video>(); video = React.createRef();
state = { state = {
rate: 1, rate: 1,
@ -54,7 +56,7 @@ class VideoPlayer extends Component {
mixWithOthers: null, mixWithOthers: null,
isBuffering: false, isBuffering: false,
filter: FilterType.NONE, filter: FilterType.NONE,
filterEnabled: true, filterEnabled: true
}; };
onLoad(data: any) { onLoad(data: any) {
@ -66,8 +68,8 @@ class VideoPlayer extends Component {
this.setState({currentTime: data.currentTime}); this.setState({currentTime: data.currentTime});
} }
onBuffer({isBuffering}: {isBuffering: boolean}) { onBuffer({ isBuffering }: { isBuffering: boolean }) {
this.setState({isBuffering}); this.setState({ isBuffering });
} }
getCurrentTimePercentage() { getCurrentTimePercentage() {
@ -88,26 +90,19 @@ class VideoPlayer extends Component {
} }
this.setState({ this.setState({
filter: filterTypes[index], filter: filterTypes[index]
}); })
} }
renderSkinControl(skin: 'custom' | 'native' | 'embed') { renderSkinControl(skin) {
const isSelected = this.state.skin == skin; const isSelected = this.state.skin == skin;
const selectControls = skin == 'native' || skin == 'embed'; const selectControls = skin == 'native' || skin == 'embed';
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({
onPress={() => { controls: selectControls,
this.setState({ skin: skin
controls: selectControls, }) }}>
skin: skin, <Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{skin} {skin}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
@ -115,111 +110,77 @@ class VideoPlayer extends Component {
} }
renderRateControl(rate: number) { renderRateControl(rate: number) {
const isSelected = this.state.rate == rate; const isSelected = (this.state.rate == rate);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({rate: rate}) }}>
onPress={() => { <Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
this.setState({rate: rate});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{rate}x {rate}x
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderResizeModeControl(resizeMode: string) { renderResizeModeControl(resizeMode: string) {
const isSelected = this.state.resizeMode == resizeMode; const isSelected = (this.state.resizeMode == resizeMode);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({resizeMode: resizeMode}) }}>
onPress={() => { <Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
this.setState({resizeMode: resizeMode});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{resizeMode} {resizeMode}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderVolumeControl(volume: number) { renderVolumeControl(volume: number) {
const isSelected = this.state.volume == volume; const isSelected = (this.state.volume == volume);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({volume: volume}) }}>
onPress={() => { <Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
this.setState({volume: volume});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{volume * 100}% {volume * 100}%
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderIgnoreSilentSwitchControl(ignoreSilentSwitch: string) { renderIgnoreSilentSwitchControl(ignoreSilentSwitch: string) {
const isSelected = this.state.ignoreSilentSwitch == ignoreSilentSwitch; const isSelected = (this.state.ignoreSilentSwitch == ignoreSilentSwitch);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({ignoreSilentSwitch: ignoreSilentSwitch}) }}>
onPress={() => { <Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
this.setState({ignoreSilentSwitch: ignoreSilentSwitch});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{ignoreSilentSwitch} {ignoreSilentSwitch}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderFullscreenControl(fullscreen: string) { renderFullscreenControl(fullscreen: string) {
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => {
onPress={() => {
if (fullscreen === 'fullscreen') { if (fullscreen === 'fullscreen') {
this.video.current?.presentFullscreenPlayer(); this.video.presentFullscreenPlayer()
} }
}}> }}>
<Text style={[styles.controlOption]}>{fullscreen}</Text> <Text style={[styles.controlOption]}>
{fullscreen}
</Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderMixWithOthersControl(mixWithOthers: string) { renderMixWithOthersControl(mixWithOthers: string) {
const isSelected = this.state.mixWithOthers == mixWithOthers; const isSelected = (this.state.mixWithOthers == mixWithOthers);
return ( return (
<TouchableOpacity <TouchableOpacity onPress={() => { this.setState({mixWithOthers: mixWithOthers}) }}>
onPress={() => { <Text style={[styles.controlOption, {fontWeight: isSelected ? "bold" : "normal"}]}>
this.setState({mixWithOthers: mixWithOthers});
}}>
<Text
style={[
styles.controlOption,
{fontWeight: isSelected ? 'bold' : 'normal'},
]}>
{mixWithOthers} {mixWithOthers}
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
); )
} }
renderCustomSkin() { renderCustomSkin() {
@ -228,23 +189,10 @@ class VideoPlayer extends Component {
return ( return (
<View style={styles.container}> <View style={styles.container}>
<TouchableOpacity <TouchableOpacity style={styles.fullScreen} onPress={() => {this.setState({paused: !this.state.paused})}}>
style={styles.fullScreen}
onPress={() => {
if (!this.video.current) {
return;
}
const {play, pause} = this.video.current;
const shouldPlay = !this.state.paused;
shouldPlay ? play() : pause();
this.setState({paused: shouldPlay});
}}>
<Video <Video
ref={(ref: Video) => { ref={(ref: Video) => {
this.video = ref; this.video = ref
}} }}
source={require('./broadchurch.mp4')} source={require('./broadchurch.mp4')}
style={styles.fullScreen} style={styles.fullScreen}
@ -258,9 +206,7 @@ class VideoPlayer extends Component {
onLoad={this.onLoad} onLoad={this.onLoad}
onBuffer={this.onBuffer} onBuffer={this.onBuffer}
onProgress={this.onProgress} onProgress={this.onProgress}
onEnd={() => { onEnd={() => { Alert.alert('Done!') }}
Alert.alert('Done!');
}}
repeat={true} repeat={true}
filter={this.state.filter} filter={this.state.filter}
filterEnabled={this.state.filterEnabled} filterEnabled={this.state.filterEnabled}
@ -274,22 +220,21 @@ class VideoPlayer extends Component {
{this.renderSkinControl('native')} {this.renderSkinControl('native')}
{this.renderSkinControl('embed')} {this.renderSkinControl('embed')}
</View> </View>
{this.state.filterEnabled ? ( {
<View style={styles.skinControl}> (this.state.filterEnabled) ?
<TouchableOpacity <View style={styles.skinControl}>
onPress={() => { <TouchableOpacity onPress={() => {
this.setFilter(-1); this.setFilter(-1)
}}> }}>
<Text style={styles.controlOption}>Previous Filter</Text> <Text style={styles.controlOption}>Previous Filter</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity onPress={() => {
onPress={() => { this.setFilter(1)
this.setFilter(1); }}>
}}> <Text style={styles.controlOption}>Next Filter</Text>
<Text style={styles.controlOption}>Next Filter</Text> </TouchableOpacity>
</TouchableOpacity> </View> : null
</View> }
) : null}
</View> </View>
<View style={styles.generalControls}> <View style={styles.generalControls}>
<View style={styles.rateControl}> <View style={styles.rateControl}>
@ -311,31 +256,28 @@ class VideoPlayer extends Component {
</View> </View>
</View> </View>
<View style={styles.generalControls}> <View style={styles.generalControls}>
{Platform.OS === 'ios' ? ( {
<> (Platform.OS === 'ios') ?
<View style={styles.ignoreSilentSwitchControl}> <>
{this.renderIgnoreSilentSwitchControl('ignore')} <View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('obey')} {this.renderIgnoreSilentSwitchControl('ignore')}
</View> {this.renderIgnoreSilentSwitchControl('obey')}
<View style={styles.mixWithOthersControl}> </View>
{this.renderMixWithOthersControl('mix')} <View style={styles.mixWithOthersControl}>
{this.renderMixWithOthersControl('duck')} {this.renderMixWithOthersControl('mix')}
</View> {this.renderMixWithOthersControl('duck')}
<View style={styles.mixWithOthersControl}> </View>
{this.renderFullscreenControl('fullscreen')} <View style={styles.mixWithOthersControl}>
</View> {this.renderFullscreenControl('fullscreen')}
</> </View>
) : null} </> : null
}
</View> </View>
<View style={styles.trackingControls}> <View style={styles.trackingControls}>
<View style={styles.progress}> <View style={styles.progress}>
<View <View style={[styles.innerProgressCompleted, {flex: flexCompleted}]} />
style={[styles.innerProgressCompleted, {flex: flexCompleted}]} <View style={[styles.innerProgressRemaining, {flex: flexRemaining}]} />
/>
<View
style={[styles.innerProgressRemaining, {flex: flexRemaining}]}
/>
</View> </View>
</View> </View>
</View> </View>
@ -344,10 +286,7 @@ class VideoPlayer extends Component {
} }
renderNativeSkin() { renderNativeSkin() {
const videoStyle = const videoStyle = this.state.skin == 'embed' ? styles.nativeVideoControls : styles.fullScreen;
this.state.skin == 'embed'
? styles.nativeVideoControls
: styles.fullScreen;
return ( return (
<View style={styles.container}> <View style={styles.container}>
<View style={styles.fullScreen}> <View style={styles.fullScreen}>
@ -364,9 +303,7 @@ class VideoPlayer extends Component {
onLoad={this.onLoad} onLoad={this.onLoad}
onBuffer={this.onBuffer} onBuffer={this.onBuffer}
onProgress={this.onProgress} onProgress={this.onProgress}
onEnd={() => { onEnd={() => { Alert.alert('Done!') }}
Alert.alert('Done!');
}}
repeat={true} repeat={true}
controls={this.state.controls} controls={this.state.controls}
filter={this.state.filter} filter={this.state.filter}
@ -380,22 +317,21 @@ class VideoPlayer extends Component {
{this.renderSkinControl('native')} {this.renderSkinControl('native')}
{this.renderSkinControl('embed')} {this.renderSkinControl('embed')}
</View> </View>
{this.state.filterEnabled ? ( {
<View style={styles.skinControl}> (this.state.filterEnabled) ?
<TouchableOpacity <View style={styles.skinControl}>
onPress={() => { <TouchableOpacity onPress={() => {
this.setFilter(-1); this.setFilter(-1)
}}> }}>
<Text style={styles.controlOption}>Previous Filter</Text> <Text style={styles.controlOption}>Previous Filter</Text>
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity onPress={() => {
onPress={() => { this.setFilter(1)
this.setFilter(1); }}>
}}> <Text style={styles.controlOption}>Next Filter</Text>
<Text style={styles.controlOption}>Next Filter</Text> </TouchableOpacity>
</TouchableOpacity> </View> : null
</View> }
) : null}
</View> </View>
<View style={styles.generalControls}> <View style={styles.generalControls}>
<View style={styles.rateControl}> <View style={styles.rateControl}>
@ -417,28 +353,28 @@ class VideoPlayer extends Component {
</View> </View>
</View> </View>
<View style={styles.generalControls}> <View style={styles.generalControls}>
{Platform.OS === 'ios' ? ( {
<> (Platform.OS === 'ios') ?
<View style={styles.ignoreSilentSwitchControl}> <>
{this.renderIgnoreSilentSwitchControl('ignore')} <View style={styles.ignoreSilentSwitchControl}>
{this.renderIgnoreSilentSwitchControl('obey')} {this.renderIgnoreSilentSwitchControl('ignore')}
</View> {this.renderIgnoreSilentSwitchControl('obey')}
<View style={styles.mixWithOthersControl}> </View>
{this.renderMixWithOthersControl('mix')} <View style={styles.mixWithOthersControl}>
{this.renderMixWithOthersControl('duck')} {this.renderMixWithOthersControl('mix')}
</View> {this.renderMixWithOthersControl('duck')}
</> </View>
) : null} </> : null
}
</View> </View>
</View> </View>
</View> </View>
); );
} }
render() { render() {
return this.state.controls return this.state.controls ? this.renderNativeSkin() : this.renderCustomSkin();
? this.renderNativeSkin()
: this.renderCustomSkin();
} }
} }
@ -457,7 +393,7 @@ const styles = StyleSheet.create({
right: 0, right: 0,
}, },
controls: { controls: {
backgroundColor: 'transparent', backgroundColor: "transparent",
borderRadius: 5, borderRadius: 5,
position: 'absolute', position: 'absolute',
bottom: 44, bottom: 44,
@ -503,24 +439,24 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center'
}, },
ignoreSilentSwitchControl: { ignoreSilentSwitchControl: {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center'
}, },
mixWithOthersControl: { mixWithOthersControl: {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center'
}, },
controlOption: { controlOption: {
alignSelf: 'center', alignSelf: 'center',
fontSize: 11, fontSize: 11,
color: 'white', color: "white",
paddingLeft: 2, paddingLeft: 2,
paddingRight: 2, paddingRight: 2,
lineHeight: 12, lineHeight: 12,
@ -536,4 +472,4 @@ const styles = StyleSheet.create({
justifyContent: 'center', justifyContent: 'center',
}, },
}); });
export default VideoPlayer; export default VideoPlayer