UNPKG

@100mslive/react-native-room-kit

Version:

100ms Room Kit provides simple & easy to use UI components to build Live Streaming & Video Conferencing experiences in your apps.

276 lines (266 loc) 9.79 kB
import React, { useRef } from 'react'; import { View, StyleSheet } from 'react-native'; import Animated, { Easing, Extrapolation, interpolate, runOnJS, useAnimatedStyle, useDerivedValue, useSharedValue, withTiming } from 'react-native-reanimated'; import { GestureDetector, Gesture } from 'react-native-gesture-handler'; import { HMSHLSPlayerPlaybackState, HMSHLSPlaylistType, useHMSHLSPlayerPlaybackState, useHMSHLSPlayerStat } from '@100mslive/react-native-hms'; import { useSelector } from 'react-redux'; import { useHLSStreamResumePause, useHLSViewsConstraints, useHMSRoomStyle } from '../hooks-util'; import { useIsHLSStreamingOn } from '../hooks-sdk'; export const _HLSSeekbar = props => { const isHLSStreamingOn = useIsHLSStreamingOn(); const isDVRStream = useSelector(state => { var _state$hmsStates$room; return ((_state$hmsStates$room = state.hmsStates.room) === null || _state$hmsStates$room === void 0 || (_state$hmsStates$room = _state$hmsStates$room.hlsStreamingState.variants) === null || _state$hmsStates$room === void 0 || (_state$hmsStates$room = _state$hmsStates$room[0]) === null || _state$hmsStates$room === void 0 ? void 0 : _state$hmsStates$room.playlistType) === HMSHLSPlaylistType.DVR; }); if (!isHLSStreamingOn || !isDVRStream) return null; return /*#__PURE__*/React.createElement(_Seekbar, props); }; const _Seekbar = /*#__PURE__*/React.memo(({ playerRef, onStart, onEnd }) => { const { playerWrapperConstraints } = useHLSViewsConstraints(); const hlsPlayerDurationDetails = useRef(null); const prevSeekbarPositionValue = useSharedValue(0); const seekbarPositionValue = useSharedValue(0); const seekbarHeightValue = useSharedValue(4); const streamStartedAt = useSelector(state => { var _state$hmsStates$room2; return (_state$hmsStates$room2 = state.hmsStates.room) === null || _state$hmsStates$room2 === void 0 || (_state$hmsStates$room2 = _state$hmsStates$room2.hlsStreamingState.variants) === null || _state$hmsStates$room2 === void 0 || (_state$hmsStates$room2 = _state$hmsStates$room2[0]) === null || _state$hmsStates$room2 === void 0 ? void 0 : _state$hmsStates$room2.startedAt; }); const distanceFromLiveEdge = useHMSHLSPlayerStat('distanceFromLive'); const distanceFromLiveEdgeValue = useDerivedValue(() => distanceFromLiveEdge, [distanceFromLiveEdge]); const seekbarRoomStyle = useHMSRoomStyle(theme => ({ backgroundColor: theme.palette.primary_default })); const { isPaused, pauseStream, resumeStream } = useHLSStreamResumePause(playerRef); const seekToBarPosition = () => { var _hlsPlayerDurationDet; if (!streamStartedAt) return; const liveStreamDuration = Date.now() - streamStartedAt.getTime(); const rollingWindowTime = (_hlsPlayerDurationDet = hlsPlayerDurationDetails.current) === null || _hlsPlayerDurationDet === void 0 ? void 0 : _hlsPlayerDurationDet.rollingWindowTime; const durationOfStream = rollingWindowTime && liveStreamDuration >= rollingWindowTime ? rollingWindowTime : liveStreamDuration; // console.log( // '$$$ rolling window time > ', // hlsPlayerDurationDetails.current?.rollingWindowTime // ); // console.log('$$$ live stream duration > ', liveStreamDuration); // console.log('$$$ current stream duration > ', durationOfStream); const prevPosition = prevSeekbarPositionValue.value; const currPosition = seekbarPositionValue.value; const diff = currPosition - prevPosition; const seekByMillis = durationOfStream / 100 * Math.abs(diff); const seekBySecs = seekByMillis / 1000; if (diff >= 0) { var _playerRef$current; (_playerRef$current = playerRef.current) === null || _playerRef$current === void 0 || _playerRef$current.seekForward(seekBySecs); } else { var _playerRef$current2; (_playerRef$current2 = playerRef.current) === null || _playerRef$current2 === void 0 || _playerRef$current2.seekBackward(seekBySecs); } resumeStream(); }; const activeSeekbarStyle = useAnimatedStyle(() => ({ width: interpolate(seekbarPositionValue.value, [0, 100], [0, playerWrapperConstraints.width], { extrapolateLeft: Extrapolation.CLAMP, extrapolateRight: Extrapolation.CLAMP }), height: seekbarHeightValue.value }), [playerWrapperConstraints.width]); const activeSeekbarThumbStyle = useAnimatedStyle(() => ({ width: seekbarHeightValue.value * 3, height: seekbarHeightValue.value * 3, borderRadius: seekbarHeightValue.value * 3 / 2, bottom: -seekbarHeightValue.value, left: -(seekbarHeightValue.value * 3) / 2, transform: [{ translateX: interpolate(seekbarPositionValue.value, [0, 100], [0, playerWrapperConstraints.width], { extrapolateLeft: Extrapolation.CLAMP, extrapolateRight: Extrapolation.CLAMP }) }] }), [playerWrapperConstraints.width]); const panGesture = Gesture.Pan().maxPointers(1).minPointers(1).hitSlop({ top: 12, bottom: 12 }).onStart(e => { 'worklet'; onStart(); runOnJS(pauseStream)(); prevSeekbarPositionValue.value = seekbarPositionValue.value; seekbarPositionValue.value = interpolate(e.x, [0, playerWrapperConstraints.width], [0, 100], { extrapolateLeft: Extrapolation.CLAMP, extrapolateRight: Extrapolation.CLAMP }); seekbarHeightValue.value = withTiming(6, { duration: 160, easing: Easing.linear }); }).onUpdate(e => { 'worklet'; seekbarPositionValue.value = interpolate(e.x, [0, playerWrapperConstraints.width], [0, 100], { extrapolateLeft: Extrapolation.CLAMP, extrapolateRight: Extrapolation.CLAMP }); }).onEnd(() => { 'worklet'; onEnd(); seekbarHeightValue.value = withTiming(4, { duration: 160, easing: Easing.linear }); runOnJS(seekToBarPosition)(); }); React.useEffect(() => { if (streamStartedAt && !isPaused) { const intervalId = setInterval(() => { var _hlsPlayerDurationDet2; const liveStreamDuration = Date.now() - streamStartedAt.getTime(); const rollingWindowTime = (_hlsPlayerDurationDet2 = hlsPlayerDurationDetails.current) === null || _hlsPlayerDurationDet2 === void 0 ? void 0 : _hlsPlayerDurationDet2.rollingWindowTime; const durationOfStream = rollingWindowTime && liveStreamDuration >= rollingWindowTime ? rollingWindowTime : liveStreamDuration; // console.log( // '$$$ rolling window time > ', // hlsPlayerDurationDetails.current?.rollingWindowTime // ); // console.log('$$$ live stream duration > ', liveStreamDuration); // console.log('$$$ current stream duration > ', durationOfStream); const currentPosition = durationOfStream - distanceFromLiveEdgeValue.value; // console.log('currentPosition > ', currentPosition); seekbarPositionValue.value = interpolate(currentPosition, [0, durationOfStream], [0, 100], { extrapolateLeft: Extrapolation.CLAMP, extrapolateRight: Extrapolation.CLAMP }); }, 1000); return () => { clearInterval(intervalId); }; } }, [isPaused, streamStartedAt]); let startedPlayingFirstTime = false; let prevPlaybackState = HMSHLSPlayerPlaybackState.UNKNOWN; const playbackState = useHMSHLSPlayerPlaybackState(); if (prevPlaybackState === HMSHLSPlayerPlaybackState.UNKNOWN && playbackState === HMSHLSPlayerPlaybackState.PLAYING) { prevPlaybackState = playbackState; startedPlayingFirstTime = true; } React.useEffect(() => { if (startedPlayingFirstTime && playerRef.current) { let mounted = true; playerRef.current.getPlayerDurationDetails().then(data => { if (mounted) { console.log('$$$ getPlayerDurationDetails > ', data); hlsPlayerDurationDetails.current = data; if (typeof data.rollingWindowTime === 'number' && data.rollingWindowTime < 300000) { hlsPlayerDurationDetails.current.rollingWindowTime = 300000; } } }).catch(() => {}); return () => { mounted = false; }; } }, [startedPlayingFirstTime]); return /*#__PURE__*/React.createElement(GestureDetector, { gesture: panGesture }, /*#__PURE__*/React.createElement(View, { collapsable: false, hitSlop: { top: 12, bottom: 12 }, style: [styles.container, { width: playerWrapperConstraints.width }] }, /*#__PURE__*/React.createElement(Animated.View, { style: [styles.inactiveSeekbar, { width: playerWrapperConstraints.width }] }, /*#__PURE__*/React.createElement(Animated.View, { style: [styles.seekbar, seekbarRoomStyle, activeSeekbarStyle] }), /*#__PURE__*/React.createElement(Animated.View, { style: [styles.seekbarThumb, seekbarRoomStyle, activeSeekbarThumbStyle] })))); }); export const HLSSeekbar = /*#__PURE__*/React.memo(_HLSSeekbar); const styles = StyleSheet.create({ container: { height: 6, justifyContent: 'flex-end' }, inactiveSeekbar: { position: 'relative', backgroundColor: 'rgba(255, 255, 255, 0.25)' }, seekbar: { height: 4 }, seekbarThumb: { position: 'absolute', bottom: -4, right: 0, zIndex: 100 } }); /** * * * * * * * * * * 200 -> 200 * 40% * * 180 - 33.33% = 60; * * currentStreamDuration * (diff in current and prev) * 100 / prev * * 0 |----------| 150 * prev = 135 * * curr = 90 * * * * * * * * * * //// 2 * 30 = 60 * 1.5 * 30 = 45 * * 180 * * 120 * * 0 |------____| 100 * * * 200 * 40% = 80 * * 200 * 30% = 60 * * * 30*90 * * 0 |---------_| 100 * Prev = 90 * |------| * Curr = 60 * * 90 - 60 = 30 * * seekbackward(80) */ //# sourceMappingURL=HLSSeekbar.js.map