diff --git a/src/components/MediaPlayer/MediaPlayer.js b/src/components/MediaPlayer/MediaPlayer.js index 96e1b366..86d7442e 100644 --- a/src/components/MediaPlayer/MediaPlayer.js +++ b/src/components/MediaPlayer/MediaPlayer.js @@ -418,7 +418,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => { }; } setOptions(videoJsOptions); - }, [ready, canvasIndex, srcIndex, canvasIsEmpty]); + }, [ready, cIndex, srcIndex, canvasIsEmpty]); if ((ready && options != undefined) || canvasIsEmpty) { diff --git a/src/components/MediaPlayer/VideoJS/VideoJSPlayer.js b/src/components/MediaPlayer/VideoJS/VideoJSPlayer.js index 16c83274..9a0a7bcc 100644 --- a/src/components/MediaPlayer/VideoJS/VideoJSPlayer.js +++ b/src/components/MediaPlayer/VideoJS/VideoJSPlayer.js @@ -78,10 +78,12 @@ function VideoJSPlayer({ const autoAdvanceRef = React.useRef(); autoAdvanceRef.current = autoAdvance; + const srcIndexRef = React.useRef(); + srcIndexRef.current = srcIndex; + let activeIdRef = React.useRef(); activeIdRef.current = activeId; const setActiveId = (id) => { - console.log('setting active id'); _setActiveId(id); activeIdRef.current = id; }; @@ -92,7 +94,6 @@ function VideoJSPlayer({ let isReadyRef = React.useRef(); isReadyRef.current = isReady; const setIsReady = (r) => { - console.log('setting isReady'); _setIsReady(r); isReadyRef.current = r; }; @@ -112,7 +113,6 @@ function VideoJSPlayer({ let cIndexRef = React.useRef(); cIndexRef.current = canvasIndex; const setCIndex = (i) => { - console.log('setting canvas index'); _setCIndex(i); cIndexRef.current = i; }; @@ -539,7 +539,7 @@ function VideoJSPlayer({ */ React.useEffect(() => { if (playerRef.current !== null && isReadyRef.current) { - playerRef.current.currentTime(currentTime, playerDispatch({ type: 'resetClick' })); + playerRef.current.currentTime(currentTimeRef.current, playerDispatch({ type: 'resetClick' })); } }, [isClicked, isReady]); @@ -581,14 +581,22 @@ function VideoJSPlayer({ * change the player and the state accordingly. */ const handleEnded = () => { - if (!autoAdvanceRef.current) { + if (!autoAdvanceRef.current && !hasMultiItems) { return; } + // Remove all the existing structure related markers in the player if (playerRef.current && playerRef.current.markers && !isPlaylist) { playerRef.current.markers.removeAll(); } - if (structuresRef.current?.length > 0) { + if (hasMultiItems) { + // When there are multiple sources in a single canvas + // advance to next source + if (srcIndex + 1 < targets.length) { + manifestDispatch({ srcIndex: srcIndex + 1, type: 'setSrcIndex' }); + playerDispatch({ currentTime: 0, type: 'setCurrentTime' }); + } + } else if (structuresRef.current?.length > 0) { const nextItem = structuresRef.current[cIndexRef.current + 1]; if (nextItem && nextItem != undefined) { @@ -632,16 +640,6 @@ function VideoJSPlayer({ } } } - // else if (hasMultiItems) { - // // When there are multiple sources in a single canvas - // // advance to next source - // if (srcIndex + 1 < targets.length) { - // manifestDispatch({ srcIndex: srcIndex + 1, type: 'setSrcIndex' }); - // } else { - // manifestDispatch({ srcIndex: 0, type: 'setSrcIndex' }); - // } - // playerDispatch({ currentTime: 0, type: 'setCurrentTime' }); - // } }; /** @@ -654,13 +652,18 @@ function VideoJSPlayer({ const handleTimeUpdate = () => { const player = playerRef.current; if (player !== null && isReadyRef.current) { - const activeSegment = getActiveSegment(player.currentTime()); + let playerTime = player.currentTime() || currentTimeRef.current; + if (hasMultiItems && srcIndexRef.current > 0) { + playerTime = playerTime + targets[srcIndexRef.current].altStart; + } + const activeSegment = getActiveSegment(playerTime); if (activeSegment && activeIdRef.current != activeSegment['id']) { - // Set the active segment id in component's state + // Set the active segment in state setActiveId(activeSegment['id']); - // setIsContained(true); - updatePlayerMarkers(activeSegment, player); manifestDispatch({ item: activeSegment, type: 'switchItem' }); + + // Update player markers + updatePlayerMarkers(activeSegment, player); } else if (activeSegment === null && player.markers) { cleanUpNav(); } @@ -674,7 +677,7 @@ function VideoJSPlayer({ * @param {Object} player Video.js player instance */ const updatePlayerMarkers = (activeSegment, player) => { - if (activeSegment !== null && !isPlaylist) { + if (!isPlaylist) { if (player.markers) { // Remove all existing structure higlights if (!isPlaylist) { @@ -735,7 +738,13 @@ function VideoJSPlayer({ manifestDispatch({ item: null, type: 'switchItem' }); } setActiveId(null); - // setIsContained(false); + const player = playerRef.current; + if (player.markers) { + // Remove all existing structure higlights + if (!isPlaylist) { + player.markers.removeAll(); + } + } }; /** diff --git a/src/components/MediaPlayer/VideoJS/components/js/VideoJSCurrentTime.js b/src/components/MediaPlayer/VideoJS/components/js/VideoJSCurrentTime.js index ed54f88b..eb86e274 100644 --- a/src/components/MediaPlayer/VideoJS/components/js/VideoJSCurrentTime.js +++ b/src/components/MediaPlayer/VideoJS/components/js/VideoJSCurrentTime.js @@ -77,7 +77,6 @@ function CurrentTimeDisplay({ player, options }) { } const { start, altStart } = targets[player.srcIndex]; - console.log('CURENT TIME: ', time, start, altStart, player.srcIndex); if (altStart != start && player.srcIndex > 0) { time = time + altStart; } diff --git a/src/components/MediaPlayer/VideoJS/components/js/VideoJSProgress.js b/src/components/MediaPlayer/VideoJS/components/js/VideoJSProgress.js index 4cd12b8c..8260ebd3 100644 --- a/src/components/MediaPlayer/VideoJS/components/js/VideoJSProgress.js +++ b/src/components/MediaPlayer/VideoJS/components/js/VideoJSProgress.js @@ -51,7 +51,7 @@ class VideoJSProgress extends vjsComponent { /** Build progress bar elements from the options */ initProgressBar() { const { targets, duration, srcIndex } = this.options; - const { start, end, altStart } = targets[srcIndex]; + const { start, end } = targets[srcIndex]; let startTime = start, endTime = end; @@ -60,7 +60,12 @@ class VideoJSProgress extends vjsComponent { let toPlay; if (isMultiSourced) { - toPlay = 100; + // Calculate the width of the playable range as a percentage of total + // Canvas duration + toPlay = Math.min( + 100, + Math.max(0, 100 * ((end - start) / totalDuration)) + ); } else { const leftBlock = (startTime * 100) / duration; const rightBlock = ((duration - endTime) * 100) / duration; @@ -97,12 +102,10 @@ class VideoJSProgress extends vjsComponent { * is playing * @param {Number} curTime current time of the player */ - handleTimeUpdate(curTime, duration) { - console.log('HANDLE TIMEUPDATE: ', curTime, duration); - + handleTimeUpdate(curTime) { const { player, options, el_ } = this; const { srcIndex, targets } = options; - const { start, end, altStart } = targets[srcIndex]; + const { start, end } = targets[srcIndex]; // Avoid null player instance when Video.js is getting initialized if (!el_ || !player) { @@ -110,11 +113,6 @@ class VideoJSProgress extends vjsComponent { } const nextItems = targets.filter((_, index) => index > srcIndex); - - // if (altStart != start) { - // curTime = altStart + curTime; - // player.currentTime(curTime); - // } // Restrict access to the intended range in the media file if (curTime < start) { player.currentTime(start); @@ -122,11 +120,11 @@ class VideoJSProgress extends vjsComponent { // Some items, particularly in playlists, were not having `player.ended()` properly // set by the 'ended' event. Providing a fallback check that the player is already // paused prevents undesirable behavior from excess state changes after play ending. - if (curTime > end && !player.paused() && !player.isDisposed()) { - console.log('next item: ', targets[0].start); - // if (nextItems.length == 0) options.nextItemClicked(0, targets[0].start); + if (curTime >= end && !player.paused() && !player.isDisposed()) { + if (nextItems.length == 0) { options.nextItemClicked(0, targets[0].start); } player.pause(); - // player.trigger('ended'); + player.trigger('ended'); + // On the next play event set the time to start or a seeked time // in between the 'ended' event and 'play' event @@ -148,21 +146,17 @@ class VideoJSProgress extends vjsComponent { for (let slider of dummySliders) { const sliderIndex = slider.dataset.srcindex; if (sliderIndex < srcIndex) { - slider.style.setProperty('background', '#477076'); + slider.style.setProperty('background', '#2A5459'); } } // Calculate the played percentage of the media file's duration let trackoffset = curTime - start; - if (altStart != start && curTime < duration && curTime < altStart) { - trackoffset = trackoffset + altStart; - } const played = Math.min( 100, - Math.max(0, 100 * trackoffset / (duration)) + Math.max(0, 100 * trackoffset / (end - start)) ); - console.log(played, curTime); document.documentElement.style.setProperty( '--range-progress', `calc(${played}%)` @@ -216,8 +210,6 @@ function ProgressBar({ const [canvasTimes, setCanvasTimes] = React.useState(times); const [activeSrcIndex, setActiveSrcIndex] = React.useState(0); const [canvasTargets, setCanvasTargets] = React.useState(targets); - const [totalDuration, setTotalDuration] = React.useState({ start: null, end: null }); - const isMultiSourced = targets.length > 1 ? true : false; let initTimeRef = React.useRef(initCurrentTime); @@ -232,20 +224,11 @@ function ProgressBar({ let playerEventListener; - const calculateTotalDuration = () => { - // You could fetch real durations via the metadata of each video if needed - let duration = canvasTargets.reduce((acc, t) => acc + t.duration, 0); - if (isNaN(duration)) { duration = canvasTargets[0].end; } - setTotalDuration({ start: canvasTargets[0].start, end: duration }); - }; - React.useEffect(() => { if (player.targets?.length > 0) { setCanvasTargets(player.targets); } - calculateTotalDuration(); - // Position the timetool tip at the first load if (timeToolRef.current && sliderRangeRef.current) { timeToolRef.current.style.top = @@ -283,6 +266,13 @@ function ProgressBar({ timeUpdateHandler(); }, 100); + /* + Set playable duration and alternate start as player properties to use in + track scrubber component, when displaying playlist manifests + */ + player.playableDuration = calculateTotalDuration() || player.duration(); + player.altStart = canvasTargets[activeSrcIndex].altStart; + // Get the pixel ratio for the range const ratio = sliderRangeRef.current.offsetWidth / (canvasTimes.end - canvasTimes.start); @@ -306,10 +296,6 @@ function ProgressBar({ leftWidth - timeToolRef.current.offsetWidth / 2 + 'px'; }, [player.src()]); - // player.on('timeupdate', () => { - // timeUpdateHandler(); - // }); - // Update progress bar with timeupdate in the player const timeUpdateHandler = () => { if (player.isDisposed() || player.ended() || player == null) { return; } @@ -319,7 +305,7 @@ function ProgressBar({ // this accounts for structured navigation when switching canvases if ((initTimeRef.current > 0 && player.currentTime() == 0)) { curTime = initTimeRef.current; - // player.currentTime(initTimeRef.current); + player.currentTime(initTimeRef.current); } else { curTime = player.currentTime(); } @@ -327,7 +313,7 @@ function ProgressBar({ // player. iOS player handles its own progress bar, so we can skip the // update here. if (!iOS) { setProgress(curTime); } - handleTimeUpdate(curTime, totalDuration.end); + handleTimeUpdate(curTime); setInitTime(0); }; @@ -335,15 +321,6 @@ function ProgressBar({ clearInterval(playerEventListener); }); - player.on('canplay', () => { - /* - Set playable duration and alternate start as player properties to use in - track scrubber component, when displaying playlist manifests - */ - player.playableDuration = (canvasTimes.end - canvasTimes.start) || player.duration(); - player.altStart = canvasTimes.start; - }); - // Update our progress bar after the user leaves full screen player.on("fullscreenchange", (e) => { if (!player.isFullscreen()) { @@ -358,11 +335,14 @@ function ProgressBar({ * @returns time equvalent of the hovered position */ const convertToTime = (e, index) => { - let time = - (e.nativeEvent.offsetX / e.target.clientWidth) * (e.target.max - e.target.min) - ; - if (index != undefined) time += canvasTimes.altStart; - return time; + let offsetx = e.nativeEvent.offsetX; + if (offsetx && offsetx != undefined) { + let time = + (offsetx / e.target.clientWidth) * (e.target.max - e.target.min) + ; + if (index != undefined) time += canvasTargets[index].altStart; + return time; + } }; /** @@ -371,25 +351,14 @@ function ProgressBar({ * @param {Object} e onChange event for input range */ const updateProgress = (e) => { - let time = getTrackTime(e); - const { start, end, altStart } = canvasTimes; - if (time != undefined) { - const clickedTarget = canvasTargets.filter(ct => time >= ct.altStart && time < ct.end)[0]; - - if (clickedTarget != undefined && clickedTarget.sIndex != activeSrcIndex) { - setActiveSrcIndex(clickedTarget.sIndex); - nextItemClicked(clickedTarget.sIndex, time - clickedTarget.altStart); - } - setProgress(time); - setCurrentTime(time); - } else { - time = currentTime; - if (activeSrcIndex > 0) time -= altStart; + let time = currentTime; - if (time >= start && time <= end) { - player.currentTime(time); - setProgress(time); - } + if (activeSrcIndex > 0) time -= targets[activeSrcIndex].altStart; + + const { start, end } = canvasTimes; + if (time >= start && time <= end) { + player.currentTime(time); + setProgress(time); } }; @@ -405,7 +374,11 @@ function ProgressBar({ if (isDummy) { currentSrcIndex = e.target.dataset.srcindex; } - let time = getTrackTime(e); + let time = convertToTime(e, currentSrcIndex); + + setActiveSrcIndex(currentSrcIndex); + setCurrentTime(time); + // Set text in the tooltip as the time relevant to the pointer event's position timeToolRef.current.innerHTML = formatTooltipTime(time); @@ -427,16 +400,6 @@ function ProgressBar({ } }; - const getTrackTime = (e) => { - let offsetx = e.nativeEvent.offsetX; - if (offsetx && offsetx != undefined) { - let time = - (offsetx / e.target.clientWidth) * totalDuration.end - ; - return time; - } - }; - /** * Initiate the switch of the src when clicked on an inactive * range. Update srcIndex in the parent components. @@ -444,29 +407,22 @@ function ProgressBar({ */ const handleClick = (e) => { const clickedSrcIndex = parseInt(e.target.dataset.srcindex); - let time = getTrackTime(e); - - // if (trackoffset != undefined) { - // // Calculate percentage of the progress based on the pointer position's - // // time and duration of the track - // let trackpercent = Math.min( - // 100, - // Math.max(0, 100 * trackoffset / totalDuration.end) - // ); - - // // Set player's current time as addition of start time of the track and offset - // player.currentTime(currentTrackRef.current.time + trackoffset); - // } + let time = currentTime; // Deduct the duration of the preceding ranges if (clickedSrcIndex > 0) { - time = canvasTargets[clickedSrcIndex - 1].duration - time; + time -= canvasTargets[clickedSrcIndex - 1].duration; } - setActiveSrcIndex(clickedSrcIndex); - setCurrentTime(time); nextItemClicked(clickedSrcIndex, time); }; + const calculateTotalDuration = () => { + // You could fetch real durations via the metadata of each video if needed + let duration = canvasTargets.reduce((acc, t) => acc + t.duration, 0); + if (isNaN(duration)) { duration = canvasTargets[0].end; } + return duration; + }; + const formatTooltipTime = (time) => { const { start, end } = canvasTimes; if (isMultiSourced) { @@ -499,6 +455,10 @@ function ProgressBar({ const createRange = (tInRange) => { let elements = []; tInRange.map((t) => { + let widthPercent = Math.min( + 100, + Math.max(0, 100 * (t.duration / calculateTotalDuration())) + ); elements.push( ); }); @@ -523,46 +484,48 @@ function ProgressBar({