xgplayer-flv.js
Advanced tools
Comparing version 1.0.4 to 1.0.5
{ | ||
"name": "xgplayer-flv.js", | ||
"version": "1.0.4", | ||
"version": "1.0.5", | ||
"description": "web video player", | ||
@@ -5,0 +5,0 @@ "main": "./dist/index.js", |
@@ -19,700 +19,724 @@ /* | ||
import EventEmitter from 'events'; | ||
import Log from '../utils/logger.js'; | ||
import Browser from '../utils/browser.js'; | ||
import PlayerEvents from './player-events.js'; | ||
import Transmuxer from '../core/transmuxer.js'; | ||
import TransmuxingEvents from '../core/transmuxing-events.js'; | ||
import MSEController from '../core/mse-controller.js'; | ||
import MSEEvents from '../core/mse-events.js'; | ||
import {ErrorTypes, ErrorDetails} from './player-errors.js'; | ||
import {createDefaultConfig} from '../config.js'; | ||
import {InvalidArgumentException, IllegalStateException} from '../utils/exception.js'; | ||
import EventEmitter from 'events' | ||
import Log from '../utils/logger.js' | ||
import Browser from '../utils/browser.js' | ||
import PlayerEvents from './player-events.js' | ||
import Transmuxer from '../core/transmuxer.js' | ||
import TransmuxingEvents from '../core/transmuxing-events.js' | ||
import MSEController from '../core/mse-controller.js' | ||
import MSEEvents from '../core/mse-events.js' | ||
import {ErrorTypes, ErrorDetails} from './player-errors.js' | ||
import {createDefaultConfig} from '../config.js' | ||
import {InvalidArgumentException, IllegalStateException} from '../utils/exception.js' | ||
/* eslint-disable */ | ||
class FlvPlayer { | ||
constructor (mediaDataSource, config) { | ||
this.TAG = 'FlvPlayer' | ||
this._type = 'FlvPlayer' | ||
this._emitter = new EventEmitter() | ||
constructor(mediaDataSource, config) { | ||
this.TAG = 'FlvPlayer'; | ||
this._type = 'FlvPlayer'; | ||
this._emitter = new EventEmitter(); | ||
this._config = createDefaultConfig() | ||
if (typeof config === 'object') { | ||
Object.assign(this._config, config) | ||
} | ||
this._config = createDefaultConfig(); | ||
if (typeof config === 'object') { | ||
Object.assign(this._config, config); | ||
} | ||
if (mediaDataSource.type.toLowerCase() !== 'flv') { | ||
throw new InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!') | ||
} | ||
if (mediaDataSource.type.toLowerCase() !== 'flv') { | ||
throw new InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!'); | ||
} | ||
if (mediaDataSource.isLive === true) { | ||
this._config.isLive = true | ||
} | ||
if (mediaDataSource.isLive === true) { | ||
this._config.isLive = true; | ||
} | ||
this.e = { | ||
onvLoadedMetadata: this._onvLoadedMetadata.bind(this), | ||
onvSeeking: this._onvSeeking.bind(this), | ||
onvCanPlay: this._onvCanPlay.bind(this), | ||
onvStalled: this._onvStalled.bind(this), | ||
onvProgress: this._onvProgress.bind(this) | ||
} | ||
this.e = { | ||
onvLoadedMetadata: this._onvLoadedMetadata.bind(this), | ||
onvSeeking: this._onvSeeking.bind(this), | ||
onvCanPlay: this._onvCanPlay.bind(this), | ||
onvStalled: this._onvStalled.bind(this), | ||
onvProgress: this._onvProgress.bind(this) | ||
}; | ||
if (window.performance && window.performance.now) { | ||
this._now = window.performance.now.bind(window.performance) | ||
} else { | ||
this._now = Date.now | ||
} | ||
if (self.performance && self.performance.now) { | ||
this._now = self.performance.now.bind(self.performance); | ||
} else { | ||
this._now = Date.now; | ||
} | ||
this._pendingSeekTime = null // in seconds | ||
this._requestSetTime = false | ||
this._seekpointRecord = null | ||
this._progressChecker = null | ||
this._pendingSeekTime = null; // in seconds | ||
this._requestSetTime = false; | ||
this._seekpointRecord = null; | ||
this._progressChecker = null; | ||
this._mediaDataSource = mediaDataSource | ||
this._mediaElement = null | ||
this._msectl = null | ||
this._transmuxer = null | ||
this._mediaDataSource = mediaDataSource; | ||
this._mediaElement = null; | ||
this._msectl = null; | ||
this._transmuxer = null; | ||
this._mseSourceOpened = false | ||
this._hasPendingLoad = false | ||
this._receivedCanPlay = false | ||
this._mseSourceOpened = false; | ||
this._hasPendingLoad = false; | ||
this._receivedCanPlay = false; | ||
this._mediaInfo = null | ||
this._statisticsInfo = null | ||
this._mediaInfo = null; | ||
this._statisticsInfo = null; | ||
let chromeNeedIDRFix = (Browser.chrome && | ||
(Browser.version.major < 50 || | ||
(Browser.version.major === 50 && Browser.version.build < 2661))) | ||
this._alwaysSeekKeyframe = !!((chromeNeedIDRFix || Browser.msedge || Browser.msie)) | ||
let chromeNeedIDRFix = (Browser.chrome && | ||
(Browser.version.major < 50 || | ||
(Browser.version.major === 50 && Browser.version.build < 2661))); | ||
this._alwaysSeekKeyframe = (chromeNeedIDRFix || Browser.msedge || Browser.msie) ? true : false; | ||
if (this._alwaysSeekKeyframe) { | ||
this._config.accurateSeek = false | ||
} | ||
this._tempPendingSegments = { | ||
audio: [], | ||
video: [] | ||
} | ||
this._definitionRetryTimes = 0 | ||
} | ||
if (this._alwaysSeekKeyframe) { | ||
this._config.accurateSeek = false; | ||
} | ||
this._tempPendingSegments = { | ||
audio: [], | ||
video: [], | ||
}; | ||
this._definitionRetryTimes = 0; | ||
destroy () { | ||
if (this._progressChecker != null) { | ||
window.clearInterval(this._progressChecker) | ||
this._progressChecker = null | ||
} | ||
if (this._transmuxer) { | ||
this.unload() | ||
} | ||
if (this._mediaElement) { | ||
this.detachMediaElement() | ||
} | ||
this.e = null | ||
this._mediaDataSource = null | ||
destroy() { | ||
if (this._progressChecker != null) { | ||
window.clearInterval(this._progressChecker); | ||
this._progressChecker = null; | ||
} | ||
if (this._transmuxer) { | ||
this.unload(); | ||
} | ||
if (this._mediaElement) { | ||
this.detachMediaElement(); | ||
} | ||
this.e = null; | ||
this._mediaDataSource = null; | ||
this._emitter.removeAllListeners() | ||
this._emitter = null | ||
} | ||
this._emitter.removeAllListeners(); | ||
this._emitter = null; | ||
on (event, listener) { | ||
if (event === PlayerEvents.MEDIA_INFO) { | ||
if (this._mediaInfo != null) { | ||
Promise.resolve().then(() => { | ||
this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo) | ||
}) | ||
} | ||
} else if (event === PlayerEvents.STATISTICS_INFO) { | ||
if (this._statisticsInfo != null) { | ||
Promise.resolve().then(() => { | ||
this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo) | ||
}) | ||
} | ||
} | ||
this._emitter.addListener(event, listener) | ||
} | ||
on(event, listener) { | ||
if (event === PlayerEvents.MEDIA_INFO) { | ||
if (this._mediaInfo != null) { | ||
Promise.resolve().then(() => { | ||
this._emitter.emit(PlayerEvents.MEDIA_INFO, this.mediaInfo); | ||
}); | ||
} | ||
} else if (event === PlayerEvents.STATISTICS_INFO) { | ||
if (this._statisticsInfo != null) { | ||
Promise.resolve().then(() => { | ||
this._emitter.emit(PlayerEvents.STATISTICS_INFO, this.statisticsInfo); | ||
}); | ||
} | ||
} | ||
this._emitter.addListener(event, listener); | ||
off (event, listener) { | ||
this._emitter.removeListener(event, listener) | ||
} | ||
attachMediaElement (mediaElement) { | ||
this._mediaElement = mediaElement | ||
mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata) | ||
mediaElement.addEventListener('seeking', this.e.onvSeeking) | ||
mediaElement.addEventListener('canplay', this.e.onvCanPlay) | ||
mediaElement.addEventListener('stalled', this.e.onvStalled) | ||
mediaElement.addEventListener('progress', this.e.onvProgress) | ||
this._msectl = new MSEController(this._config) | ||
this._msectl.on(MSEEvents.UPDATE_END, this._onmseUpdateEnd.bind(this)) | ||
this._msectl.on(MSEEvents.BUFFER_FULL, this._onmseBufferFull.bind(this)) | ||
this._msectl.on(MSEEvents.SOURCE_OPEN, () => { | ||
this._mseSourceOpened = true | ||
if (this._hasPendingLoad) { | ||
this._hasPendingLoad = false | ||
this.load() | ||
} | ||
}) | ||
this._msectl.on(MSEEvents.ERROR, (info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, | ||
ErrorTypes.MEDIA_ERROR, | ||
ErrorDetails.MEDIA_MSE_ERROR, | ||
info | ||
) | ||
}) | ||
this._msectl.attachMediaElement(mediaElement) | ||
if (this._pendingSeekTime != null) { | ||
try { | ||
mediaElement.currentTime = this._pendingSeekTime | ||
this._pendingSeekTime = null | ||
} catch (e) { | ||
// IE11 may throw InvalidStateError if readyState === 0 | ||
// We can defer set currentTime operation after loadedmetadata | ||
} | ||
} | ||
} | ||
off(event, listener) { | ||
this._emitter.removeListener(event, listener); | ||
detachMediaElement () { | ||
if (this._mediaElement) { | ||
this._msectl.detachMediaElement() | ||
this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata) | ||
this._mediaElement.removeEventListener('seeking', this.e.onvSeeking) | ||
this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay) | ||
this._mediaElement.removeEventListener('stalled', this.e.onvStalled) | ||
this._mediaElement.removeEventListener('progress', this.e.onvProgress) | ||
this._mediaElement = null | ||
} | ||
if (this._msectl) { | ||
this._msectl.destroy() | ||
this._msectl = null | ||
} | ||
} | ||
attachMediaElement(mediaElement) { | ||
this._mediaElement = mediaElement; | ||
mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata); | ||
mediaElement.addEventListener('seeking', this.e.onvSeeking); | ||
mediaElement.addEventListener('canplay', this.e.onvCanPlay); | ||
mediaElement.addEventListener('stalled', this.e.onvStalled); | ||
mediaElement.addEventListener('progress', this.e.onvProgress); | ||
onDefinitionChange (url, expectTime = 3) { | ||
// setTimeout(() => { | ||
// if (!this.isDefinitionDataReady && this._definitionRetryTimes < 3) { | ||
// this._definitionRetryTimes += 1 | ||
// this.onDefinitionChange(url, expectTime) | ||
// } else if (this.isDefinitionDataReady) { | ||
// if (this._transmuxer !== this._tempTransmuxer) { | ||
// this._transmuxer.destroy() | ||
// this._transmuxer = this._tempTransmuxer | ||
// let currentTime = this._mediaElement.currentTime | ||
// this._tempTransmuxer.seek(currentTime * 1000) | ||
// } | ||
// Object.keys(this._tempPendingSegments).forEach(key => { | ||
// this._msectl._pendingSegments[key] = this._tempPendingSegments[key] | ||
// }) | ||
// this._tempPendingSegments = { | ||
// audio: [], | ||
// video: [] | ||
// } | ||
// | ||
// this._definitionRetryTimes = 0 | ||
// } else if (this._definitionRetryTimes >= 3) { | ||
// this._definitionRetryTimes = 0 | ||
// if (this._tempTransmuxer) { | ||
// this._tempTransmuxer.destroy() | ||
// this._tempTransmuxer = null | ||
// this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, '', '清晰度切换失败!') | ||
// } | ||
// this._definitionRetryTimes = 0 | ||
// } | ||
// this._tempPendingSegments = { | ||
// audio: [], | ||
// video: [] | ||
// } | ||
// }, expectTime * 1000) | ||
this._msectl = new MSEController(this._config); | ||
this._mediaDataSource.segments[0].url = url | ||
this._tempMds = Object.assign({}, this._mediaDataSource, {url}) | ||
this._msectl.on(MSEEvents.UPDATE_END, this._onmseUpdateEnd.bind(this)); | ||
this._msectl.on(MSEEvents.BUFFER_FULL, this._onmseBufferFull.bind(this)); | ||
this._msectl.on(MSEEvents.SOURCE_OPEN, () => { | ||
this._mseSourceOpened = true; | ||
if (this._hasPendingLoad) { | ||
this._hasPendingLoad = false; | ||
this.load(); | ||
} | ||
}); | ||
this._msectl.on(MSEEvents.ERROR, (info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, | ||
ErrorTypes.MEDIA_ERROR, | ||
ErrorDetails.MEDIA_MSE_ERROR, | ||
info | ||
); | ||
}); | ||
this._tempTransmuxer = new Transmuxer(this._tempMds, this._config) | ||
this._msectl.attachMediaElement(mediaElement); | ||
this._tempTransmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => { | ||
if (!this._config.isLive) { | ||
this._tempPendingSegments[type] = [is] | ||
// this._msectl.appendInitSegment(is) | ||
if (this._transmuxer !== this._tempTransmuxer && this._tempTransmuxer) { | ||
const currentTime = this._mediaElement.currentTime | ||
this._tempTransmuxer.seek(currentTime * 1000) | ||
} | ||
// if (this._transmuxer !== this._tempTransmuxer) { | ||
// this._transmuxer.destroy() | ||
// } | ||
// this._transmuxer = this._tempTransmuxer | ||
} else { | ||
this._msectl.doClearSourceBuffer() | ||
this._msectl.appendInitSegment(is) | ||
if (this._transmuxer !== this._tempTransmuxer) { | ||
this._transmuxer.destroy() | ||
} | ||
this._transmuxer = this._tempTransmuxer | ||
} | ||
}) | ||
if (this._pendingSeekTime != null) { | ||
try { | ||
mediaElement.currentTime = this._pendingSeekTime; | ||
this._pendingSeekTime = null; | ||
} catch (e) { | ||
// IE11 may throw InvalidStateError if readyState === 0 | ||
// We can defer set currentTime operation after loadedmetadata | ||
this._tempTransmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => { | ||
if (!this._config.isLive) { | ||
if (!this._tempTransmuxer) { | ||
this._msectl.appendMediaSegment(ms) | ||
} else { | ||
this._tempPendingSegments[type] && this._tempPendingSegments[type].push(ms) | ||
if (this.isDefinitionDataReady) { | ||
Object.keys(this._tempPendingSegments).forEach(key => { | ||
this._msectl._pendingSegments[key] = this._tempPendingSegments[key] | ||
}) | ||
this._tempPendingSegments = { | ||
audio: [], | ||
video: [] | ||
} | ||
this._transmuxer.destroy() | ||
this._transmuxer = this._tempTransmuxer | ||
delete this._tempTransmuxer | ||
} | ||
} | ||
} | ||
} else { | ||
this._msectl.appendMediaSegment(ms) | ||
} | ||
detachMediaElement() { | ||
if (this._mediaElement) { | ||
this._msectl.detachMediaElement(); | ||
this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata); | ||
this._mediaElement.removeEventListener('seeking', this.e.onvSeeking); | ||
this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay); | ||
this._mediaElement.removeEventListener('stalled', this.e.onvStalled); | ||
this._mediaElement.removeEventListener('progress', this.e.onvProgress); | ||
this._mediaElement = null; | ||
// lazyLoad check | ||
if (this._config.lazyLoad && !this._config.isLive) { | ||
let currentTime = this._mediaElement.currentTime | ||
if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) { | ||
if (this._progressChecker == null) { | ||
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task') | ||
this._suspendTransmuxer() | ||
} | ||
} | ||
if (this._msectl) { | ||
this._msectl.destroy(); | ||
this._msectl = null; | ||
} | ||
} | ||
} | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => { | ||
this._msectl.endOfStream() | ||
this._emitter.emit(PlayerEvents.LOADING_COMPLETE) | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => { | ||
this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF) | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info) | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, {code: -1, msg: info}) | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => { | ||
this._mediaInfo = mediaInfo | ||
this._tempTransmuxer.seek((this._mediaElement.currentTime + expectTime) * 1000) | ||
this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo)) | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => { | ||
this._statisticsInfo = this._fillStatisticsInfo(statInfo) | ||
this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo)) | ||
}) | ||
this._tempTransmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => { | ||
if (this._transmuxer === this._tempTransmuxer && this._mediaElement && !this._config.accurateSeek) { | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = milliseconds / 1000 | ||
} | ||
}) | ||
onDefinitionChange(url, expectTime = 3) { | ||
setTimeout(() => { | ||
if (!this.isDefinitionDataReady && this._definitionRetryTimes < 3) { | ||
this._definitionRetryTimes += 1; | ||
this.onDefinitionChange(url, expectTime); | ||
} else if (this.isDefinitionDataReady) { | ||
this._transmuxer.destroy(); | ||
this._tempTransmuxer.open() | ||
} | ||
Object.keys(this._tempPendingSegments).forEach(key => { | ||
this._msectl._pendingSegments[key] = this._tempPendingSegments[key]; | ||
}); | ||
this._tempPendingSegments = { | ||
audio: [], | ||
video: [], | ||
}; | ||
this._transmuxer = this._tempTransmuxer; | ||
this._definitionRetryTimes = 0; | ||
} else if (this._definitionRetryTimes >= 3) { | ||
this._definitionRetryTimes = 0; | ||
if (this._tempTransmuxer) { | ||
this._tempTransmuxer.destroy(); | ||
this._tempTransmuxer = null; | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, '', '清晰度切换失败!'); | ||
} | ||
this._definitionRetryTimes = 0; | ||
} | ||
this._tempPendingSegments = { | ||
audio: [], | ||
video: [], | ||
}; | ||
}, expectTime * 1000); | ||
load () { | ||
if (!this._mediaElement) { | ||
throw new IllegalStateException('HTMLMediaElement must be attached before load()!') | ||
} | ||
if (this._transmuxer) { | ||
throw new IllegalStateException('FlvPlayer.load() has been called, please call unload() first!') | ||
} | ||
if (this._hasPendingLoad) { | ||
return | ||
} | ||
this._mediaDataSource.segments[0].url = url; | ||
this._tempMds = Object.assign({}, this._mediaDataSource, { url }); | ||
if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) { | ||
this._hasPendingLoad = true | ||
return | ||
} | ||
this._tempTransmuxer = new Transmuxer(this._tempMds, this._config); | ||
if (this._mediaElement.readyState > 0) { | ||
this._requestSetTime = true | ||
// IE11 may throw InvalidStateError if readyState === 0 | ||
this._mediaElement.currentTime = 0 | ||
} | ||
this._transmuxer = new Transmuxer(this._mediaDataSource, this._config) | ||
this._tempTransmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => { | ||
if (!this._config.isLive) { | ||
this._tempPendingSegments[type] = [is]; | ||
} else { | ||
this._msectl.doClearSourceBuffer(); | ||
this._msectl.appendInitSegment(is); | ||
if (this._transmuxer !== this._tempTransmuxer) { | ||
this._transmuxer.destroy(); | ||
} | ||
this._transmuxer = this._tempTransmuxer; | ||
} | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => { | ||
this._msectl.appendInitSegment(is) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => { | ||
this._msectl.appendMediaSegment(ms) | ||
this._tempTransmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => { | ||
if (!this._config.isLive) { | ||
if (this._transmuxer === this._tempTransmuxer) { | ||
this._msectl.appendMediaSegment(ms); | ||
} else { | ||
this._tempPendingSegments[type] && this._tempPendingSegments[type].push(ms); | ||
} | ||
} else { | ||
this._msectl.appendMediaSegment(ms); | ||
} | ||
// lazyLoad check | ||
if (this._config.lazyLoad && !this._config.isLive) { | ||
let currentTime = this._mediaElement.currentTime | ||
if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) { | ||
if (this._progressChecker == null) { | ||
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task') | ||
this._suspendTransmuxer() | ||
} | ||
} | ||
} | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => { | ||
this._msectl.endOfStream() | ||
this._emitter.emit(PlayerEvents.LOADING_COMPLETE) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => { | ||
this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, {code: -1, msg: info}) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => { | ||
this._mediaInfo = mediaInfo | ||
this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo)) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => { | ||
this._statisticsInfo = this._fillStatisticsInfo(statInfo) | ||
this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo)) | ||
}) | ||
this._transmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => { | ||
if (this._mediaElement && !this._config.accurateSeek) { | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = milliseconds / 1000 | ||
} | ||
}) | ||
// lazyLoad check | ||
if (this._config.lazyLoad && !this._config.isLive) { | ||
let currentTime = this._mediaElement.currentTime; | ||
if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) { | ||
if (this._progressChecker == null) { | ||
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); | ||
this._suspendTransmuxer(); | ||
} | ||
} | ||
} | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => { | ||
this._msectl.endOfStream(); | ||
this._emitter.emit(PlayerEvents.LOADING_COMPLETE); | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => { | ||
this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF); | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info); | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, {code: -1, msg: info}); | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => { | ||
this._mediaInfo = mediaInfo; | ||
this._tempTransmuxer.seek((this._mediaElement.currentTime + expectTime) * 1000); | ||
this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo)); | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => { | ||
this._statisticsInfo = this._fillStatisticsInfo(statInfo); | ||
this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo)); | ||
}); | ||
this._tempTransmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => { | ||
if (this._transmuxer === this._tempTransmuxer && this._mediaElement && !this._config.accurateSeek) { | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = milliseconds / 1000; | ||
} | ||
}); | ||
this._transmuxer.open() | ||
} | ||
this._tempTransmuxer.open(); | ||
unload () { | ||
if (this._mediaElement) { | ||
this._mediaElement.pause() | ||
} | ||
load() { | ||
if (!this._mediaElement) { | ||
throw new IllegalStateException('HTMLMediaElement must be attached before load()!'); | ||
} | ||
if (this._transmuxer) { | ||
throw new IllegalStateException('FlvPlayer.load() has been called, please call unload() first!'); | ||
} | ||
if (this._hasPendingLoad) { | ||
return; | ||
} | ||
if (this._msectl) { | ||
this._msectl.seek(0) | ||
} | ||
if (this._transmuxer) { | ||
this._transmuxer.close() | ||
this._transmuxer.destroy() | ||
this._transmuxer = null | ||
} | ||
} | ||
if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) { | ||
this._hasPendingLoad = true; | ||
return; | ||
} | ||
play () { | ||
return this._mediaElement.play() | ||
} | ||
if (this._mediaElement.readyState > 0) { | ||
this._requestSetTime = true; | ||
// IE11 may throw InvalidStateError if readyState === 0 | ||
this._mediaElement.currentTime = 0; | ||
} | ||
pause () { | ||
this._mediaElement.pause() | ||
} | ||
this._transmuxer = new Transmuxer(this._mediaDataSource, this._config); | ||
get type () { | ||
return this._type | ||
} | ||
this._transmuxer.on(TransmuxingEvents.INIT_SEGMENT, (type, is) => { | ||
this._msectl.appendInitSegment(is); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.MEDIA_SEGMENT, (type, ms) => { | ||
this._msectl.appendMediaSegment(ms); | ||
get buffered () { | ||
return this._mediaElement.buffered | ||
} | ||
// lazyLoad check | ||
if (this._config.lazyLoad && !this._config.isLive) { | ||
let currentTime = this._mediaElement.currentTime; | ||
if (ms.info.endDts >= (currentTime + this._config.lazyLoadMaxDuration) * 1000) { | ||
if (this._progressChecker == null) { | ||
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); | ||
this._suspendTransmuxer(); | ||
} | ||
} | ||
} | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.LOADING_COMPLETE, () => { | ||
this._msectl.endOfStream(); | ||
this._emitter.emit(PlayerEvents.LOADING_COMPLETE); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.RECOVERED_EARLY_EOF, () => { | ||
this._emitter.emit(PlayerEvents.RECOVERED_EARLY_EOF); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.IO_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.NETWORK_ERROR, detail, info); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.DEMUX_ERROR, (detail, info) => { | ||
this._emitter.emit(PlayerEvents.ERROR, ErrorTypes.MEDIA_ERROR, detail, {code: -1, msg: info}); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.MEDIA_INFO, (mediaInfo) => { | ||
this._mediaInfo = mediaInfo; | ||
this._emitter.emit(PlayerEvents.MEDIA_INFO, Object.assign({}, mediaInfo)); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.STATISTICS_INFO, (statInfo) => { | ||
this._statisticsInfo = this._fillStatisticsInfo(statInfo); | ||
this._emitter.emit(PlayerEvents.STATISTICS_INFO, Object.assign({}, this._statisticsInfo)); | ||
}); | ||
this._transmuxer.on(TransmuxingEvents.RECOMMEND_SEEKPOINT, (milliseconds) => { | ||
if (this._mediaElement && !this._config.accurateSeek) { | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = milliseconds / 1000; | ||
} | ||
}); | ||
get duration () { | ||
return this._mediaElement.duration | ||
} | ||
this._transmuxer.open(); | ||
} | ||
get volume () { | ||
return this._mediaElement.volume | ||
} | ||
unload() { | ||
if (this._mediaElement) { | ||
this._mediaElement.pause(); | ||
} | ||
if (this._msectl) { | ||
this._msectl.seek(0); | ||
} | ||
if (this._transmuxer) { | ||
this._transmuxer.close(); | ||
this._transmuxer.destroy(); | ||
this._transmuxer = null; | ||
} | ||
} | ||
set volume (value) { | ||
this._mediaElement.volume = value | ||
} | ||
play() { | ||
return this._mediaElement.play(); | ||
} | ||
get muted () { | ||
return this._mediaElement.muted | ||
} | ||
pause() { | ||
this._mediaElement.pause(); | ||
} | ||
set muted (muted) { | ||
this._mediaElement.muted = muted | ||
} | ||
get type() { | ||
return this._type; | ||
get currentTime () { | ||
if (this._mediaElement) { | ||
return this._mediaElement.currentTime | ||
} | ||
return 0 | ||
} | ||
get buffered() { | ||
return this._mediaElement.buffered; | ||
set currentTime (seconds) { | ||
if (this._mediaElement) { | ||
this._internalSeek(seconds) | ||
} else { | ||
this._pendingSeekTime = seconds | ||
} | ||
} | ||
get duration() { | ||
return this._mediaElement.duration; | ||
} | ||
get mediaInfo () { | ||
return Object.assign({}, this._mediaInfo) | ||
} | ||
get volume() { | ||
return this._mediaElement.volume; | ||
get statisticsInfo () { | ||
if (this._statisticsInfo == null) { | ||
this._statisticsInfo = {} | ||
} | ||
this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo) | ||
return Object.assign({}, this._statisticsInfo) | ||
} | ||
set volume(value) { | ||
this._mediaElement.volume = value; | ||
} | ||
_fillStatisticsInfo (statInfo) { | ||
statInfo.playerType = this._type | ||
get muted() { | ||
return this._mediaElement.muted; | ||
if (!(this._mediaElement instanceof window.HTMLVideoElement)) { | ||
return statInfo | ||
} | ||
set muted(muted) { | ||
this._mediaElement.muted = muted; | ||
} | ||
let hasQualityInfo = true | ||
let decoded = 0 | ||
let dropped = 0 | ||
get currentTime() { | ||
if (this._mediaElement) { | ||
return this._mediaElement.currentTime; | ||
} | ||
return 0; | ||
if (this._mediaElement.getVideoPlaybackQuality) { | ||
let quality = this._mediaElement.getVideoPlaybackQuality() | ||
decoded = quality.totalVideoFrames | ||
dropped = quality.droppedVideoFrames | ||
} else if (this._mediaElement.webkitDecodedFrameCount != undefined) { | ||
decoded = this._mediaElement.webkitDecodedFrameCount | ||
dropped = this._mediaElement.webkitDroppedFrameCount | ||
} else { | ||
hasQualityInfo = false | ||
} | ||
set currentTime(seconds) { | ||
if (this._mediaElement) { | ||
this._internalSeek(seconds); | ||
} else { | ||
this._pendingSeekTime = seconds; | ||
} | ||
if (hasQualityInfo) { | ||
statInfo.decodedFrames = decoded | ||
statInfo.droppedFrames = dropped | ||
} | ||
get mediaInfo() { | ||
return Object.assign({}, this._mediaInfo); | ||
} | ||
return statInfo | ||
} | ||
get statisticsInfo() { | ||
if (this._statisticsInfo == null) { | ||
this._statisticsInfo = {}; | ||
} | ||
this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo); | ||
return Object.assign({}, this._statisticsInfo); | ||
_onmseUpdateEnd () { | ||
if (!this._config.lazyLoad || this._config.isLive) { | ||
return | ||
} | ||
_fillStatisticsInfo(statInfo) { | ||
statInfo.playerType = this._type; | ||
let buffered = this._mediaElement.buffered | ||
let currentTime = this._mediaElement.currentTime | ||
let currentRangeStart = 0 | ||
let currentRangeEnd = 0 | ||
if (!(this._mediaElement instanceof HTMLVideoElement)) { | ||
return statInfo; | ||
} | ||
let hasQualityInfo = true; | ||
let decoded = 0; | ||
let dropped = 0; | ||
if (this._mediaElement.getVideoPlaybackQuality) { | ||
let quality = this._mediaElement.getVideoPlaybackQuality(); | ||
decoded = quality.totalVideoFrames; | ||
dropped = quality.droppedVideoFrames; | ||
} else if (this._mediaElement.webkitDecodedFrameCount != undefined) { | ||
decoded = this._mediaElement.webkitDecodedFrameCount; | ||
dropped = this._mediaElement.webkitDroppedFrameCount; | ||
} else { | ||
hasQualityInfo = false; | ||
} | ||
if (hasQualityInfo) { | ||
statInfo.decodedFrames = decoded; | ||
statInfo.droppedFrames = dropped; | ||
} | ||
return statInfo; | ||
for (let i = 0; i < buffered.length; i++) { | ||
let start = buffered.start(i) | ||
let end = buffered.end(i) | ||
if (start <= currentTime && currentTime < end) { | ||
currentRangeStart = start | ||
currentRangeEnd = end | ||
break | ||
} | ||
} | ||
_onmseUpdateEnd() { | ||
if (!this._config.lazyLoad || this._config.isLive) { | ||
return; | ||
} | ||
let buffered = this._mediaElement.buffered; | ||
let currentTime = this._mediaElement.currentTime; | ||
let currentRangeStart = 0; | ||
let currentRangeEnd = 0; | ||
for (let i = 0; i < buffered.length; i++) { | ||
let start = buffered.start(i); | ||
let end = buffered.end(i); | ||
if (start <= currentTime && currentTime < end) { | ||
currentRangeStart = start; | ||
currentRangeEnd = end; | ||
break; | ||
} | ||
} | ||
if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) { | ||
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); | ||
this._suspendTransmuxer(); | ||
} | ||
if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) { | ||
Log.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task') | ||
this._suspendTransmuxer() | ||
} | ||
} | ||
_onmseBufferFull() { | ||
Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task'); | ||
if (this._progressChecker == null) { | ||
this._suspendTransmuxer(); | ||
} | ||
_onmseBufferFull () { | ||
Log.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task') | ||
if (this._progressChecker == null) { | ||
this._suspendTransmuxer() | ||
} | ||
} | ||
_suspendTransmuxer() { | ||
if (this._transmuxer) { | ||
this._transmuxer.pause(); | ||
_suspendTransmuxer () { | ||
if (this._transmuxer) { | ||
this._transmuxer.pause() | ||
if (this._progressChecker == null) { | ||
this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000); | ||
} | ||
} | ||
if (this._progressChecker == null) { | ||
this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000) | ||
} | ||
} | ||
} | ||
_checkProgressAndResume() { | ||
let currentTime = this._mediaElement.currentTime; | ||
let buffered = this._mediaElement.buffered; | ||
_checkProgressAndResume () { | ||
let currentTime = this._mediaElement.currentTime | ||
let buffered = this._mediaElement.buffered | ||
let needResume = false; | ||
let needResume = false | ||
for (let i = 0; i < buffered.length; i++) { | ||
let from = buffered.start(i); | ||
let to = buffered.end(i); | ||
if (currentTime >= from && currentTime < to) { | ||
if (currentTime >= to - this._config.lazyLoadRecoverDuration) { | ||
needResume = true; | ||
} | ||
break; | ||
} | ||
for (let i = 0; i < buffered.length; i++) { | ||
let from = buffered.start(i) | ||
let to = buffered.end(i) | ||
if (currentTime >= from && currentTime < to) { | ||
if (currentTime >= to - this._config.lazyLoadRecoverDuration) { | ||
needResume = true | ||
} | ||
break | ||
} | ||
} | ||
if (needResume) { | ||
window.clearInterval(this._progressChecker); | ||
this._progressChecker = null; | ||
if (needResume) { | ||
Log.v(this.TAG, 'Continue loading from paused position'); | ||
this._transmuxer.resume(); | ||
} | ||
} | ||
if (needResume) { | ||
window.clearInterval(this._progressChecker) | ||
this._progressChecker = null | ||
if (needResume) { | ||
Log.v(this.TAG, 'Continue loading from paused position') | ||
this._transmuxer.resume() | ||
} | ||
} | ||
} | ||
_isTimepointBuffered(seconds) { | ||
let buffered = this._mediaElement.buffered; | ||
_isTimepointBuffered (seconds) { | ||
let buffered = this._mediaElement.buffered | ||
for (let i = 0; i < buffered.length; i++) { | ||
let from = buffered.start(i); | ||
let to = buffered.end(i); | ||
if (seconds >= from && seconds < to) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
for (let i = 0; i < buffered.length; i++) { | ||
let from = buffered.start(i) | ||
let to = buffered.end(i) | ||
if (seconds >= from && seconds < to) { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
_internalSeek(seconds) { | ||
let directSeek = this._isTimepointBuffered(seconds); | ||
_internalSeek (seconds) { | ||
let directSeek = this._isTimepointBuffered(seconds) | ||
let directSeekBegin = false; | ||
let directSeekBeginTime = 0; | ||
let directSeekBegin = false | ||
let directSeekBeginTime = 0 | ||
if (seconds < 1.0 && this._mediaElement.buffered.length > 0) { | ||
let videoBeginTime = this._mediaElement.buffered.start(0); | ||
if ((videoBeginTime < 1.0 && seconds < videoBeginTime) || Browser.safari) { | ||
directSeekBegin = true; | ||
// also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid | ||
directSeekBeginTime = Browser.safari ? 0.1 : videoBeginTime; | ||
} | ||
} | ||
if (seconds < 1.0 && this._mediaElement.buffered.length > 0) { | ||
let videoBeginTime = this._mediaElement.buffered.start(0) | ||
if ((videoBeginTime < 1.0 && seconds < videoBeginTime) || Browser.safari) { | ||
directSeekBegin = true | ||
// also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid | ||
directSeekBeginTime = Browser.safari ? 0.1 : videoBeginTime | ||
} | ||
} | ||
if (directSeekBegin) { // seek to video begin, set currentTime directly if beginPTS buffered | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = directSeekBeginTime; | ||
} else if (directSeek) { // buffered position | ||
if (!this._alwaysSeekKeyframe) { | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = seconds; | ||
} else { | ||
let idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000)); | ||
this._requestSetTime = true; | ||
if (idr != null) { | ||
this._mediaElement.currentTime = idr.dts / 1000; | ||
} else { | ||
this._mediaElement.currentTime = seconds; | ||
} | ||
} | ||
if (this._progressChecker != null) { | ||
this._checkProgressAndResume(); | ||
} | ||
if (directSeekBegin) { // seek to video begin, set currentTime directly if beginPTS buffered | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = directSeekBeginTime | ||
} else if (directSeek) { // buffered position | ||
if (!this._alwaysSeekKeyframe) { | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = seconds | ||
} else { | ||
let idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000)) | ||
this._requestSetTime = true | ||
if (idr != null) { | ||
this._mediaElement.currentTime = idr.dts / 1000 | ||
} else { | ||
if (this._progressChecker != null) { | ||
window.clearInterval(this._progressChecker); | ||
this._progressChecker = null; | ||
} | ||
this._msectl.seek(seconds); | ||
this._transmuxer.seek(Math.floor(seconds * 1000)); // in milliseconds | ||
// no need to set mediaElement.currentTime if non-accurateSeek, | ||
// just wait for the recommend_seekpoint callback | ||
if (this._config.accurateSeek) { | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = seconds; | ||
} | ||
this._mediaElement.currentTime = seconds | ||
} | ||
} | ||
if (this._progressChecker != null) { | ||
this._checkProgressAndResume() | ||
} | ||
} else { | ||
if (this._progressChecker != null) { | ||
window.clearInterval(this._progressChecker) | ||
this._progressChecker = null | ||
} | ||
this._msectl.seek(seconds) | ||
this._transmuxer.seek(Math.floor(seconds * 1000)) // in milliseconds | ||
// no need to set mediaElement.currentTime if non-accurateSeek, | ||
// just wait for the recommend_seekpoint callback | ||
if (this._config.accurateSeek) { | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = seconds | ||
} | ||
} | ||
} | ||
_checkAndApplyUnbufferedSeekpoint() { | ||
if (this._seekpointRecord) { | ||
if (this._seekpointRecord.recordTime <= this._now() - 100) { | ||
let target = this._mediaElement.currentTime; | ||
this._seekpointRecord = null; | ||
if (!this._isTimepointBuffered(target)) { | ||
if (this._progressChecker != null) { | ||
window.clearTimeout(this._progressChecker); | ||
this._progressChecker = null; | ||
} | ||
// .currentTime is consists with .buffered timestamp | ||
// Chrome/Edge use DTS, while FireFox/Safari use PTS | ||
this._msectl.seek(target); | ||
this._transmuxer.seek(Math.floor(target * 1000)); | ||
// set currentTime if accurateSeek, or wait for recommend_seekpoint callback | ||
if (this._config.accurateSeek) { | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = target; | ||
} | ||
} | ||
} else { | ||
window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50); | ||
} | ||
_checkAndApplyUnbufferedSeekpoint () { | ||
if (this._seekpointRecord) { | ||
if (this._seekpointRecord.recordTime <= this._now() - 100) { | ||
let target = this._mediaElement.currentTime | ||
this._seekpointRecord = null | ||
if (!this._isTimepointBuffered(target)) { | ||
if (this._progressChecker != null) { | ||
window.clearTimeout(this._progressChecker) | ||
this._progressChecker = null | ||
} | ||
// .currentTime is consists with .buffered timestamp | ||
// Chrome/Edge use DTS, while FireFox/Safari use PTS | ||
this._msectl.seek(target) | ||
this._transmuxer.seek(Math.floor(target * 1000)) | ||
// set currentTime if accurateSeek, or wait for recommend_seekpoint callback | ||
if (this._config.accurateSeek) { | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = target | ||
} | ||
} | ||
} else { | ||
window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50) | ||
} | ||
} | ||
} | ||
_checkAndResumeStuckPlayback(stalled) { | ||
let media = this._mediaElement; | ||
if (stalled || !this._receivedCanPlay || media.readyState < 2) { // HAVE_CURRENT_DATA | ||
let buffered = media.buffered; | ||
if (buffered.length > 0 && media.currentTime < buffered.start(0)) { | ||
Log.w(this.TAG, `Playback seems stuck at ${media.currentTime}, seek to ${buffered.start(0)}`); | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = buffered.start(0); | ||
this._mediaElement.removeEventListener('progress', this.e.onvProgress); | ||
} | ||
} else { | ||
// Playback didn't stuck, remove progress event listener | ||
this._mediaElement.removeEventListener('progress', this.e.onvProgress); | ||
} | ||
_checkAndResumeStuckPlayback (stalled) { | ||
let media = this._mediaElement | ||
if (stalled || !this._receivedCanPlay || media.readyState < 2) { // HAVE_CURRENT_DATA | ||
let buffered = media.buffered | ||
if (buffered.length > 0 && media.currentTime < buffered.start(0)) { | ||
Log.w(this.TAG, `Playback seems stuck at ${media.currentTime}, seek to ${buffered.start(0)}`) | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = buffered.start(0) | ||
this._mediaElement.removeEventListener('progress', this.e.onvProgress) | ||
} | ||
} else { | ||
// Playback didn't stuck, remove progress event listener | ||
this._mediaElement.removeEventListener('progress', this.e.onvProgress) | ||
} | ||
} | ||
_onvLoadedMetadata(e) { | ||
if (this._pendingSeekTime != null) { | ||
this._mediaElement.currentTime = this._pendingSeekTime; | ||
this._pendingSeekTime = null; | ||
} | ||
_onvLoadedMetadata (e) { | ||
if (this._pendingSeekTime != null) { | ||
this._mediaElement.currentTime = this._pendingSeekTime | ||
this._pendingSeekTime = null | ||
} | ||
} | ||
_onvSeeking(e) { // handle seeking request from browser's progress bar | ||
let target = this._mediaElement.currentTime; | ||
let buffered = this._mediaElement.buffered; | ||
_onvSeeking (e) { // handle seeking request from browser's progress bar | ||
let target = this._mediaElement.currentTime | ||
let buffered = this._mediaElement.buffered | ||
if (this._requestSetTime) { | ||
this._requestSetTime = false; | ||
return; | ||
} | ||
if (this._requestSetTime) { | ||
this._requestSetTime = false | ||
return | ||
} | ||
if (target < 1.0 && buffered.length > 0) { | ||
// seek to video begin, set currentTime directly if beginPTS buffered | ||
let videoBeginTime = buffered.start(0); | ||
if ((videoBeginTime < 1.0 && target < videoBeginTime) || Browser.safari) { | ||
this._requestSetTime = true; | ||
// also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid | ||
this._mediaElement.currentTime = Browser.safari ? 0.1 : videoBeginTime; | ||
return; | ||
} | ||
} | ||
if (target < 1.0 && buffered.length > 0) { | ||
// seek to video begin, set currentTime directly if beginPTS buffered | ||
let videoBeginTime = buffered.start(0) | ||
if ((videoBeginTime < 1.0 && target < videoBeginTime) || Browser.safari) { | ||
this._requestSetTime = true | ||
// also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid | ||
this._mediaElement.currentTime = Browser.safari ? 0.1 : videoBeginTime | ||
return | ||
} | ||
} | ||
if (this._isTimepointBuffered(target)) { | ||
if (this._alwaysSeekKeyframe) { | ||
let idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000)); | ||
if (idr != null) { | ||
this._requestSetTime = true; | ||
this._mediaElement.currentTime = idr.dts / 1000; | ||
} | ||
} | ||
if (this._progressChecker != null) { | ||
this._checkProgressAndResume(); | ||
} | ||
return; | ||
if (this._isTimepointBuffered(target)) { | ||
if (this._alwaysSeekKeyframe) { | ||
let idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000)) | ||
if (idr != null) { | ||
this._requestSetTime = true | ||
this._mediaElement.currentTime = idr.dts / 1000 | ||
} | ||
this._seekpointRecord = { | ||
seekPoint: target, | ||
recordTime: this._now() | ||
}; | ||
window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50); | ||
} | ||
if (this._progressChecker != null) { | ||
this._checkProgressAndResume() | ||
} | ||
return | ||
} | ||
_onvCanPlay(e) { | ||
this._receivedCanPlay = true; | ||
this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay); | ||
this._seekpointRecord = { | ||
seekPoint: target, | ||
recordTime: this._now() | ||
} | ||
window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50) | ||
} | ||
_onvStalled(e) { | ||
this._checkAndResumeStuckPlayback(true); | ||
} | ||
_onvCanPlay (e) { | ||
this._receivedCanPlay = true | ||
this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay) | ||
} | ||
_onvProgress(e) { | ||
this._checkAndResumeStuckPlayback(); | ||
} | ||
_onvStalled (e) { | ||
this._checkAndResumeStuckPlayback(true) | ||
} | ||
get isDefinitionDataReady() { | ||
const minSegmentLen = this._config.isLive ? 1 : 2; | ||
return Object.keys(this._tempPendingSegments).every((key) => this._tempPendingSegments[key].length >= minSegmentLen); | ||
} | ||
_onvProgress (e) { | ||
this._checkAndResumeStuckPlayback() | ||
} | ||
get isDefinitionDataReady () { | ||
const minSegmentLen = 10 | ||
return Object.keys(this._tempPendingSegments).every((key) => this._tempPendingSegments[key].length >= minSegmentLen) | ||
} | ||
} | ||
export default FlvPlayer; | ||
export default FlvPlayer |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
9216
657884
5