Last active
March 29, 2020 16:10
-
-
Save anuragteapot/288c51d8edd7c0743a73b7fa4d59fcf2 to your computer and use it in GitHub Desktop.
Media Source Extension
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function debounce(func, wait, immediate) { | |
var timeout; | |
return function() { | |
var context = this; | |
var args = arguments; | |
var later = function() { | |
timeout = null; | |
if (!immediate) func.apply(context, args); | |
}; | |
var callNow = immediate && !timeout; | |
clearTimeout(timeout); | |
timeout = setTimeout(later, wait); | |
if (callNow) func.apply(context, args); | |
}; | |
} | |
class MediaSourceApi { | |
/** | |
* | |
* Constructor | |
* | |
* @method constructor | |
* | |
* @param {HTMLVideoElement} videoElement | |
* @param {string} src | |
* @param {Boolean} autoPlay | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
constructor(videoElement, src, autoPlay) { | |
/** | |
* @private { HTMLVideoElement } | |
*/ | |
this.video = videoElement; | |
/** | |
* @private { Boolean } | |
*/ | |
this.autoPlay = autoPlay; | |
/** | |
* @private { String } | |
*/ | |
this.assetURL = src; | |
/** | |
* @private { Object } | |
*/ | |
this.queue = {}; | |
/** | |
* @private { Boolean } | |
*/ | |
this.video.muted = false; | |
/** | |
* @private { String } | |
*/ | |
this.mimeCodec = 'video/mp4; codecs="avc1.64001e, mp4a.40.2"'; | |
/** | |
* @private { MediaSource } | |
*/ | |
this.mediaSource = null; | |
/** | |
* @private { number } | |
*/ | |
this.segmentNumbers; | |
/** | |
* @private { number } | |
*/ | |
this.segmentLength = 0; | |
/** | |
* @private { number } | |
* | |
* */ | |
this.segmentDuration = 0; | |
/** | |
* @private { number } | |
*/ | |
this.segmentCurrent = 0; | |
/** | |
* @private { number } | |
*/ | |
this.segmentFetched = 0; | |
/** | |
* @private { Array } | |
*/ | |
this.requestedSegments = []; | |
/** | |
* @private { Double } | |
*/ | |
this.bufferedStartTime = 0.0; | |
/** | |
* @private { Double } | |
*/ | |
this.bufferedEndTime = 0.0; | |
/** | |
* @private { number} | |
*/ | |
this.segmentBufferDiff = 3; | |
/** | |
* @private { SourceBuffer } | |
*/ | |
this.sourceBuffer = null; | |
} | |
/** | |
* | |
* This function initialize the player. | |
* | |
* @method init | |
* | |
* @param {number} time | |
* | |
* @returns {this} this | |
* | |
* @public | |
* | |
*/ | |
init() { | |
if ( | |
"MediaSource" in window && | |
MediaSource.isTypeSupported(this.mimeCodec) | |
) { | |
this.mediaSource = new MediaSource(); | |
this.video.src = URL.createObjectURL(this.mediaSource); | |
this.addEvents("MEDIA_SOURCE"); | |
} else { | |
console.warn("Unsupported MIME type or codec: ", this.mimeCodec); | |
console.warn("Playing with default HTML5 Video Player"); | |
this.video.src = this.assetURL; | |
} | |
return this; | |
} | |
/** | |
* | |
* This function flush data. | |
* | |
* @method flushData | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
flushData() { | |
this.mediaSource = null; | |
this.segmentNumbers; | |
this.segmentLength = 0; | |
this.segmentDuration = 0; | |
this.segmentCurrent = 0; | |
this.segmentFetched = 0; | |
this.requestedSegments = []; | |
this.bufferedStartTime = 0.0; | |
this.bufferedEndTime = 0.0; | |
this.segmentBufferDiff = 3; | |
this.sourceBuffer = null; | |
} | |
/** | |
* | |
* This function attaches events. | |
* | |
* @method addEvents | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
addEvents(type) { | |
if (!type) { | |
return new Error("Types is required"); | |
} | |
if (type == "VIDEO") { | |
this.video.addEventListener( | |
"seeking", | |
debounce(this.handleSeek, 500), | |
false | |
); | |
this.video.addEventListener("seeked", this.videoOnSeeked, false); | |
this.video.addEventListener("loadeddata", this.videOnLoadedData, false); | |
this.video.addEventListener("timeupdate", this.checkBuffer, false); | |
this.video.addEventListener("canplay", this.onCanPlay, false); | |
} else if (type == "MEDIA_SOURCE") { | |
this.mediaSource.addEventListener( | |
"sourceopen", | |
this.mediaSourceOpen, | |
false | |
); | |
this.mediaSource.addEventListener( | |
"sourceended", | |
this.mediaSourceEnded, | |
false | |
); | |
this.mediaSource.addEventListener( | |
"sourceclose", | |
this.mediaSourceClose, | |
false | |
); | |
} else if (type == "SOURCE_BUFFER") { | |
this.sourceBuffer.addEventListener( | |
"update", | |
this.sourceBufferOnUpdate, | |
false | |
); | |
this.sourceBuffer.addEventListener( | |
"updatestart", | |
this.sourceBufferUpdateStart, | |
false | |
); | |
this.sourceBuffer.addEventListener( | |
"updateend", | |
this.sourceBufferOnUpdateEnd | |
); | |
this.sourceBuffer.addEventListener( | |
"abort", | |
this.sourceBufferAbort, | |
false | |
); | |
this.sourceBuffer.addEventListener( | |
"error", | |
this.sourceBufferError, | |
false | |
); | |
} | |
} | |
/** | |
* | |
* This function remove events. | |
* | |
* @method removeEvents | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
removeEvents(type) { | |
if (!type) { | |
return new Error("Types is required"); | |
} | |
if (type == "VIDEO") { | |
this.video.removeEventListener("seeking", debounce(this.handleSeek, 500)); | |
this.video.removeEventListener("seeked", this.videoOnSeeked); | |
this.video.removeEventListener("loadeddata", this.videOnLoadedData); | |
this.video.removeEventListener("timeupdate", this.checkBuffer); | |
this.video.removeEventListener("canplay", this.onCanPlay); | |
} else if (type == "MEDIA_SOURCE") { | |
this.mediaSource.removeEventListener("sourceopen", this.mediaSourceOpen); | |
this.mediaSource.removeEventListener( | |
"sourceended", | |
this.mediaSourceEnded | |
); | |
this.mediaSource.removeEventListener( | |
"sourceclose", | |
this.mediaSourceClose | |
); | |
} else if (type == "SOURCE_BUFFER") { | |
this.sourceBuffer.removeEventListener( | |
"update", | |
this.sourceBufferOnUpdate | |
); | |
this.sourceBuffer.removeEventListener( | |
"updatestart", | |
this.sourceBufferUpdateStart | |
); | |
this.sourceBuffer.removeEventListener( | |
"updateend", | |
this.sourceBufferOnUpdateEnd | |
); | |
this.sourceBuffer.removeEventListener("abort", this.sourceBufferAbort); | |
this.sourceBuffer.removeEventListener("error", this.sourceBufferError); | |
} | |
} | |
/** | |
* | |
* Use to change video src.. | |
* | |
* @method change | |
* | |
* @param {String} src | |
* | |
* @returns {VoidFunction} | |
* | |
* @async | |
* | |
* @public | |
* | |
*/ | |
async change(src) { | |
this.video.pause(); | |
await this.removeSourceBuffer(0, Infinity); | |
this.sourceBuffer.appendWindowStart = 0; | |
this.sourceBuffer.appendWindowEnd = Infinity; | |
this.removeEvents("VIDEO"); | |
this.removeEvents("SOURCE_BUFFER"); | |
this.removeEvents("MEDIA_SOURCE"); | |
if (this.mediaSource.readyState == "open") { | |
this.mediaSource.endOfStream(); | |
} | |
this.assetURL = src; | |
this.flushData(); | |
this.init(); | |
} | |
/** | |
* | |
* Use to destroy video. | |
* | |
* @method destroy | |
* | |
* @param {String} src | |
* | |
* @returns {VoidFunction} | |
* | |
* @async | |
* | |
* @public | |
* | |
*/ | |
async destroy() { | |
this.video.pause(); | |
await this.removeSourceBuffer(0, Infinity); | |
this.sourceBuffer.appendWindowStart = 0; | |
this.sourceBuffer.appendWindowEnd = Infinity; | |
this.removeEvents("VIDEO"); | |
this.removeEvents("SOURCE_BUFFER"); | |
this.removeEvents("MEDIA_SOURCE"); | |
this.mediaSource.removeSourceBuffer(this.sourceBuffer); | |
if (this.mediaSource.readyState == "open") { | |
this.mediaSource.endOfStream(); | |
} | |
this.video.src = null; | |
this.video.removeAttribute("src"); | |
} | |
/** | |
* | |
* Use to end video. | |
* | |
* @method end | |
* | |
* @returns {VoidFunction} | |
* | |
* @async | |
* | |
* @public | |
* | |
*/ | |
async end() { | |
await this.removeSourceBuffer(0, Infinity); | |
this.sourceBuffer.appendWindowStart = 0; | |
this.sourceBuffer.appendWindowEnd = Infinity; | |
if (this.mediaSource.readyState == "open") { | |
this.mediaSource.endOfStream(); | |
} | |
} | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method mediaSourceClose | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
mediaSourceClose(event) { | |
console.log(event.type); | |
} | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method mediaSourceEnded | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
mediaSourceEnded(event) { | |
console.log(event.type); | |
} | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method mediaSourceOpen | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
mediaSourceOpen = async event => { | |
if (this.mediaSource.sourceBuffers.length > 0) return; | |
this.addEvents("VIDEO"); | |
this.sourceBuffer = this.mediaSource.addSourceBuffer(this.mimeCodec); | |
this.addEvents("SOURCE_BUFFER"); | |
const fileLength = await this.getFileLength(this.assetURL); | |
this.setFileLength(fileLength); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method sourceBufferOnUpdateEnd | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
sourceBufferOnUpdateEnd = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method sourceBufferOnUpdate | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
sourceBufferOnUpdate = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method sourceBufferAbort | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
sourceBufferAbort = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method sourceBufferError | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
sourceBufferError = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method sourceBufferUpdateStart | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
sourceBufferUpdateStart = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method videoOnSeeked | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
videoOnSeeked = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method videOnLoadedData | |
* | |
* @param {Event} event | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
videOnLoadedData = event => { | |
// console.log(event.type); | |
}; | |
/** | |
* | |
* This function play the video. | |
* | |
* @method onCanPlay | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
onCanPlay = () => { | |
this.segmentDuration = this.video.duration / this.segmentNumbers; | |
if (this.autoPlay) { | |
const promise = this.video.play(); | |
if (promise !== undefined) { | |
promise | |
.then(_ => { | |
console.log(_); | |
}) | |
.catch(error => { | |
console.log(error); | |
}); | |
} | |
} | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method setFileLength | |
* | |
* @param {number} fileLength | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
*/ | |
setFileLength = async fileLength => { | |
this.segmentNumbers = parseInt((fileLength / 1024 / 1024).toFixed(0)); | |
for (let i = 0; i < this.segmentNumbers; ++i) { | |
this.requestedSegments[i] = false; | |
} | |
this.segmentLength = Math.round(fileLength / this.segmentNumbers); | |
const segmentInfo = this.getSegmentInfoByNumber(0); | |
try { | |
const responseData = await this.fetchRange( | |
this.assetURL, | |
segmentInfo.start, | |
segmentInfo.end | |
); | |
await this.appendSegment(responseData); | |
this.requestedSegments[0] = true; | |
} catch (err) { | |
console.log(err); | |
} | |
}; | |
/** | |
* | |
* This function remove source buffer. | |
* | |
* @method removeSourceBuffer | |
* | |
* @param {number} start | |
* @param {number} end | |
* | |
* @returns {Promise} | |
* | |
* @public | |
* | |
*/ | |
removeSourceBuffer(start, end) { | |
return new Promise((resolve, reject) => { | |
this.sourceBuffer.remove(start, end); | |
this.sourceBuffer.addEventListener("updateend", resolve); | |
this.sourceBuffer.addEventListener("error", reject); | |
}); | |
} | |
/** | |
* | |
* This function get the length of file. | |
* | |
* @method getFileLength | |
* | |
* @param {string} url | |
* | |
* @returns {Promise} | |
* | |
* @public | |
* | |
*/ | |
getFileLength = url => { | |
return new Promise((resolve, reject) => { | |
const xhr = new XMLHttpRequest(); | |
xhr.open("head", url); | |
xhr.onload = () => { | |
resolve(xhr.getResponseHeader("content-length")); | |
}; | |
xhr.onerror = err => { | |
reject(err); | |
}; | |
xhr.send(); | |
}); | |
}; | |
/** | |
* | |
* This function makes https request to get data. | |
* | |
* @method fetchRange | |
* | |
* @param {string} url | |
* @param {number} start | |
* @param {number} end | |
* | |
* @returns {Promise} | |
* | |
* @public | |
* | |
*/ | |
fetchRange = (url, start, end) => { | |
return new Promise((resolve, reject) => { | |
const xhr = new XMLHttpRequest(); | |
xhr.open("get", url); | |
xhr.responseType = "arraybuffer"; | |
xhr.setRequestHeader("Range", "bytes=" + start + "-" + end); | |
xhr.onload = async () => { | |
resolve(new Uint8Array(xhr.response)); | |
}; | |
xhr.onerror = err => { | |
reject(err); | |
}; | |
xhr.send(); | |
}); | |
}; | |
/** | |
* | |
* This function append buffer to source buffer. | |
* | |
* @method appendSegment | |
* | |
* @param {ArrayBuffer} chunk | |
* | |
* @returns {Promise} | |
* | |
* @public | |
* | |
*/ | |
appendSegment = chunk => { | |
return new Promise((resolve, reject) => { | |
const file = new Blob([chunk], { type: "video/mp4" }); | |
const reader = new FileReader(); | |
// const duration = this.mediaSource.duration; | |
// if (this.segmentCurrent != 0 && duration != NaN) { | |
// console.log("Duration: " + duration); | |
// this.sourceBuffer.timestampOffset = duration; | |
// } | |
reader.onload = event => { | |
const segmentChunk = new Uint8Array(event.target.result); | |
if (segmentChunk === null) { | |
mediaSource.endOfStream("network"); | |
return reject(new Error("Network Error")); | |
} | |
this.sourceBuffer.appendBuffer(new Uint8Array(event.target.result)); | |
this.sourceBuffer.addEventListener("updateend", resolve); | |
this.sourceBuffer.addEventListener("error", reject); | |
}; | |
reader.readAsArrayBuffer(file); | |
}); | |
}; | |
/** | |
* | |
* This function check for buffer and perform various operaions. | |
* | |
* @method checkBuffer | |
* | |
* @returns {VoidFunction} | |
* | |
* @public | |
* | |
* @async | |
* | |
*/ | |
checkBuffer = async () => { | |
if (this.sourceBuffer.updating) { | |
return; | |
} | |
if (this.mediaSource.readyState !== "open") { | |
return; | |
} | |
if (this.sourceBuffer.mode == "PARSING_MEDIA_SEGMENT") { | |
return; | |
} | |
if (this.video.buffered.length >= 1) { | |
this.bufferedEndTime = this.video.buffered.end( | |
this.video.buffered.length - 1 | |
); | |
this.bufferedStartTime = this.video.buffered.start( | |
this.video.buffered.length - 1 | |
); | |
} | |
const segmentInfo = this.getSegmentInfoByTime(this.bufferedStartTime); | |
if (this.requestedSegments[segmentInfo.segment] == true) { | |
for (let i = 0; i < segmentInfo.segment; i++) { | |
this.requestedSegments[i] = false; | |
} | |
} | |
const nextSegment = this.getNextSegment(); | |
if (nextSegment == this.segmentNumbers && this.haveAllSegments()) { | |
if (this.mediaSource.readyState == "open") { | |
this.mediaSource.endOfStream(); | |
} | |
this.video.removeEventListener("timeupdate", this.checkBuffer); | |
} else if (this.shouldFetchNextSegment(nextSegment)) { | |
this.requestedSegments[nextSegment] = true; | |
this.segmentCurrent = nextSegment; | |
const segmentInfo = this.getSegmentInfoByNumber(nextSegment); | |
console.log(segmentInfo); | |
try { | |
const responseData = await this.fetchRange( | |
this.assetURL, | |
segmentInfo.start, | |
segmentInfo.end | |
); | |
await this.appendSegment(responseData); | |
} catch (err) { | |
this.requestedSegments[nextSegment] = false; | |
console.log(err); | |
} | |
} | |
}; | |
/** | |
* | |
* This function which handle seek. | |
* | |
* @method handleSeek | |
* | |
* @returns {VoidFunction} | |
* | |
* @async | |
* | |
* @public | |
* | |
*/ | |
handleSeek = async () => { | |
if (this.sourceBuffer.updating) { | |
return; | |
} | |
if (this.mediaSource.readyState !== "open") { | |
return; | |
} | |
if (this.sourceBuffer.mode == "PARSING_MEDIA_SEGMENT") { | |
return; | |
} | |
if ( | |
this.bufferedEndTime > this.video.currentTime && | |
this.bufferedStartTime < this.video.currentTime | |
) { | |
return; | |
} | |
const nextSegment = this.getNextSegment(); | |
if (nextSegment === this.segmentNumbers && this.haveAllSegments()) { | |
if (this.mediaSource.readyState == "open") { | |
this.mediaSource.endOfStream(); | |
} | |
this.video.removeEventListener("timeupdate", this.checkBuffer); | |
} else { | |
console.log("Handle out seek"); | |
console.log(this); | |
if (this.shouldFetchNextSegment(nextSegment)) { | |
console.log("yes"); | |
} | |
// this.sourceBuffer.appendWindowStart = Math.floor(this.video.currentTime); | |
// this.sourceBuffer.appendWindowEnd = Infinity; | |
// this.segmentCurrent = nextSegment; | |
// this.video.removeEventListener("timeupdate", this.checkBuffer); | |
// for (let segment = 0; segment <= nextSegment; segment++) { | |
// if (this.shouldFetchNextSegment(segment)) { | |
// this.requestedSegments[segment] = true; | |
// const segmentInfo = this.getSegmentInfoByNumber(segment); | |
// try { | |
// const responseData = await this.fetchRange( | |
// this.assetURL, | |
// segmentInfo.start, | |
// segmentInfo.end | |
// ); | |
// await this.appendSegment(responseData); | |
// } catch (err) { | |
// console.log(err); | |
// } | |
// } | |
// } | |
// this.video.addEventListener("timeupdate", this.checkBuffer); | |
} | |
}; | |
fetch = async time => { | |
const segmentInfo = this.getSegmentInfoByTime(parseInt(time)); | |
this.video.pause(); | |
// console.log(this.sourceBuffer.buffered); | |
// console.log(this.video.buffered); | |
// await this.removeSourceBuffer(0, segmentInfo.timeStart); | |
// console.log(segmentInfo); | |
this.sourceBuffer.appendWindowStart = segmentInfo.timeStart; | |
// this.sourceBuffer.timestampOffset = segmentInfo.timeStart; | |
try { | |
const responseData = await this.fetchRange( | |
this.assetURL, | |
segmentInfo.start, | |
segmentInfo.end | |
); | |
await this.appendSegment(responseData); | |
this.video.currentTime = time; | |
} catch (err) { | |
console.log(err); | |
} | |
this.video.play(); | |
}; | |
/** | |
* | |
* This function to get next segment need to be fetched. | |
* | |
* @method getNextSegment | |
* | |
* @returns {number} | |
* | |
* @public | |
* | |
*/ | |
getNextSegment = () => { | |
const s1 = | |
((Math.ceil(this.video.currentTime) / Math.floor(this.segmentDuration)) | | |
0) + | |
1; | |
const currentSegmentInfo = this.getSegmentInfoByTime( | |
this.video.currentTime | |
); | |
const s2 = | |
this.segmentCurrent + 1 <= this.segmentNumbers && | |
this.segmentCurrent - currentSegmentInfo.segment <= this.segmentBufferDiff | |
? this.segmentCurrent + 1 | |
: this.segmentCurrent; | |
return Math.max(s1, s2); | |
}; | |
/** | |
* | |
* This function check every segment is fetched or not/ | |
* | |
* @method haveAllSegments | |
* | |
* @returns {Boolean} | |
* | |
* @public | |
* | |
*/ | |
haveAllSegments = () => { | |
return this.requestedSegments.every(val => { | |
return !!val; | |
}); | |
}; | |
/** | |
* | |
* This to check whether should fetch next segment or not ? | |
* | |
* @method shouldFetchNextSegment | |
* | |
* @param {number} nextSegment | |
* | |
* @returns {Boolean} | |
* | |
* @public | |
* | |
*/ | |
shouldFetchNextSegment = nextSegment => { | |
if ( | |
this.segmentCurrent <= 2 && | |
this.requestedSegments[nextSegment] == false | |
) { | |
return true; | |
} | |
return ( | |
this.video.currentTime >= | |
Math.floor(this.segmentDuration) * nextSegment * 0.1 && | |
this.requestedSegments[nextSegment] == false | |
); | |
}; | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method getSegmentInfoByTime | |
* | |
* @param {number} time | |
* | |
* @returns {{ segment: number, timeStart: number, timeEnd:number, start:number, end :number}} | |
* | |
* @public | |
* | |
*/ | |
getSegmentInfoByTime(time) { | |
const segmentNumber = Math.floor(time / this.segmentDuration); | |
return this.getSegmentInfoByNumber(segmentNumber); | |
} | |
/** | |
* | |
* This function gives start and end length of media to be fetched. | |
* | |
* @method getSegmentInfoByNumber | |
* | |
* @param {number} time | |
* | |
* @returns {{ segment: number, timeStart: number, timeEnd:number, start:number, end :number}} | |
* | |
* @public | |
* | |
*/ | |
getSegmentInfoByNumber(segmentNumber) { | |
return { | |
segment: segmentNumber, | |
timeStart: | |
Math.floor(Math.floor(segmentNumber) * this.segmentDuration) + | |
(segmentNumber == 0 ? 0 : 1), | |
timeEnd: Math.floor(Math.floor(segmentNumber + 1) * this.segmentDuration), | |
start: | |
Math.floor(segmentNumber) * this.segmentLength + | |
(segmentNumber == 0 ? 0 : 1), | |
end: Math.floor(segmentNumber + 1) * this.segmentLength | |
}; | |
} | |
} | |
if (typeof window != undefined) { | |
window.FlexVideoPlayer = MediaSourceApi; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment