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.
159 lines
4.6 KiB
159 lines
4.6 KiB
// render the timeline with d3
|
|
let timeline = document.querySelector('.timeline');
|
|
timeline.innerHTML = '';
|
|
|
|
let margin = {
|
|
top: 20,
|
|
right: 80,
|
|
bottom: 30,
|
|
left: 50
|
|
};
|
|
let width = 960 - margin.left - margin.right;
|
|
let height = 500 - margin.top - margin.bottom;
|
|
let svg = d3.select('.timeline').append('svg')
|
|
.attr('width', width + margin.left + margin.right)
|
|
.attr('height', height + margin.top + margin.bottom)
|
|
.append('g')
|
|
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
|
|
|
|
|
|
const displayTimeline = function(error, data) {
|
|
var x = d3.scale.linear().range([0, width]),
|
|
y = d3.scale.linear().range([height, 0]),
|
|
|
|
timeAxis = d3.svg.axis().scale(x).orient('bottom'),
|
|
tickFormatter = d3.format(',.0f'),
|
|
bitrateAxis = d3.svg.axis()
|
|
.scale(y)
|
|
.tickFormat(function(value) {
|
|
return tickFormatter(value / 1024);
|
|
})
|
|
.orient('left'),
|
|
|
|
bandwidthLine = d3.svg.line()
|
|
.interpolate('basis')
|
|
.x(function(data) {
|
|
return x(data.time);
|
|
})
|
|
.y(function(data) {
|
|
return y(data.bandwidth);
|
|
}),
|
|
effectiveBandwidthLine = d3.svg.line()
|
|
.interpolate('basis')
|
|
.x(function(data) {
|
|
return x(data.time);
|
|
})
|
|
.y(function(data) {
|
|
return y(data.bandwidth);
|
|
});
|
|
|
|
x.domain(d3.extent(data.bandwidth, function(data) {
|
|
return data.time;
|
|
}));
|
|
y.domain([0, Math.max(d3.max(data.bandwidth, function(data) {
|
|
return data.bandwidth;
|
|
}), d3.max(data.options.playlists), d3.max(data.playlists, function(data) {
|
|
return data.bitrate;
|
|
}))]);
|
|
|
|
// time axis
|
|
svg.selectAll('.axis').remove();
|
|
svg.append('g')
|
|
.attr('class', 'x axis')
|
|
.attr('transform', 'translate(0,' + height + ')')
|
|
.call(timeAxis);
|
|
|
|
// bitrate axis
|
|
svg.append('g')
|
|
.attr('class', 'y axis')
|
|
.call(bitrateAxis)
|
|
.append('text')
|
|
.attr('transform', 'rotate(-90)')
|
|
.attr('y', 6)
|
|
.attr('dy', '.71em')
|
|
.style('text-anchor', 'end')
|
|
.text('Bitrate (kb/s)');
|
|
|
|
// playlist bitrate lines
|
|
svg.selectAll('.line.bitrate').remove();
|
|
svg.selectAll('.line.bitrate')
|
|
.data(data.options.playlists)
|
|
.enter().append('path')
|
|
.attr('class', 'line bitrate')
|
|
.attr('d', function(playlist) {
|
|
return 'M0,' + y(playlist) + 'L' + width + ',' + y(playlist);
|
|
});
|
|
|
|
// bandwidth line
|
|
svg.selectAll('.bandwidth').remove();
|
|
svg.append('path')
|
|
.datum(data.bandwidth)
|
|
.attr('class', 'line bandwidth')
|
|
.attr('d', bandwidthLine);
|
|
svg.selectAll('.effective-bandwidth').remove();
|
|
svg.append('path')
|
|
.datum(data.effectiveBandwidth)
|
|
.attr('class', 'line effective-bandwidth')
|
|
.attr('d', effectiveBandwidthLine);
|
|
|
|
svg.append('text')
|
|
.attr('class', 'bandwidth label')
|
|
.attr('transform', 'translate(' + x(x.range()[1]) + ', ' + y(data.bandwidth.slice(-1)[0].bandwidth) + ')')
|
|
.attr('dy', '1.35em')
|
|
.text('bandwidth');
|
|
svg.append('text')
|
|
.attr('class', 'bandwidth label')
|
|
.attr('transform', 'translate(' + x(x.range()[1]) + ', ' + y(data.effectiveBandwidth.slice(-1)[0].bandwidth) + ')')
|
|
.attr('dy', '1.35em')
|
|
.text('measured');
|
|
|
|
// segment bitrate dots
|
|
svg.selectAll('.segment-bitrate').remove();
|
|
svg.selectAll('.segment-bitrate')
|
|
.data(data.playlists)
|
|
.enter().append('circle')
|
|
.attr('class', 'dot segment-bitrate')
|
|
.attr('r', 3.5)
|
|
.attr('cx', function(playlist) {
|
|
return x(playlist.time);
|
|
})
|
|
.attr('cy', function(playlist) {
|
|
return y(playlist.bitrate);
|
|
});
|
|
|
|
// highlight intervals when the buffer is empty
|
|
svg.selectAll('.buffer-empty').remove();
|
|
svg.selectAll('.buffer-empty')
|
|
.data(data.buffered.reduce(function(result, sample) {
|
|
var last = result[result.length - 1];
|
|
if (sample.buffered === 0) {
|
|
if (last && sample.time === last.end + 1) {
|
|
// add this sample to the interval we're accumulating
|
|
return result.slice(0, result.length - 1).concat({
|
|
start: last.start,
|
|
end: sample.time
|
|
});
|
|
} else {
|
|
// this sample starts a new interval
|
|
return result.concat({
|
|
start: sample.time,
|
|
end: sample.time
|
|
});
|
|
}
|
|
}
|
|
// filter out time periods where the buffer isn't empty
|
|
return result;
|
|
}, []))
|
|
.enter().append('rect')
|
|
.attr('class', 'buffer-empty')
|
|
.attr('x', function(data) {
|
|
return x(data.start);
|
|
})
|
|
.attr('width', function(data) {
|
|
return x(1 + data.end - data.start);
|
|
})
|
|
.attr('y', 0)
|
|
.attr('height', y(height));
|
|
};
|
|
|
|
export default displayTimeline;
|