@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
JavaScript
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