Skip to content

Commit

Permalink
remove hls.stats getter
Browse files Browse the repository at this point in the history
stats are project specific and can all be computed using Hls.Events
move previous hls.stats into demo page (as an example)
  • Loading branch information
mangui committed Nov 3, 2015
1 parent 5e09af1 commit 94228b5
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 234 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
123 changes: 121 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,33 @@ <h4> Stats Display </h4>
break;
}
}
// 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;
}
}
});

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 +686,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
10 changes: 0 additions & 10 deletions src/hls.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

import Event from './events';
import {ErrorTypes, ErrorDetails} from './errors';
import StatsHandler from './stats';
import PlaylistLoader from './loader/playlist-loader';
import FragmentLoader from './loader/fragment-loader';
import AbrController from './controller/abr-controller';
Expand Down Expand Up @@ -86,7 +85,6 @@ class Hls {
this.abrController = new config.abrController(this);
this.bufferController = new BufferController(this);
//this.fpsController = new FPSController(this);
this.statsHandler = new StatsHandler(this);
}

destroy() {
Expand All @@ -97,7 +95,6 @@ class Hls {
this.levelController.destroy();
this.bufferController.destroy();
//this.fpsController.destroy();
this.statsHandler.destroy();
this.url = null;
this.detachVideo();
this.observer.removeAllListeners();
Expand All @@ -106,7 +103,6 @@ class Hls {
attachVideo(video) {
logger.log('attachVideo');
this.video = video;
this.statsHandler.attachVideo(video);
// setup the media source
var ms = this.mediaSource = new MediaSource();
//Media Source listeners
Expand All @@ -126,7 +122,6 @@ class Hls {
var video = this.video;
logger.log('trigger MSE_DETACHING');
this.trigger(Event.MSE_DETACHING);
this.statsHandler.detachVideo(video);
var ms = this.mediaSource;
if (ms) {
if (ms.readyState === 'open') {
Expand Down Expand Up @@ -267,11 +262,6 @@ class Hls {
return this.levelController.manualLevel;
}

/* return playback session stats */
get stats() {
return this.statsHandler.stats;
}

onMediaSourceOpen() {
logger.log('media source opened');
this.trigger(Event.MSE_ATTACHED, {video: this.video, mediaSource: this.mediaSource});
Expand Down
Loading

0 comments on commit 94228b5

Please sign in to comment.