UNPKG

ovenplayer

Version:

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

342 lines (258 loc) 10.5 kB
/** * Created by hoho on 2018. 6. 7.. */ import Provider from "api/provider/html5/Provider"; import { errorTrigger } from "api/provider/utils"; import { PROVIDER_HLS, PLAYER_STATE, STATE_IDLE, STATE_LOADING, ERRORS, INIT_HLSJS_FAIL, HLS_PREPARED, HLS_DESTROYED, PLAYER_UNKNWON_NETWORK_ERROR, PLAYER_BAD_REQUEST_ERROR, PLAYER_AUTH_FAILED_ERROR, PLAYER_NOT_ACCEPTABLE_ERROR, CONTENT_LEVEL_CHANGED, AUDIO_TRACK_CHANGED, SUBTITLE_TRACK_CHANGED } from "api/constants"; import sizeHumanizer from "utils/sizeHumanizer"; /** * @brief hlsjs provider extended core. * @param container player element. * @param playerConfig config. * */ const HlsProvider = function (element, playerConfig, adTagUrl) { let that = {}; let hls = null; let superStop_func = null; let superDestroy_func = null; let loadRetryer = null; let isManifestLoaded = false; let firstLoaded = false; try { let hlsConfig = { debug: false }; let hlsConfigFromPlayerConfig = playerConfig.getConfig().hlsConfig; if (hlsConfigFromPlayerConfig) { for (let key in hlsConfigFromPlayerConfig) { hlsConfig[key] = hlsConfigFromPlayerConfig[key]; } } if (playerConfig.getConfig().licenseCustomHeader) { const licenseXhrSetup = function (xhr, url, keyContext, licenseChallenge) { xhr.setRequestHeader(playerConfig.getConfig().licenseCustomHeader.key, playerConfig.getConfig().licenseCustomHeader.value); }; hlsConfig.licenseXhrSetup = licenseXhrSetup; } hls = new Hls(hlsConfig); hls.attachMedia(element); let spec = { name: PROVIDER_HLS, element: element, mse: hls, listener: null, isLoaded: false, canSeek: false, isLive: false, seeking: false, state: STATE_IDLE, buffer: 0, dvrWindow: 0, framerate: 0, currentQuality: -1, qualityLevels: [], currentAudioTrack: -1, audioTracks: [], currentSource: -1, sources: [], adTagUrl: adTagUrl, subtitleTracks: [], currentSubtitleTrack: -1 }; that = Provider(spec, playerConfig, function (source, lastPlayPosition) { OvenPlayerConsole.log("HLS : onExtendedLoad : ", source, "lastPlayPosition : " + lastPlayPosition); that.trigger(HLS_PREPARED, hls); hls.loadSource(source.file); hls.once(Hls.Events.MANIFEST_LOADED, function (event, data) { isManifestLoaded = true; for (let i = 0; i < hls.levels.length; i++) { let qualityLevel = hls.levels[i]; spec.qualityLevels.push({ bitrate: qualityLevel.bitrate, height: qualityLevel.height, width: qualityLevel.width, index: i, label: qualityLevel.width + "x" + qualityLevel.height + ", " + sizeHumanizer(qualityLevel.bitrate, true, "bps") }); } spec.currentQuality = hls.firstLevel; if (hls.audioTracks && hls.audioTracks.length > 0) { for (let i = 0; i < hls.audioTracks.length; i++) { let audioTrack = hls.audioTracks[i]; spec.audioTracks.push({ index: audioTrack.id, label: audioTrack.name }); if (audioTrack.default === true) { spec.currentAudioTrack = audioTrack.id; } } } if (hls.subtitleTracks && hls.subtitleTracks.length > 0) { for (let i = 0; i < hls.subtitleTracks.length; i++) { let subtitle = hls.subtitleTracks[i]; spec.subtitleTracks.push({ index: subtitle.id, label: !!subtitle.name ? subtitle.name : !!subtitle.lang ? subtitle.lang : subtitle.id }); } } }); hls.once(Hls.Events.LEVEL_LOADED, function (event, data) { firstLoaded = true; if (loadRetryer) { clearTimeout(loadRetryer); loadRetryer = null; } if (data.details.live) { spec.isLive = true; } else { if (lastPlayPosition && lastPlayPosition >= 0) { that.seek(lastPlayPosition); } } }); hls.on(Hls.Events.LEVEL_SWITCHED, function (event, data) { spec.currentQuality = data.level; that.trigger(CONTENT_LEVEL_CHANGED, { isAuto: hls.autoLevelEnabled, currentQuality: spec.currentQuality, type: "render" }); }); hls.on(Hls.Events.AUDIO_TRACK_SWITCHED, function (event, data) { spec.currentAudioTrack = data.id; that.trigger(AUDIO_TRACK_CHANGED, { currentAudioTrack: spec.currentAudioTrack }); }); hls.on(Hls.Events.SUBTITLE_TRACK_SWITCH, function (event, data) { spec.currentSubtitleTrack = data.id; that.trigger(SUBTITLE_TRACK_CHANGED, { currentSubtitleTrack: spec.currentSubtitleTrack }); }); hls.on(Hls.Events.LEVEL_UPDATED, function (event, data) { if (data && data.details) { spec.dvrWindow = data.details.totalduration; } }); hls.on(Hls.Events.ERROR, function (event, data) { if (data && data.networkDetails && data.networkDetails.status === 202) { if (loadRetryer) { clearTimeout(loadRetryer); loadRetryer = null; } that.setState(STATE_LOADING); loadRetryer = setTimeout(function () { if (hls) { that.stop(); hls.stopLoad(); hls.loadSource(source.file); } }, 1000); return; } if (!data.fatal) { // do nothing when non fatal error. hlsjs will recover it automatically. return; } let errorType = PLAYER_UNKNWON_NETWORK_ERROR; if (data && data.networkDetails && data.networkDetails.status === 400) { errorType = PLAYER_BAD_REQUEST_ERROR; } else if (data && data.networkDetails && data.networkDetails.status === 403) { errorType = PLAYER_AUTH_FAILED_ERROR; } else if (data && data.networkDetails && data.networkDetails.status === 406) { errorType = PLAYER_NOT_ACCEPTABLE_ERROR; } let tempError = ERRORS.codes[errorType]; tempError.error = data; errorTrigger(tempError, that); }); that.on(PLAYER_STATE, function (data) { if (!firstLoaded && data.prevstate === STATE_LOADING && data.newstate === STATE_IDLE) { if (loadRetryer) { clearTimeout(loadRetryer); loadRetryer = null; } if (hls) { hls.stopLoad(); } } }); }); that.setCurrentQuality = (qualityIndex) => { hls.currentLevel = qualityIndex; spec.currentQuality = qualityIndex; return spec.currentQuality; }; that.isAutoQuality = () => { return hls.autoLevelEnabled; }; that.setAutoQuality = (isAuto) => { if (isAuto) { hls.currentLevel = -1; } }; that.setCurrentAudioTrack = (audioTrackIndex) => { hls.audioTrack = audioTrackIndex; spec.currentAudioTrack = audioTrackIndex; return spec.currentAudioTrack; }; that.setCurrentSubtitleTrack = (subtitleTrackIndex) => { hls.subtitleTrack = subtitleTrackIndex; spec.currentSubtitleTrack = subtitleTrackIndex; return spec.currentSubtitleTrack; }; that.getDuration = () => { return element.duration; } superStop_func = that.super('stop'); that.stop = () => { if (loadRetryer) { clearTimeout(loadRetryer); loadRetryer = null; } if (hls) { hls.stopLoad(); } superStop_func(); }; superDestroy_func = that.super('destroy'); that.destroy = () => { if (loadRetryer) { clearTimeout(loadRetryer); loadRetryer = null; } if (hls) { hls.destroy(); that.trigger(HLS_DESTROYED); } hls = null; OvenPlayerConsole.log("HLS : PROVIDER DESTROYED."); superDestroy_func(); }; OvenPlayerConsole.log("HLS PROVIDER LOADED."); } catch (error) { let tempError = ERRORS.codes[INIT_HLSJS_FAIL]; tempError.error = error; throw tempError; } return that; }; export default HlsProvider;