Skip to content

Commit

Permalink
clean-up loader interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mangui committed Aug 19, 2016
1 parent a2e050d commit 71df224
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 92 deletions.
6 changes: 3 additions & 3 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -784,19 +784,19 @@ Full list of errors is described below:
### Network Errors
- `Hls.ErrorDetails.MANIFEST_LOAD_ERROR` - raised when manifest loading fails because of a network error
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.MANIFEST_LOAD_ERROR`, fatal : `true`, url : manifest URL, response : xhr response, loader : URL loader }
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.MANIFEST_LOAD_ERROR`, fatal : `true`, url : manifest URL, response : { code: error code, text: error text }, loader : URL loader }
- `Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT` - raised when manifest loading fails because of a timeout
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT`, fatal : `true`, url : manifest URL, loader : URL loader }
- `Hls.ErrorDetails.MANIFEST_PARSING_ERROR` - raised when manifest parsing failed to find proper content
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.MANIFEST_PARSING_ERROR`, fatal : `true`, url : manifest URL, reason : parsing error reason }
- `Hls.ErrorDetails.LEVEL_LOAD_ERROR` - raised when level loading fails because of a network error
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.LEVEL_LOAD_ERROR`, fatal : `true`, url : level URL, response : xhr response, loader : URL loader }
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.LEVEL_LOAD_ERROR`, fatal : `true`, url : level URL, response : { code: error code, text: error text }, loader : URL loader }
- `Hls.ErrorDetails.LEVEL_LOAD_TIMEOUT` - raised when level loading fails because of a timeout
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.LEVEL_LOAD_TIMEOUT`, fatal : `true`, url : level URL, loader : URL loader }
- `Hls.ErrorDetails.LEVEL_SWITCH_ERROR` - raised when level switching fails
- data: { type : `OTHER_ERROR`, details : `Hls.ErrorDetails.LEVEL_SWITCH_ERROR`, fatal : `false`, level : failed level index, reason : failure reason }
- `Hls.ErrorDetails.FRAG_LOAD_ERROR` - raised when fragment loading fails because of a network error
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.FRAG_LOAD_ERROR`, fatal : `true` or `false`, frag : fragment object, response : xhr response }
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.FRAG_LOAD_ERROR`, fatal : `true` or `false`, frag : fragment object, response : { code: error code, text: error text } }
- `Hls.ErrorDetails.FRAG_LOOP_LOADING_ERROR` - raised upon detection of same fragment being requested in loop
- data: { type : `NETWORK_ERROR`, details : `Hls.ErrorDetails.FRAG_LOOP_LOADING_ERROR`, fatal : `true` or `false`, frag : fragment object }
- `Hls.ErrorDetails.FRAG_LOAD_TIMEOUT` - raised when fragment loading fails because of a timeout
Expand Down
6 changes: 3 additions & 3 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,12 @@ <h4> Stats Display </h4>
switch(data.details) {
case Hls.ErrorDetails.MANIFEST_LOAD_ERROR:
try {
$("#HlsStatus").html("cannot Load <a href=\"" + data.url + "\">" + url + "</a><br>HTTP response code:" + data.response.status + "<br>" + data.response.statusText);
if(data.response.status === 0) {
$("#HlsStatus").html("cannot Load <a href=\"" + data.context.url + "\">" + url + "</a><br>HTTP response code:" + data.response.code + " <br>" + data.response.text);
if(data.response.code === 0) {
$("#HlsStatus").append("this might be a CORS issue, consider installing <a href=\"https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi\">Allow-Control-Allow-Origin</a> Chrome Extension");
}
} catch(err) {
$("#HlsStatus").html("cannot Load <a href=\"" + data.url + "\">" + url + "</a><br>Reason:Load " + data.event.type);
$("#HlsStatus").html("cannot Load <a href=\"" + data.context.url + "\">" + url + "</a><br>Reason:Load " + data.response.text);
}
break;
case Hls.ErrorDetails.MANIFEST_LOAD_TIMEOUT:
Expand Down
16 changes: 8 additions & 8 deletions src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ export const ErrorTypes = {
};

export const ErrorDetails = {
// Identifier for a manifest load error - data: { url : faulty URL, response : XHR response}
// Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }}
MANIFEST_LOAD_ERROR: 'manifestLoadError',
// Identifier for a manifest load timeout - data: { url : faulty URL, response : XHR response}
// Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut',
// Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason}
MANIFEST_PARSING_ERROR: 'manifestParsingError',
// Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason}
MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError',
// Identifier for a level load error - data: { url : faulty URL, response : XHR response}
// Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }}
LEVEL_LOAD_ERROR: 'levelLoadError',
// Identifier for a level load timeout - data: { url : faulty URL, response : XHR response}
// Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut',
// Identifier for a level switch error - data: { level : faulty level Id, event : error description}
LEVEL_SWITCH_ERROR: 'levelSwitchError',
// Identifier for an audio track load error - data: { url : faulty URL, response : XHR response}
// Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }}
AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError',
// Identifier for an audio track load timeout - data: { url : faulty URL, response : XHR response}
// Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}
AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut',
// Identifier for fragment load error - data: { frag : fragment object, response : XHR response}
// Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }}
FRAG_LOAD_ERROR: 'fragLoadError',
// Identifier for fragment loop loading error - data: { frag : fragment object}
FRAG_LOOP_LOADING_ERROR: 'fragLoopLoadingError',
Expand All @@ -36,7 +36,7 @@ export const ErrorDetails = {
FRAG_DECRYPT_ERROR: 'fragDecryptError',
// Identifier for a fragment parsing error event - data: parsing error description
FRAG_PARSING_ERROR: 'fragParsingError',
// Identifier for decrypt key load error - data: { frag : fragment object, response : XHR response}
// Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }}
KEY_LOAD_ERROR: 'keyLoadError',
// Identifier for decrypt key load timeout error - data: { frag : fragment object}
KEY_LOAD_TIMEOUT: 'keyLoadTimeOut',
Expand Down
6 changes: 4 additions & 2 deletions src/hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import TimelineController from './controller/timeline-controller';
import FPSController from './controller/fps-controller';
import AudioTrackController from './controller/audio-track-controller';
import {logger, enableLogs} from './utils/logger';
import FetchLoader from './utils/fetch-loader';
//import FetchLoader from './utils/fetch-loader';
import XhrLoader from './utils/xhr-loader';
import EventEmitter from 'events';
import KeyLoader from './loader/key-loader';
import Cues from './utils/cues';
Expand Down Expand Up @@ -83,7 +84,8 @@ class Hls {
fpsDroppedMonitoringPeriod: 5000,
fpsDroppedMonitoringThreshold: 0.2,
appendErrorMaxRetry: 3,
loader: FetchLoader,
loader: XhrLoader,
//loader: FetchLoader,
fLoader: undefined,
pLoader: undefined,
abrController : AbrController,
Expand Down
25 changes: 17 additions & 8 deletions src/loader/fragment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,37 @@ class FragmentLoader extends EventHandler {
loader.abort();
}
loader = this.loaders[type] = frag.loader = typeof(config.fLoader) !== 'undefined' ? new config.fLoader(config) : new config.loader(config);
loader.load(frag.url, { frag : frag }, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, 0, 0, this.loadprogress.bind(this), frag);

let loaderContext, loaderConfig, loaderCallbacks;
loaderContext = { url : frag.url, frag : frag, responseType : 'arraybuffer'};
let start = frag.byteRangeStartOffset, end = frag.byteRangeEndOffset;
if (!isNaN(start) && !isNaN(end)) {
loaderContext.headers = { 'Range' : ('bytes=' + start + '-' + (end-1)) };
}
loaderConfig = { timeout : config.fragLoadingTimeOut, maxRetry : 0 , retryDelay : 0};
loaderCallbacks = { onSuccess : this.loadsuccess.bind(this), onError :this.loaderror.bind(this), onTimeout : this.loadtimeout.bind(this), onProgress: this.loadprogress.bind(this)};
loader.load(loaderContext,loaderConfig,loaderCallbacks);
}

loadsuccess(event, stats, context) {
let payload = event.currentTarget.response, frag = context.frag;
loadsuccess(response, stats, context) {
let payload = response.data, frag = context.frag;
stats.length = payload.byteLength;
// detach fragment loader on load success
frag.loader = undefined;
this.loaders[frag.type] = undefined;
this.hls.trigger(Event.FRAG_LOADED, {payload: payload, frag: frag, stats: stats});
}

loaderror(event, context) {
loaderror(response, context) {
let loader = context.loader;
if (loader) {
loader.abort();
}
this.loaders[context.type] = undefined;
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag: context.frag, response: event});
}
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag: context.frag, response: response});
}

loadtimeout(event, stats, context) {
loadtimeout(stats, context) {
let loader = context.loader;
if (loader) {
loader.abort();
Expand All @@ -67,7 +76,7 @@ class FragmentLoader extends EventHandler {
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_TIMEOUT, fatal: false, frag: context.frag});
}

loadprogress(event, stats, context) {
loadprogress(stats, context) {
let frag = context.frag;
frag.loaded = stats.loaded;
this.hls.trigger(Event.FRAG_LOAD_PROGRESS, {frag: frag, stats: stats});
Expand Down
21 changes: 11 additions & 10 deletions src/loader/key-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,34 +44,39 @@ class KeyLoader extends EventHandler {
frag.loader = this.loaders[type] = new config.loader(config);
this.decrypturl = uri;
this.decryptkey = null;
frag.loader.load(uri, { frag : frag }, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, config.fragLoadingMaxRetry, config.fragLoadingRetryDelay, this.loadprogress.bind(this), frag);

let loaderContext, loaderConfig, loaderCallbacks;
loaderContext = { url : uri, frag : frag, responseType : 'arraybuffer'};
loaderConfig = { timeout : config.fragLoadingTimeOut, maxRetry : config.fragLoadingMaxRetry , retryDelay : config.fragLoadingRetryDelay};
loaderCallbacks = { onSuccess : this.loadsuccess.bind(this), onError :this.loaderror.bind(this), onTimeout : this.loadtimeout.bind(this)};
frag.loader.load(loaderContext,loaderConfig,loaderCallbacks);
} else if (this.decryptkey) {
// we already loaded this key, return it
decryptdata.key = this.decryptkey;
this.hls.trigger(Event.KEY_LOADED, {frag: frag});
}
}

loadsuccess(event, stats, context) {
loadsuccess(response, stats, context) {
let frag = context.frag;
this.decryptkey = frag.decryptdata.key = new Uint8Array(event.currentTarget.response);
this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data);
// detach fragment loader on load success
frag.loader = undefined;
this.loaders[context.type] = undefined;
this.hls.trigger(Event.KEY_LOADED, {frag: frag});
}

loaderror(event, context) {
loaderror(response, context) {
let frag = context.frag,
loader = frag.loader;
if (loader) {
loader.abort();
}
this.loaders[context.type] = undefined;
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag: frag, response: event});
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag: frag, response: response});
}

loadtimeout(event, stats, context) {
loadtimeout(stats, context) {
let frag = context.frag,
loader = frag.loader;
if (loader) {
Expand All @@ -80,10 +85,6 @@ class KeyLoader extends EventHandler {
this.loaders[context.type] = undefined;
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_TIMEOUT, fatal: false, frag: frag});
}

loadprogress() {

}
}

export default KeyLoader;
21 changes: 13 additions & 8 deletions src/loader/playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,13 @@ class PlaylistLoader extends EventHandler {
loader.abort();
}
loader = this.loaders[context.type] = context.loader = typeof(config.pLoader) !== 'undefined' ? new config.pLoader(config) : new config.loader(config);
loader.load(url, context, '', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), timeout, retry, retryDelay);
context.url = url;
context.responseType = '';

let loaderConfig, loaderCallbacks;
loaderConfig = { timeout : timeout, maxRetry : retry , retryDelay : retryDelay};
loaderCallbacks = { onSuccess : this.loadsuccess.bind(this), onError :this.loaderror.bind(this), onTimeout : this.loadtimeout.bind(this)};
loader.load(context,loaderConfig,loaderCallbacks);
}

resolve(url, baseUrl) {
Expand Down Expand Up @@ -317,10 +323,9 @@ class PlaylistLoader extends EventHandler {
return level;
}

loadsuccess(event, stats, context) {
var target = event.currentTarget,
string = target.responseText,
url = target.responseURL,
loadsuccess(response, stats, context) {
var string = response.data,
url = response.url,
type = context.type,
id = context.id,
level = context.level,
Expand Down Expand Up @@ -370,7 +375,7 @@ class PlaylistLoader extends EventHandler {
}
}

loaderror(event, context) {
loaderror(response, context) {
var details, fatal,loader = context.loader;
switch(context.type) {
case 'manifest':
Expand All @@ -390,10 +395,10 @@ class PlaylistLoader extends EventHandler {
loader.abort();
this.loaders[context.type] = undefined;
}
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: details, fatal: fatal, url: loader.url, loader: loader, response: event.currentTarget, context : context});
this.hls.trigger(Event.ERROR, {type: ErrorTypes.NETWORK_ERROR, details: details, fatal: fatal, url: loader.url, loader: loader, response: response, context : context});
}

loadtimeout(event, stats, context) {
loadtimeout(stats, context) {
var details, fatal, loader = context.loader;
switch(context.type) {
case 'manifest':
Expand Down
37 changes: 21 additions & 16 deletions src/utils/fetch-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Fetch based logger
* timeout / abort / onprogress not supported for now
* timeout / abort : some ideas here : https://github.com/whatwg/fetch/issues/20#issuecomment-196113354
* but still it is not bullet proof as it fails to avoid data waste.... we could only cancel the
* but still it is not bullet proof as it fails to avoid data waste....
*/

class FetchLoader {
Expand All @@ -17,38 +17,43 @@ class FetchLoader {
abort() {
}

load(url, context, responseType, onSuccess, onError, onTimeout, timeout, maxRetry, retryDelay, onProgress = null, frag = null) { // jshint ignore:line

let stats = {trequest: performance.now(), retry: 0}, targetURL = url;
load(context, config, callbacks) {
let stats = {trequest: performance.now(), retry: 0}, targetURL = context.url;

let initParams = { method: 'GET',
mode: 'cors',
credentials: 'same-origin'
};

let request = new Request(url,initParams),
let request = new Request(context.url,initParams),
fetchPromise = fetch(request,initParams);

// process fetchPromise
let responsePromise = fetchPromise.then(function(response) {
stats.tfirst = Math.max(stats.trequest,performance.now());
targetURL = response.url;
if (responseType === 'arraybuffer') {
return response.arrayBuffer();
if (response.ok) {
stats.tfirst = Math.max(stats.trequest,performance.now());
targetURL = response.url;
if (context.responseType === 'arraybuffer') {
return response.arrayBuffer();
} else {
return response.text();
}
} else {
return response.text();
callbacks.onError({text : 'fetch, bad network response'}, context);
return;
}
}).catch(function(error) {
callbacks.onError({text : error.message}, context);
return;
});
// process response Promise
responsePromise.then(function(responseData) {
stats.tload = Math.max(stats.tfirst,performance.now());
let event = { currentTarget : { responseURL : targetURL } };
if (responseType === 'arraybuffer') {
event.currentTarget.response = responseData;
} else {
event.currentTarget.responseText = responseData;
if (responseData) {
stats.tload = Math.max(stats.tfirst,performance.now());
let response = { url : targetURL, data : responseData};
callbacks.onSuccess(response,stats,context);
}
onSuccess(event, stats,context);
});
}
}
Expand Down
Loading

0 comments on commit 71df224

Please sign in to comment.