Merge pull request #89 from videojs/hotfix/bitrate-shifting
Hotfix/bitrate shifting
Showing
6 changed files
with
102 additions
and
24 deletions
... | @@ -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']); | ... | ... |
No preview for this file type
... | @@ -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(); | ... | ... |
-
Please register or sign in to post a comment