Skip to content

Commit

Permalink
Merge pull request #2 from dailymotion/master
Browse files Browse the repository at this point in the history
update branch
  • Loading branch information
gjanblaszczyk committed Nov 9, 2015
2 parents 362a5a2 + d0319d7 commit db6f5fe
Show file tree
Hide file tree
Showing 17 changed files with 345 additions and 570 deletions.
40 changes: 0 additions & 40 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,46 +396,6 @@ however if ```config.autoStartLoad``` is set to ```false```, the following metho
#### ```hls.startLoad()```
start/restart playlist/fragment loading. this is only effective if MANIFEST_PARSED event has been triggered and video element has been attached to hls object.
## Analytics
playback session analytics could be retrieved through two means.
### ```hls.stats```
get : return aggregated playback session stats
```js
{
tech : 'hls.js',
levelNb : total nb of quality level referenced in Manifest
levelStart : first quality level experienced by End User
autoLevelMin : min quality level experienced by End User (in auto mode)
autoLevelMax : max quality level experienced by End User (in auto mode)
autoLevelAvg : avg quality level experienced by End User (in auto mode)
autoLevelLast : last quality level experienced by End User (in auto mode)
autoLevelSwitch : nb of quality level switch in auto mode
autoLevelCappingMin : min auto quality level capping value
autoLevelCappingMax : max auto quality level capping value
autoLevelCappingLast : last auto quality level capping value
manualLevelMin : min quality level experienced by End User (in manual mode)
manualLevelMax : max quality level experienced by End User (in manual mode)
manualLevelLast : last quality level experienced by End User (in manual mode)
manualLevelSwitch : nb of quality level switch in manual mode
fragMinKbps : min fragment load bandwidth
fragMaxKbps : max fragment load bandwidth
fragAvgKbps : avg fragment load bandwidth
fragLastKbps : last fragment load bandwidth
fragMinLatency : min fragment load latency
fragMaxLatency : max fragment load latency
fragAvgLatency : avg fragment load latency
fragLastLatency : last fragment load latency
fragBuffered : total nb of buffered fragments
fragBufferedBytes : total nb of buffered bytes
fragChangedAuto : nb of frag played (loaded in auto mode)
fragChangedManual : nb of frag played (loaded in manual mode)
fpsDropEvent : nb of FPS drop event
fpsTotalDroppedFrames : total nb of dropped frames since video element creation
}
```
## Runtime Events
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ as of today, it is supported on:
- `#EXT-X-MEDIA-SEQUENCE`
- `#EXT-X-TARGETDURATION`
- `#EXT-X-DISCONTINUITY`
- `#EXT-X-BYTERANGE`

## Getting Started

Expand Down
125 changes: 123 additions & 2 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ <h4> Stats Display </h4>
});

'use strict';
var hls,events, enableStreaming = true, autoRecoverError = false;
var hls,events, stats, enableStreaming = true, autoRecoverError = false;
var video = $('#video')[0];
video.volume = 0.05;

Expand Down Expand Up @@ -283,6 +283,7 @@ <h4> Stats Display </h4>
});
hls.on(Hls.Events.MANIFEST_PARSED,function(event,data) {
$("#HlsStatus").text("manifest successfully loaded," + hls.levels.length + " levels found");
stats = {levelNb: data.levels.length};
updateLevelInfo();
});
hls.on(Hls.Events.LEVEL_LOADED,function(event,data) {
Expand Down Expand Up @@ -322,12 +323,94 @@ <h4> Stats Display </h4>
}
refreshCanvas();
updateLevelInfo();

var latency = data.stats.tfirst - data.stats.trequest, process = data.stats.tbuffered - data.stats.trequest, bitrate = Math.round(8 * data.stats.length / (data.stats.tbuffered - data.stats.tfirst));
if (stats.fragBuffered) {
stats.fragMinLatency = Math.min(stats.fragMinLatency, latency);
stats.fragMaxLatency = Math.max(stats.fragMaxLatency, latency);
stats.fragMinProcess = Math.min(stats.fragMinProcess, process);
stats.fragMaxProcess = Math.max(stats.fragMaxProcess, process);
stats.fragMinKbps = Math.min(stats.fragMinKbps, bitrate);
stats.fragMaxKbps = Math.max(stats.fragMaxKbps, bitrate);
stats.autoLevelCappingMin = Math.min(stats.autoLevelCappingMin, hls.autoLevelCapping);
stats.autoLevelCappingMax = Math.max(stats.autoLevelCappingMax, hls.autoLevelCapping);
stats.fragBuffered++;
} else {
stats.fragMinLatency = stats.fragMaxLatency = latency;
stats.fragMinProcess = stats.fragMaxProcess = process;
stats.fragMinKbps = stats.fragMaxKbps = bitrate;
stats.fragBuffered = 1;
stats.fragBufferedBytes = 0;
stats.autoLevelCappingMin = stats.autoLevelCappingMax = hls.autoLevelCapping;
this.sumLatency = 0;
this.sumKbps = 0;
this.sumProcess = 0;
}
stats.fraglastLatency = latency;
this.sumLatency += latency;
stats.fragAvgLatency = Math.round(this.sumLatency / stats.fragBuffered);
stats.fragLastProcess = process;
this.sumProcess += process;
stats.fragAvgProcess = Math.round(this.sumProcess / stats.fragBuffered);
stats.fragLastKbps = bitrate;
this.sumKbps += bitrate;
stats.fragAvgKbps = Math.round(this.sumKbps / stats.fragBuffered);
stats.fragBufferedBytes += data.stats.length;
stats.autoLevelCappingLast = hls.autoLevelCapping;
});
hls.on(Hls.Events.FRAG_CHANGED,function(event,data) {
var event = {time : Date.now() - events.t0, type : 'frag changed', name : data.frag.sn + ' @ ' + data.frag.level };
events.video.push(event);
refreshCanvas();
updateLevelInfo();

var level = data.frag.level, autoLevel = data.frag.autoLevel;
if (stats.levelStart === undefined) {
stats.levelStart = level;
}
if (autoLevel) {
if (stats.fragChangedAuto) {
stats.autoLevelMin = Math.min(stats.autoLevelMin, level);
stats.autoLevelMax = Math.max(stats.autoLevelMax, level);
stats.fragChangedAuto++;
if (this.levelLastAuto && level !== stats.autoLevelLast) {
stats.autoLevelSwitch++;
}
} else {
stats.autoLevelMin = stats.autoLevelMax = level;
stats.autoLevelSwitch = 0;
stats.fragChangedAuto = 1;
this.sumAutoLevel = 0;
}
this.sumAutoLevel += level;
stats.autoLevelAvg = Math.round(1000 * this.sumAutoLevel / stats.fragChangedAuto) / 1000;
stats.autoLevelLast = level;
} else {
if (stats.fragChangedManual) {
stats.manualLevelMin = Math.min(stats.manualLevelMin, level);
stats.manualLevelMax = Math.max(stats.manualLevelMax, level);
stats.fragChangedManual++;
if (!this.levelLastAuto && level !== stats.manualLevelLast) {
stats.manualLevelSwitch++;
}
} else {
stats.manualLevelMin = stats.manualLevelMax = level;
stats.manualLevelSwitch = 0;
stats.fragChangedManual = 1;
}
stats.manualLevelLast = level;
}
this.levelLastAuto = autoLevel;
});

hls.on(Hls.Events.FRAG_LOAD_EMERGENCY_ABORTED,function(event,data) {
if (stats) {
if (stats.fragLoadEmergencyAborted === undefined) {
stats.fragLoadEmergencyAborted = 1;
} else {
stats.fragLoadEmergencyAborted++;
}
}
});

hls.on(Hls.Events.ERROR, function(event,data) {
Expand Down Expand Up @@ -383,11 +466,35 @@ <h4> Stats Display </h4>
break;
}
}
if(!stats) stats = {};
// track all errors independently
if (stats[data.details] === undefined) {
stats[data.details] = 1;
} else {
stats[data.details] += 1;
}
// track fatal error
if (data.fatal) {
if (stats.fatalError === undefined) {
stats.fatalError = 1;
} else {
stats.fatalError += 1;
}
}
$("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t"));
});

hls.on(Hls.Events.FPS_DROP,function(event,data) {
var evt = {time : Date.now() - events.t0, type : "frame drop", name : data.currentDropped + "/" + data.currentDecoded};
events.video.push(evt);
if (stats) {
if (stats.fpsDropEvent === undefined) {
stats.fpsDropEvent = 1;
} else {
stats.fpsDropEvent++;
}
stats.fpsTotalDroppedFrames = data.totalDroppedFrames;
}
});
video.addEventListener('resize', handleVideoEvent);
video.addEventListener('seeking', handleVideoEvent);
Expand Down Expand Up @@ -581,14 +688,28 @@ <h4> Stats Display </h4>
+ "Dropped Frames:"
+ droppedFrames + "<br>";
$("#buffered_log").html(log);
$("#HlsStats").text(JSON.stringify(hls.stats,null,"\t"));
$("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t"));
ctx.fillStyle = "blue";
var x = v.currentTime / v.duration * canvas.width;
ctx.fillRect(x, 0, 2, 15);
}

}

function sortObject(obj) {
if(typeof obj !== 'object')
return obj
var temp = {};
var keys = [];
for(var key in obj)
keys.push(key);
keys.sort();
for(var index in keys)
temp[keys[index]] = sortObject(obj[keys[index]]);
return temp;
}


function showCanvas() {
showMetrics();
$("#buffered_log").show();
Expand Down
3 changes: 0 additions & 3 deletions design.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ design idea is pretty simple :
- definition of Hls.Events
- [src/errors.js][]
- definition of Hls.ErrorTypes and Hls.ErrorDetails
- [src/stats.js][]
- subsystem monitoring events, and aggregating them into an object, that could be retrieved through hls.stats getter

- [src/controller/buffer-controller.js][]
- in charge of:
- ensuring that buffer is filled as per defined quality selection logic.
Expand Down

0 comments on commit db6f5fe

Please sign in to comment.