Skip to content

Commit

Permalink
playlist-loader: support lazy frag/key URL resolving
Browse files Browse the repository at this point in the history
frag/key absolute URL is resolved on FRAG_LOADING/KEY_LOADING
speed up playlist parsing (performance factor 3)
related to https://github.com/dailymotion/hls.js/issues/875
  • Loading branch information
mangui committed Dec 12, 2016
1 parent e4ad9dc commit dacd64b
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 9 deletions.
10 changes: 10 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,16 @@ Enable WebWorker (if available on browser) for TS demuxing/MP4 remuxing, to impr
Enable to use JavaScript version AES decryption for fallback of WebCrypto API.
#### `enableLazyURLResolve`
(default: `false`)
Enable lazy URL resolving in fragment/key object.
Instead of resolving relative fragment/key URL on playlist parsing, URL are resolved on `FRAG_LOADING` / `KEY_LOADING`
this improves manifest parsing performance.
the drawback is that `frag.url` is not set in frag object, instead it is replaced by `frag.baseurl and `frag.relurl`

#### `startLevel`

(default: `undefined`)
Expand Down
7 changes: 6 additions & 1 deletion demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ <h2 class="title">demo page</h2>
<label class="innerControls"><input id="enableStreaming" type=checkbox checked/> Enable Streaming</label>
<label class="innerControls"><input id="autoRecoverError" type=checkbox checked/> Auto-Recover Media Error</label>
<label class="innerControls"><input id="enableWorker" type=checkbox checked/> Enable Worker</label>
<label class="innerControls"><input id="enableLazyURLResolve" type=checkbox checked/> Lazy Frag URL Resolve</label>
<label class="innerControls">Level Capping <input id="levelCapping" type=number/></label>
<label class="innerControls">default Audio Codec <input id="defaultAudioCodec"/></label>
<div id="StreamPermalink" class="innerControls"></div>
Expand Down Expand Up @@ -226,11 +227,13 @@ <h4> Stats Display </h4>
$('#enableStreaming').click(function() { enableStreaming = this.checked; loadStream($('#streamURL').val()); });
$('#autoRecoverError').click(function() { autoRecoverError = this.checked; updatePermalink();});
$('#enableWorker').click(function() { enableWorker = this.checked; updatePermalink();});
$('#enableLazyURLResolve').click(function() { enableLazyURLResolve = this.checked; updatePermalink();});
$('#levelCapping').change(function() { levelCapping = this.value; updatePermalink();});
$('#defaultAudioCodec').change(function() { defaultAudioCodec = this.value; updatePermalink();});
$('#enableStreaming').prop( "checked", enableStreaming );
$('#autoRecoverError').prop( "checked", autoRecoverError );
$('#enableWorker').prop( "checked", enableWorker );
$('#enableLazyURLResolve').prop( "checked", enableLazyURLResolve );
$('#levelCapping').val(levelCapping);
$('#defaultAudioCodec').val(defaultAudioCodec || "undefined");

Expand All @@ -244,6 +247,7 @@ <h4> Stats Display </h4>
enableStreaming = JSON.parse(getURLParam('enableStreaming',true))
autoRecoverError = JSON.parse(getURLParam('autoRecoverError',true)),
enableWorker = JSON.parse(getURLParam('enableWorker',true)),
enableLazyURLResolve = JSON.parse(getURLParam('enableLazyURLResolve',true)),
levelCapping = JSON.parse(getURLParam('levelCapping',-1)),
defaultAudioCodec = getURLParam('defaultAudioCodec',undefined);
var video = $('#video')[0];
Expand Down Expand Up @@ -274,7 +278,7 @@ <h4> Stats Display </h4>
$("#HlsStatus").text('loading ' + url);
events = { url : url, t0 : performance.now(), load : [], buffer : [], video : [], level : [], bitrate : []};
recoverDecodingErrorDate = recoverSwapAudioCodecDate = null;
hls = new Hls({debug:true, enableWorker : enableWorker, defaultAudioCodec : defaultAudioCodec});
hls = new Hls({debug:true, enableWorker : enableWorker, enableLazyURLResolve : enableLazyURLResolve, defaultAudioCodec : defaultAudioCodec});
$("#HlsStatus").text('loading manifest and attaching video element...');
hls.loadSource(url);
hls.autoLevelCapping = levelCapping;
Expand Down Expand Up @@ -1057,6 +1061,7 @@ <h4> Stats Display </h4>
'&enableStreaming=' + enableStreaming +
'&autoRecoverError=' + autoRecoverError +
'&enableWorker=' + enableWorker +
'&enableLazyURLResolve=' + enableLazyURLResolve +
'&levelCapping=' + levelCapping +
'&defaultAudioCodec=' + defaultAudioCodec;
var description = 'permalink: ' + "<a href=\"" + hlsLink + "\">" + hlsLink + "</a>";
Expand Down
1 change: 1 addition & 0 deletions src/hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Hls {
maxMaxBufferLength: 600,
enableWorker: true,
enableSoftwareAES: true,
enableLazyURLResolve : false,
manifestLoadingTimeOut: 10000,
manifestLoadingMaxRetry: 1,
manifestLoadingRetryDelay: 1000,
Expand Down
4 changes: 3 additions & 1 deletion src/loader/fragment-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors';
import {logger} from '../utils/logger';
import URLToolkit from 'url-toolkit';

class FragmentLoader extends EventHandler {

Expand Down Expand Up @@ -40,7 +41,8 @@ class FragmentLoader extends EventHandler {
loader = this.loaders[type] = frag.loader = typeof(config.fLoader) !== 'undefined' ? new config.fLoader(config) : new config.loader(config);

let loaderContext, loaderConfig, loaderCallbacks;
loaderContext = { url : frag.url, frag : frag, responseType : 'arraybuffer', progressData : false};
let url = frag.url ? frag.url : URLToolkit.buildAbsoluteURL(frag.baseurl,frag.relurl);
loaderContext = { url : url , frag : frag, responseType : 'arraybuffer', progressData : false};
let start = frag.byteRangeStartOffset, end = frag.byteRangeEndOffset;
if (!isNaN(start) && !isNaN(end)) {
loaderContext.rangeStart = start;
Expand Down
3 changes: 2 additions & 1 deletion src/loader/key-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors';
import {logger} from '../utils/logger';
import URLToolkit from 'url-toolkit';

class KeyLoader extends EventHandler {

Expand All @@ -32,7 +33,7 @@ class KeyLoader extends EventHandler {
type = frag.type,
loader = this.loaders[type],
decryptdata = frag.decryptdata,
uri = decryptdata.uri;
uri = decryptdata.uri ? decryptdata.uri : URLToolkit.buildAbsoluteURL(decryptdata.baseuri,decryptdata.reluri);
// if uri is different from previous one or if decrypt key not retrieved yet
if (uri !== this.decrypturl || this.decryptkey === null) {
let config = this.hls.config;
Expand Down
23 changes: 17 additions & 6 deletions src/loader/playlist-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,9 @@ class PlaylistLoader extends EventHandler {
byteRangeEndOffset = null,
byteRangeStartOffset = null,
tagList = [],
i;
i,
config = this.hls.config,
lazyURLResolve = config ? config.enableLazyURLResolve : false;

LEVEL_PLAYLIST_REGEX.lastIndex = 0;

Expand Down Expand Up @@ -269,9 +271,7 @@ class PlaylistLoader extends EventHandler {
if (!isNaN(duration)) {
var sn = currentSN++;
fragdecryptdata = this.fragmentDecryptdataFromLevelkey(levelkey, sn);
var url = value1 ? this.resolve(value1, baseurl) : null;
frag = {url: url,
type : type,
frag = {type : type,
duration: duration,
title: title,
start: totalduration,
Expand All @@ -281,6 +281,12 @@ class PlaylistLoader extends EventHandler {
decryptdata : fragdecryptdata,
programDateTime: programDateTime,
tagList: tagList};
if (lazyURLResolve) {
frag.relurl = value1;
frag.baseurl = baseurl;
} else {
frag.url = value1 ? this.resolve(value1, baseurl) : null;
}
// only include byte range options if used/needed
if(byteRangeStartOffset !== null) {
frag.byteRangeStartOffset = byteRangeStartOffset;
Expand All @@ -307,7 +313,12 @@ class PlaylistLoader extends EventHandler {
if ((decrypturi) && (decryptmethod === 'AES-128')) {
levelkey.method = decryptmethod;
// URI to get the key
levelkey.uri = this.resolve(decrypturi, baseurl);
if (lazyURLResolve) {
levelkey.baseuri = baseurl;
levelkey.reluri = decrypturi;
} else {
levelkey.uri = this.resolve(decrypturi, baseurl);
}
levelkey.key = null;
// Initialization Vector (IV)
levelkey.iv = decryptiv;
Expand Down Expand Up @@ -336,7 +347,7 @@ class PlaylistLoader extends EventHandler {
}
}
//logger.log('found ' + level.fragments.length + ' fragments');
if(frag && !frag.url) {
if(frag && !(frag.url || frag.relurl)) {
level.fragments.pop();
totalduration-=frag.duration;
}
Expand Down

0 comments on commit dacd64b

Please sign in to comment.