You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
133 lines
3.2 KiB
133 lines
3.2 KiB
import { Now } from '../../utils'
|
|
|
|
export default class AjaxProgressiveSource {
|
|
constructor(url, options) {
|
|
this.url = url
|
|
this.destination = null
|
|
this.request = null
|
|
this.streaming = false
|
|
|
|
this.completed = false
|
|
this.established = false
|
|
this.progress = 0
|
|
|
|
this.fileSize = 0
|
|
this.loadedSize = 0
|
|
this.chunkSize = options.chunkSize || 1024 * 1024
|
|
|
|
this.isLoading = false
|
|
this.loadStartTime = 0
|
|
this.throttled = options.throttled !== false
|
|
this.aborted = false
|
|
|
|
this.onEstablishedCallback = options.onSourceEstablished
|
|
this.onCompletedCallback = options.onSourceCompleted
|
|
}
|
|
|
|
connect(destination) {
|
|
this.destination = destination
|
|
}
|
|
|
|
start() {
|
|
this.request = new XMLHttpRequest()
|
|
|
|
this.request.onreadystatechange = function() {
|
|
if (this.request.readyState === this.request.DONE) {
|
|
this.fileSize = parseInt(
|
|
this.request.getResponseHeader('Content-Length')
|
|
)
|
|
this.loadNextChunk()
|
|
}
|
|
}.bind(this)
|
|
|
|
this.request.onprogress = this.onProgress.bind(this)
|
|
this.request.open('HEAD', this.url)
|
|
this.request.send()
|
|
}
|
|
|
|
resume(secondsHeadroom) {
|
|
if (this.isLoading || !this.throttled) {
|
|
return
|
|
}
|
|
|
|
// Guess the worst case loading time with lots of safety margin. This is
|
|
// somewhat arbitrary...
|
|
const worstCaseLoadingTime = this.loadTime * 8 + 2
|
|
if (worstCaseLoadingTime > secondsHeadroom) {
|
|
this.loadNextChunk()
|
|
}
|
|
}
|
|
|
|
destroy() {
|
|
this.request.abort()
|
|
this.aborted = true
|
|
}
|
|
|
|
loadNextChunk() {
|
|
const start = this.loadedSize
|
|
const end = Math.min(this.loadedSize + this.chunkSize - 1, this.fileSize - 1)
|
|
|
|
if (start >= this.fileSize || this.aborted) {
|
|
this.completed = true
|
|
if (this.onCompletedCallback) {
|
|
this.onCompletedCallback(this)
|
|
}
|
|
return
|
|
}
|
|
|
|
this.isLoading = true
|
|
this.loadStartTime = Now()
|
|
this.request = new XMLHttpRequest()
|
|
|
|
this.request.onreadystatechange = function() {
|
|
if (
|
|
this.request.readyState === this.request.DONE &&
|
|
this.request.status >= 200 &&
|
|
this.request.status < 300
|
|
) {
|
|
this.onChunkLoad(this.request.response)
|
|
} else if (this.request.readyState === this.request.DONE) {
|
|
// Retry?
|
|
if (this.loadFails++ < 3) {
|
|
this.loadNextChunk()
|
|
}
|
|
}
|
|
}.bind(this)
|
|
|
|
if (start === 0) {
|
|
this.request.onprogress = this.onProgress.bind(this)
|
|
}
|
|
|
|
this.request.open('GET', this.url + '?' + start + '-' + end)
|
|
this.request.setRequestHeader('Range', 'bytes=' + start + '-' + end)
|
|
this.request.responseType = 'arraybuffer'
|
|
this.request.send()
|
|
}
|
|
|
|
onProgress(ev) {
|
|
this.progress = ev.loaded / ev.total
|
|
}
|
|
|
|
onChunkLoad(data) {
|
|
const isFirstChunk = !this.established
|
|
this.established = true
|
|
this.progress = 1
|
|
|
|
this.loadedSize += data.byteLength
|
|
this.loadFails = 0
|
|
this.isLoading = false
|
|
|
|
if (isFirstChunk && this.onEstablishedCallback) {
|
|
this.onEstablishedCallback(this)
|
|
}
|
|
|
|
if (this.destination) {
|
|
this.destination.write(data)
|
|
}
|
|
|
|
this.loadTime = Now() - this.loadStartTime
|
|
if (!this.throttled) {
|
|
this.loadNextChunk()
|
|
}
|
|
}
|
|
}
|