311 lines
9.5 KiB
TypeScript
Raw Normal View History

import React, {forwardRef, memo, useCallback} from 'react';
import {Indicator} from './Indicator.tsx';
import {View} from 'react-native';
import styles from '../styles.tsx';
import ToggleControl from '../ToggleControl.tsx';
import {
isAndroid,
isIos,
samplePoster,
textTracksSelectionBy,
} from '../constants';
import MultiValueControl, {
MultiValueControlPropType,
} from '../MultiValueControl.tsx';
import {
ResizeMode,
SelectedTrackType,
SelectedVideoTrackType,
VideoDecoderProperties,
VideoRef,
} from 'react-native-video';
import {StateType} from '../types';
import {
toast,
Seeker,
AudioTrackSelector,
TextTrackSelector,
VideoTrackSelector,
TopControl,
} from '../components';
type Props = {
channelDown: () => void;
channelUp: () => void;
setState: (value: StateType) => void;
state: StateType;
};
const _Overlay = forwardRef<VideoRef, Props>((props, ref) => {
const {state, setState, channelUp, channelDown} = props;
const popupInfo = useCallback(() => {
VideoDecoderProperties.getWidevineLevel().then((widevineLevel: number) => {
VideoDecoderProperties.isHEVCSupported().then((hevc: string) => {
VideoDecoderProperties.isCodecSupported('video/avc', 1920, 1080).then(
(avc: string) => {
toast(
true,
'Widevine level: ' +
widevineLevel +
'\n hevc: ' +
hevc +
'\n avc: ' +
avc,
);
},
);
});
});
}, []);
const toggleFullscreen = () => {
setState({...state, fullscreen: !state.fullscreen});
};
const toggleControls = () => {
setState({...state, showRNVControls: !state.showRNVControls});
};
const toggleDecoration = () => {
setState({...state, decoration: !state.decoration});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
ref.current?.setFullScreen(!state.decoration);
};
const toggleShowNotificationControls = () => {
setState({
...state,
showNotificationControls: !state.showNotificationControls,
});
};
const onSelectedAudioTrackChange = (itemValue: string) => {
console.log('on audio value change ' + itemValue);
if (itemValue === 'none') {
setState({
...state,
selectedAudioTrack: {
type: SelectedTrackType.DISABLED,
},
});
} else {
setState({
...state,
selectedAudioTrack: {
type: SelectedTrackType.INDEX,
value: itemValue,
},
});
}
};
const onSelectedTextTrackChange = (itemValue: string) => {
console.log('on value change ' + itemValue);
setState({
...state,
selectedTextTrack: {
type:
textTracksSelectionBy === 'index'
? SelectedTrackType.INDEX
: SelectedTrackType.LANGUAGE,
value: itemValue,
},
});
};
const onSelectedVideoTrackChange = (itemValue: string) => {
console.log('on value change ' + itemValue);
if (itemValue === undefined || itemValue === 'auto') {
setState({
...state,
selectedVideoTrack: {
type: SelectedVideoTrackType.AUTO,
},
});
} else {
setState({
...state,
selectedVideoTrack: {
type: SelectedVideoTrackType.INDEX,
value: itemValue,
},
});
}
};
const videoSeek = (position: number) => {
setState({...state, isSeeking: true});
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
ref.current?.seek(position);
};
const onRateSelected = (value: MultiValueControlPropType) => {
if (typeof value === 'number') {
setState({...state, rate: value});
}
};
const onVolumeSelected = (value: MultiValueControlPropType) => {
if (typeof value === 'number') {
setState({...state, volume: value});
}
};
const onResizeModeSelected = (value: MultiValueControlPropType) => {
if (typeof value === 'object') {
setState({...state, resizeMode: value});
}
};
return (
<>
<Indicator isLoading={state.isLoading} />
<View style={styles.topControls}>
<View style={styles.resizeModeControl}>
<TopControl
srcListId={state.srcListId}
showRNVControls={state.showRNVControls}
toggleControls={toggleControls}
/>
</View>
</View>
{!state.showRNVControls ? (
<>
<View style={styles.leftControls}>
<ToggleControl onPress={channelDown} text="ChDown" />
</View>
<View style={styles.rightControls}>
<ToggleControl onPress={channelUp} text="ChUp" />
</View>
<View style={styles.bottomControls}>
<View style={styles.generalControls}>
{isAndroid ? (
<View style={styles.generalControls}>
<ToggleControl onPress={popupInfo} text="decoderInfo" />
<ToggleControl
isSelected={state.useCache}
onPress={() => {
setState({...state, useCache: !state.useCache});
}}
selectedText="enable cache"
unselectedText="disable cache"
/>
</View>
) : null}
<ToggleControl
isSelected={state.paused}
onPress={() => {
setState({...state, paused: !state.paused});
}}
selectedText="pause"
unselectedText="playing"
/>
<ToggleControl
isSelected={state.loop}
onPress={() => {
setState({...state, loop: !state.loop});
}}
selectedText="loop enable"
unselectedText="loop disable"
/>
<ToggleControl onPress={toggleFullscreen} text="fullscreen" />
<ToggleControl onPress={toggleDecoration} text="decoration" />
<ToggleControl
isSelected={!!state.poster}
onPress={() => {
setState({
...state,
poster: state.poster ? undefined : samplePoster,
});
}}
selectedText="poster"
unselectedText="no poster"
/>
<ToggleControl
isSelected={state.showNotificationControls}
onPress={toggleShowNotificationControls}
selectedText="hide notification controls"
unselectedText="show notification controls"
/>
</View>
<View style={styles.generalControls}>
{/* shall be replaced by slider */}
<MultiValueControl
values={[0, 0.25, 0.5, 1.0, 1.5, 2.0]}
onPress={onRateSelected}
selected={state.rate}
/>
{/* shall be replaced by slider */}
<MultiValueControl
values={[0.5, 1, 1.5]}
onPress={onVolumeSelected}
selected={state.volume}
/>
<MultiValueControl
values={[
ResizeMode.COVER,
ResizeMode.CONTAIN,
ResizeMode.STRETCH,
]}
onPress={onResizeModeSelected}
selected={state.resizeMode}
/>
<ToggleControl
isSelected={state.muted}
onPress={() => {
setState({...state, muted: !state.muted});
}}
text="muted"
/>
{isIos ? (
<ToggleControl
isSelected={state.paused}
onPress={() => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
ref.current
?.save({})
?.then((response: unknown) => {
console.log('Downloaded URI', response);
})
.catch((error: unknown) => {
console.log('error during save ', error);
});
}}
text="save"
/>
) : null}
</View>
<Seeker
currentTime={state.currentTime}
duration={state.duration}
isLoading={state.isLoading}
videoSeek={prop => videoSeek(prop)}
isUISeeking={state.isSeeking}
/>
<View style={styles.generalControls}>
<AudioTrackSelector
audioTracks={state.audioTracks}
selectedAudioTrack={state.selectedAudioTrack}
onValueChange={onSelectedAudioTrackChange}
/>
<TextTrackSelector
textTracks={state.textTracks}
selectedTextTrack={state.selectedTextTrack}
onValueChange={onSelectedTextTrackChange}
textTracksSelectionBy={textTracksSelectionBy}
/>
<VideoTrackSelector
videoTracks={state.videoTracks}
selectedVideoTrack={state.selectedVideoTrack}
onValueChange={onSelectedVideoTrackChange}
/>
</View>
</View>
</>
) : null}
</>
);
});
export const Overlay = memo(_Overlay);