UNPKG

ovenplayer

Version:

OvenPlayer is Open-Source HTML5 Player. OvenPlayer supports WebRTC Signaling from OvenMediaEngine for Sub-Second Latency Streaming.

438 lines (332 loc) 14.1 kB
/** * Created by hoho on 2018. 7. 24.. */ import OvenTemplate from "view/engine/OvenTemplate"; import PanelManager from "view/global/PanelManager"; import { naturalHms } from "utils/strings" import LA$ from "utils/likeA$"; import { CONTENT_TIME, CONTENT_BUFFER, AD_TIME, PROVIDER_HLS, PROVIDER_HTML5 } from "api/constants"; const ProgressBar = function ($container, api, isAd, metadata) { const $root = LA$(api.getContainerElement()); if (api.getConfig().disableSeekUI) { $container.addClass('op-progressbar-container-disabled'); } let currentPlayingPosition = 0; let currentPlayingPercentage = 0; let currentLoadedPercentage = 0; let mouseInside = false, mouseDown = false; let panelManager = PanelManager(); let adDuration = 0; let lastGridThumbnail = ""; let durationForCalc = 0; let $progressBar = "", $progressLoad = "", $progressPlay = "", $progressHover = "", $knobContainer = "", $knob = "", knobWidth = 0, $time = "", $preview = ""; let isMobile = api.getBrowser().mobile; const mediaElement = api.getMediaElement(); let hlsLive = false; let nativeHlsLive = false; if (metadata && metadata.type === PROVIDER_HLS && metadata.duration === Infinity) { hlsLive = true; if (api.getProviderName() === PROVIDER_HTML5) { nativeHlsLive = true; } } function positionElements(percentage) { let progressBarWidth = $progressBar.width(); let position = progressBarWidth * percentage; $progressPlay.css("width", position + "px"); $progressHover.css("left", position + "px"); let knobPostion = (progressBarWidth - knobWidth) * percentage; $knobContainer.css("left", knobPostion + "px"); currentPlayingPosition = position; currentPlayingPercentage = percentage; } function drawHoverProgress(percentage) { let progressBarWidth = $progressBar.width(); let hoverPosition = progressBarWidth * percentage; $progressHover.css("width", (percentage === 0 ? percentage : (hoverPosition - currentPlayingPosition)) + "px"); } function drawLoadProgress(percentage) { let progressBarWidth = $progressBar.width(); let loadPosition = progressBarWidth * percentage; $progressLoad.css("width", loadPosition + "px"); currentLoadedPercentage = percentage; } function calculatePercentage(event) { let progressBarWidth = $progressBar.width(); let progressBarOffsetX = $progressBar.offset().left; let pointerOffsetX = event.pageX; if (event.touches) { pointerOffsetX = (event.pageX || event.touches[0].clientX); } let percentage = (pointerOffsetX - progressBarOffsetX) / progressBarWidth; if (percentage < 0) { return 0; } if (percentage > 1) { return 1; } return percentage; } function getNativeHlsDvrWindow() { return mediaElement.seekable.end(mediaElement.seekable.length - 1) - mediaElement.seekable.start(0); } function drawTimeIndicator(percentage, event) { if (panelManager.size() > 0 || percentage === -1) { $time.hide(); $preview.hide(); return; } else { $time.show(); $preview.show(); } let duration = 0; let second = 0; if (hlsLive && !nativeHlsLive) { duration = api.getDvrWindow(); second = duration * (1 - percentage); if (api.isTimecodeMode()) { $time.text('- ' + naturalHms(second)); } else { $time.text('- ' + Math.round(second * api.getFramerate())); } } else if (hlsLive && nativeHlsLive) { duration = getNativeHlsDvrWindow(); second = duration * (1 - percentage); if (api.isTimecodeMode()) { $time.text('- ' + naturalHms(second)); } else { $time.text('- ' + Math.round(second * api.getFramerate())); } } else { duration = api.getDuration(); second = duration * percentage; if (api.isTimecodeMode()) { $time.text(naturalHms(second)); } else { $time.text(Math.round(second * api.getFramerate())); } } let timeElemWidth = $time.width(); let progressBarWidth = $progressBar.width(); let position = progressBarWidth * percentage; let positionOfPixel = event.pageX - $progressBar.offset().left; if (event.touches) { positionOfPixel = (event.pageX || event.touches[0].clientX) - $progressBar.offset().left; } const calculateMagnetic = function (elementWidth) { if (positionOfPixel < elementWidth / 2) { return 0; } else if (progressBarWidth - positionOfPixel < elementWidth / 2) { return progressBarWidth - elementWidth; } else { return position - elementWidth / 2; } }; let magneticPosition = calculateMagnetic(timeElemWidth); $time.css("left", magneticPosition + "px"); if (api.getSources()[api.getCurrentSource()].gridThumbnail) { let interval = api.getConfig().gridThumbnail.thumbnailInterval; let width = api.getConfig().gridThumbnail.originalThumbnailWidth; let height = api.getConfig().gridThumbnail.originalThumbnailHeight; let columnCount = api.getConfig().gridThumbnail.columnCount; let rowCount = api.getConfig().gridThumbnail.rowCount; let scale = api.getConfig().gridThumbnail.resizeScale; $preview.css('width', width * scale + 'px'); $preview.css('height', height * scale + 'px'); $preview.css('background-size', width * scale * columnCount + 'px ' + height * scale * rowCount + 'px'); let thumbnailNumber = Math.floor(second / interval); let imageNumber = Math.floor(thumbnailNumber / (columnCount * rowCount)); let rowNumber = Math.floor((thumbnailNumber % (columnCount * rowCount)) / columnCount); let columnNumber = (thumbnailNumber % (columnCount * rowCount)) % columnCount; let left = -1 * columnNumber * width * scale; let top = -1 * rowNumber * height * scale; OvenPlayerConsole.log('Grid Thumbnail:', thumbnailNumber + ': ' + imageNumber + '(' + rowNumber + ', ' + columnNumber + ')'); let thumbnails = api.getSources()[api.getCurrentSource()].gridThumbnail; let thumbnail = thumbnails[imageNumber]; if (lastGridThumbnail !== thumbnail) { $preview.css('background-image', 'url(' + thumbnail + ')'); lastGridThumbnail = thumbnail; } $preview.css('background-position', 'left ' + left + 'px top ' + top + 'px'); let previewMagneticPosition = calculateMagnetic(width * scale); $preview.css("left", previewMagneticPosition + "px"); } else { $preview.hide(); } } function seek(percentage) { let time = (durationForCalc || 0) * percentage; if (hlsLive && nativeHlsLive) { const dvrWindow = getNativeHlsDvrWindow(); time = (durationForCalc - dvrWindow) + dvrWindow * percentage; } let sectionStart = api.getSources()[api.getCurrentSource()].sectionStart; if (sectionStart && sectionStart > 0) { time = time + sectionStart; } api.seek(time); } const onRendered = function ($current, template) { $progressBar = $current; $progressLoad = $current.find(".op-load-progress"); $progressPlay = $current.find(".op-play-progress"); $progressHover = $current.find(".op-hover-progress"); $knobContainer = $current.find(".op-progressbar-knob-container"); $knob = $current.find(".op-progressbar-knob"); knobWidth = $knob.width(); $time = $current.find(".op-progressbar-time"); $preview = $current.find(".op-progressbar-preview"); if (isAd) { api.on(AD_TIME, function (data) { if (data && data.duration && typeof data.position === "number") { positionElements(data.position / data.duration); adDuration = data.duration; } }, template); } else { api.on(CONTENT_TIME, function (data) { if (data && data.duration && typeof data.position === "number") { durationForCalc = data.duration; let percentage = data.position / data.duration; if (hlsLive && !nativeHlsLive) { percentage = (api.getDvrWindow() - (data.duration - data.position)) / api.getDvrWindow(); } if (hlsLive && nativeHlsLive) { const dvrWindow = getNativeHlsDvrWindow(); durationForCalc = dvrWindow; const position = Math.min(dvrWindow, data.position); percentage = (dvrWindow - (dvrWindow - position)) / dvrWindow; } positionElements(percentage); } }, template); api.on(CONTENT_BUFFER, function (data) { if (data && data.bufferPercent) { drawLoadProgress(data.bufferPercent / 100); } }, template); } }; const onDestroyed = function (template) { if (isAd) { api.off(AD_TIME, null, template); } else { api.off(CONTENT_TIME, null, template); api.off(CONTENT_BUFFER, null, template); } }; let events = { "touchstart .op-progressbar": function (event) { if (isAd) { return false; } mouseDown = true; const percentage = calculatePercentage(event); if (percentage === -1) { return false; } positionElements(percentage); drawHoverProgress(0); seek(percentage); }, "touchmove .op-progressbar": function (event) { if (mouseDown) { const percentage = calculatePercentage(event); if (percentage === -1) { return false; } positionElements(percentage); drawHoverProgress(0); seek(percentage); drawTimeIndicator(percentage, event); } }, "touchend .op-progressbar": function (event) { if (mouseDown) { mouseDown = false; } $root.removeClass("op-progressbar-hover"); $time.hide(); $preview.hide(); }, "mouseenter .op-progressbar": function (event, $current, template) { event.preventDefault(); if (!isMobile) { if (!isAd) { mouseInside = true; $time.show(); } $root.addClass("op-progressbar-hover"); } }, "mouseleave .op-progressbar": function (event, $current, template) { event.preventDefault(); mouseInside = false; mouseDown = false; if (!mouseInside) { $root.removeClass("op-progressbar-hover"); $time.hide(); $preview.hide(); } drawHoverProgress(0); }, "mousedown .op-progressbar": function (event, $current, template) { event.preventDefault(); if (isAd || isMobile) { return false; } mouseDown = true; const percentage = calculatePercentage(event); if (percentage === -1) { return false; } positionElements(percentage); drawHoverProgress(0); seek(percentage); }, "mousemove .op-progressbar": function (event, $current, template) { event.preventDefault(); if (!mouseDown && !isAd && !isMobile) { const percentage = calculatePercentage(event); drawHoverProgress(percentage); drawTimeIndicator(percentage, event); } if (mouseDown && !isMobile) { const percentage = calculatePercentage(event); if (percentage === -1) { return false; } positionElements(percentage); drawHoverProgress(0); seek(percentage); drawTimeIndicator(percentage, event); } }, "mouseup .op-progressbar": function (event, $current, template) { event.preventDefault(); if (mouseDown && !isMobile) { mouseDown = false; $root.removeClass("op-progressbar-hover"); } } }; if (api.getConfig().disableSeekUI) { events = {} } return OvenTemplate($container, "ProgressBar", api.getConfig(), null, events, onRendered, onDestroyed); }; export default ProgressBar;