5a036701 by David LaPalomento

Merge pull request #89 from videojs/hotfix/bitrate-shifting

Hotfix/bitrate shifting
2 parents d8868f67 b65573ad
...@@ -250,8 +250,6 @@ module.exports = function(grunt) { ...@@ -250,8 +250,6 @@ module.exports = function(grunt) {
250 // Default task. 250 // Default task.
251 grunt.registerTask('default', 251 grunt.registerTask('default',
252 ['clean', 252 ['clean',
253 'jshint',
254 'manifests-to-js',
255 'test', 253 'test',
256 'concat', 254 'concat',
257 'uglify']); 255 'uglify']);
......
...@@ -150,12 +150,21 @@ ...@@ -150,12 +150,21 @@
150 playlist = loader.master.playlists[playlist]; 150 playlist = loader.master.playlists[playlist];
151 } 151 }
152 152
153 if (playlist.uri === media.uri) { 153 // switch to fully loaded playlists immediately
154 // switching to the currently active playlist is a no-op 154 if (loader.master.playlists[playlist.uri].endList) {
155 if (request) {
156 request.abort();
157 request = null;
158 }
159 loader.state = 'HAVE_METADATA';
160 media = playlist;
155 return; 161 return;
156 } 162 }
157 163
158 164 // switching to the active playlist is a no-op
165 if (playlist.uri === media.uri) {
166 return;
167 }
159 168
160 loader.state = 'SWITCHING_MEDIA'; 169 loader.state = 'SWITCHING_MEDIA';
161 170
......
...@@ -348,9 +348,78 @@ ...@@ -348,9 +348,78 @@
348 '#EXT-X-ENDLIST\n'); 348 '#EXT-X-ENDLIST\n');
349 loader.media('low.m3u8'); 349 loader.media('low.m3u8');
350 350
351 strictEqual(requests.length, 0, 'no requests is sent'); 351 strictEqual(requests.length, 0, 'no requests are sent');
352 }); 352 });
353 353
354 test('switching to the active live playlist is a no-op', function() {
355 var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
356 requests.pop().respond(200, null,
357 '#EXTM3U\n' +
358 '#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
359 'low.m3u8\n' +
360 '#EXT-X-STREAM-INF:BANDWIDTH=2\n' +
361 'high.m3u8\n');
362 requests.pop().respond(200, null,
363 '#EXTM3U\n' +
364 '#EXT-X-MEDIA-SEQUENCE:0\n' +
365 '#EXTINF:10,\n' +
366 'low-0.ts\n');
367 loader.media('low.m3u8');
368
369 strictEqual(requests.length, 0, 'no requests are sent');
370 });
371
372 test('switches back to loaded playlists without re-requesting them', function() {
373 var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
374 requests.pop().respond(200, null,
375 '#EXTM3U\n' +
376 '#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
377 'low.m3u8\n' +
378 '#EXT-X-STREAM-INF:BANDWIDTH=2\n' +
379 'high.m3u8\n');
380 requests.pop().respond(200, null,
381 '#EXTM3U\n' +
382 '#EXT-X-MEDIA-SEQUENCE:0\n' +
383 '#EXTINF:10,\n' +
384 'low-0.ts\n' +
385 '#EXT-X-ENDLIST\n');
386 loader.media('high.m3u8');
387 requests.pop().respond(200, null,
388 '#EXTM3U\n' +
389 '#EXT-X-MEDIA-SEQUENCE:0\n' +
390 '#EXTINF:10,\n' +
391 'high-0.ts\n' +
392 '#EXT-X-ENDLIST\n');
393 loader.media('low.m3u8');
394
395 strictEqual(requests.length, 0, 'no outstanding requests');
396 strictEqual(loader.state, 'HAVE_METADATA', 'returned to loaded playlist');
397 });
398
399 test('aborts outstanding requests if switching back to an already loaded playlist', function() {
400 var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
401 requests.pop().respond(200, null,
402 '#EXTM3U\n' +
403 '#EXT-X-STREAM-INF:BANDWIDTH=1\n' +
404 'low.m3u8\n' +
405 '#EXT-X-STREAM-INF:BANDWIDTH=2\n' +
406 'high.m3u8\n');
407 requests.pop().respond(200, null,
408 '#EXTM3U\n' +
409 '#EXT-X-MEDIA-SEQUENCE:0\n' +
410 '#EXTINF:10,\n' +
411 'low-0.ts\n' +
412 '#EXT-X-ENDLIST\n');
413 loader.media('high.m3u8');
414 loader.media('low.m3u8');
415
416 strictEqual(requests.length, 1, 'requested high playlist');
417 ok(requests[0].aborted, 'aborted playlist request');
418 strictEqual(loader.state, 'HAVE_METADATA', 'returned to loaded playlist');
419 strictEqual(loader.media(), loader.master.playlists[0], 'switched to loaded playlist');
420 });
421
422
354 test('does not abort requests when the same playlist is re-requested', function() { 423 test('does not abort requests when the same playlist is re-requested', function() {
355 var loader = new videojs.Hls.PlaylistLoader('master.m3u8'); 424 var loader = new videojs.Hls.PlaylistLoader('master.m3u8');
356 requests.pop().respond(200, null, 425 requests.pop().respond(200, null,
......
...@@ -423,9 +423,8 @@ form label { ...@@ -423,9 +423,8 @@ form label {
423 .line.bandwidth { 423 .line.bandwidth {
424 stroke: steelblue; 424 stroke: steelblue;
425 } 425 }
426 .line.buffered { 426 .line.effective-bandwidth {
427 stroke: #2ca02c; 427 stroke: #FF7F0E;
428 opacity: 0.25;
429 } 428 }
430 .line.bitrate { 429 .line.bitrate {
431 stroke: #999; 430 stroke: #999;
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 var segmentDuration = 9, // seconds 3 var segmentDuration = 9, // seconds
4 segmentCount = 100, 4 segmentCount = 100,
5 duration = segmentDuration * segmentCount, 5 duration = segmentDuration * segmentCount,
6 propagationDelay = 1, 6 propagationDelay = 0.5,
7 7
8 runSimulation, 8 runSimulation,
9 playlistResponse, 9 playlistResponse,
...@@ -86,6 +86,7 @@ ...@@ -86,6 +86,7 @@
86 runSimulation = function(options, done) { 86 runSimulation = function(options, done) {
87 var results = { 87 var results = {
88 bandwidth: [], 88 bandwidth: [],
89 effectiveBandwidth: [],
89 playlists: [], 90 playlists: [],
90 buffered: [], 91 buffered: [],
91 options: options 92 options: options
...@@ -207,6 +208,10 @@ ...@@ -207,6 +208,10 @@
207 request.status = 200; 208 request.status = 200;
208 request.response = new Uint8Array(segmentSize * 0.125); 209 request.response = new Uint8Array(segmentSize * 0.125);
209 request.setResponseBody(''); 210 request.setResponseBody('');
211 results.effectiveBandwidth.push({
212 time: Math.ceil(+new Date() * 0.001),
213 bandwidth: player.hls.bandwidth
214 });
210 }, ((remaining / value.bandwidth) + i) * 1000); 215 }, ((remaining / value.bandwidth) + i) * 1000);
211 return false; 216 return false;
212 } 217 }
...@@ -266,7 +271,6 @@ ...@@ -266,7 +271,6 @@
266 displayTimeline = function(error, data) { 271 displayTimeline = function(error, data) {
267 var x = d3.scale.linear().range([0, width]), 272 var x = d3.scale.linear().range([0, width]),
268 y = d3.scale.linear().range([height, 0]), 273 y = d3.scale.linear().range([height, 0]),
269 y0 = d3.scale.linear().range([height, 0]),
270 274
271 timeAxis = d3.svg.axis().scale(x).orient('bottom'), 275 timeAxis = d3.svg.axis().scale(x).orient('bottom'),
272 tickFormatter = d3.format(',.0f'), 276 tickFormatter = d3.format(',.0f'),
...@@ -285,13 +289,13 @@ ...@@ -285,13 +289,13 @@
285 .y(function(data) { 289 .y(function(data) {
286 return y(data.bandwidth); 290 return y(data.bandwidth);
287 }), 291 }),
288 bufferedLine = d3.svg.line() 292 effectiveBandwidthLine = d3.svg.line()
289 .interpolate('basis') 293 .interpolate('basis')
290 .x(function(data) { 294 .x(function(data) {
291 return x(data.time); 295 return x(data.time);
292 }) 296 })
293 .y(function(data) { 297 .y(function(data) {
294 return y0(data.buffered); 298 return y(data.bandwidth);
295 }); 299 });
296 300
297 x.domain(d3.extent(data.bandwidth, function(data) { 301 x.domain(d3.extent(data.bandwidth, function(data) {
...@@ -300,9 +304,6 @@ ...@@ -300,9 +304,6 @@
300 y.domain([0, Math.max(d3.max(data.bandwidth, function(data) { 304 y.domain([0, Math.max(d3.max(data.bandwidth, function(data) {
301 return data.bandwidth; 305 return data.bandwidth;
302 }), d3.max(data.options.playlists))]); 306 }), d3.max(data.options.playlists))]);
303 y0.domain([0, d3.max(data.buffered, function(data) {
304 return data.buffered;
305 })]);
306 307
307 // time axis 308 // time axis
308 svg.selectAll('.axis').remove(); 309 svg.selectAll('.axis').remove();
...@@ -337,20 +338,22 @@ ...@@ -337,20 +338,22 @@
337 .datum(data.bandwidth) 338 .datum(data.bandwidth)
338 .attr('class', 'line bandwidth') 339 .attr('class', 'line bandwidth')
339 .attr('d', bandwidthLine); 340 .attr('d', bandwidthLine);
341 svg.selectAll('.effective-bandwidth').remove();
342 svg.append('path')
343 .datum(data.effectiveBandwidth)
344 .attr('class', 'line effective-bandwidth')
345 .attr('d', effectiveBandwidthLine);
340 346
341 svg.append('text') 347 svg.append('text')
342 .attr('class', 'bandwidth label') 348 .attr('class', 'bandwidth label')
343 .attr('transform', 'translate(' + x(x.range()[1]) + ', ' + y(data.bandwidth.slice(-1)[0].bandwidth) + ')') 349 .attr('transform', 'translate(' + x(x.range()[1]) + ', ' + y(data.bandwidth.slice(-1)[0].bandwidth) + ')')
344 .attr('dy', '1.35em') 350 .attr('dy', '1.35em')
345 .text('bandwidth'); 351 .text('bandwidth');
346 352 svg.append('text')
347 // buffered line 353 .attr('class', 'bandwidth label')
348 svg.selectAll('.buffered').remove(); 354 .attr('transform', 'translate(' + x(x.range()[1]) + ', ' + y(data.effectiveBandwidth.slice(-1)[0].bandwidth) + ')')
349 svg.append('path') 355 .attr('dy', '1.35em')
350 .datum(data.buffered) 356 .text('measured');
351 .attr('class', 'line buffered')
352 .attr('y', 6)
353 .attr('d', bufferedLine);
354 357
355 // segment bitrate dots 358 // segment bitrate dots
356 svg.selectAll('.segment-bitrate').remove(); 359 svg.selectAll('.segment-bitrate').remove();
......