Abort outstanding segment requests when seeking
If segment requests are not aborted, fillBuffer will detect an outstanding XHR and fail to download the target segment. This would force the user to click twice on the progress bar to get seek to "take" if their was a segment request in progress.
Showing
3 changed files
with
77 additions
and
12 deletions
... | @@ -168,9 +168,12 @@ var | ... | @@ -168,9 +168,12 @@ var |
168 | }; | 168 | }; |
169 | 169 | ||
170 | player.on('seeking', function() { | 170 | player.on('seeking', function() { |
171 | var seekValue = player.el().querySelector('.vjs-tech').vjs_getProperty('lastSeekedTime'); | 171 | var currentTime = player.currentTime(); |
172 | player.hls.mediaIndex = getMediaIndexByTime(player.hls.media, seekValue); | 172 | player.hls.mediaIndex = getMediaIndexByTime(player.hls.media, currentTime); |
173 | fillBuffer(seekValue * 1000); | 173 | if (segmentXhr) { |
174 | segmentXhr.abort(); | ||
175 | } | ||
176 | fillBuffer(currentTime * 1000); | ||
174 | }); | 177 | }); |
175 | 178 | ||
176 | /** | 179 | /** |
... | @@ -302,8 +305,10 @@ var | ... | @@ -302,8 +305,10 @@ var |
302 | /** | 305 | /** |
303 | * Determines whether there is enough video data currently in the buffer | 306 | * Determines whether there is enough video data currently in the buffer |
304 | * and downloads a new segment if the buffered time is less than the goal. | 307 | * and downloads a new segment if the buffered time is less than the goal. |
308 | * @param offset (optional) {number} the offset into the downloaded segment | ||
309 | * to seek to, in milliseconds | ||
305 | */ | 310 | */ |
306 | fillBuffer = function(millisecond) { | 311 | fillBuffer = function(offset) { |
307 | var | 312 | var |
308 | buffered = player.buffered(), | 313 | buffered = player.buffered(), |
309 | bufferedTime = 0, | 314 | bufferedTime = 0, |
... | @@ -342,20 +347,27 @@ var | ... | @@ -342,20 +347,27 @@ var |
342 | segmentXhr.onreadystatechange = function() { | 347 | segmentXhr.onreadystatechange = function() { |
343 | var playlist; | 348 | var playlist; |
344 | 349 | ||
345 | if (segmentXhr.readyState === 4) { | 350 | if (this.readyState === 4) { |
351 | // the segment request is no longer outstanding | ||
352 | segmentXhr = null; | ||
353 | |||
354 | // stop processing if the request was aborted | ||
355 | if (!this.response) { | ||
356 | return; | ||
357 | } | ||
358 | |||
346 | // calculate the download bandwidth | 359 | // calculate the download bandwidth |
347 | player.hls.segmentXhrTime = (+new Date()) - startTime; | 360 | player.hls.segmentXhrTime = (+new Date()) - startTime; |
348 | player.hls.bandwidth = (segmentXhr.response.byteLength / player.hls.segmentXhrTime) * 8 * 1000; | 361 | player.hls.bandwidth = (this.response.byteLength / player.hls.segmentXhrTime) * 8 * 1000; |
349 | 362 | ||
350 | // transmux the segment data from MP2T to FLV | 363 | // transmux the segment data from MP2T to FLV |
351 | segmentParser.parseSegmentBinaryData(new Uint8Array(segmentXhr.response)); | 364 | segmentParser.parseSegmentBinaryData(new Uint8Array(this.response)); |
352 | 365 | ||
353 | // handle intra-segment seeking, if requested // | 366 | // handle intra-segment seeking, if requested // |
354 | // do not iterate over 0 index because it comes back with the end time // | 367 | if (offset !== undefined && typeof offset === "number") { |
355 | if (millisecond !== undefined && typeof(millisecond) === "number") { | 368 | player.el().querySelector('.vjs-tech').vjs_setProperty('lastSeekedTime', getPtsByTime(segmentParser,offset)/1000); |
356 | player.el().querySelector('.vjs-tech').vjs_setProperty('lastSeekedTime', getPtsByTime(segmentParser,millisecond)/1000); | ||
357 | for (tagIndex = 0; tagIndex < segmentParser.getTags().length; tagIndex++) { | 369 | for (tagIndex = 0; tagIndex < segmentParser.getTags().length; tagIndex++) { |
358 | if (segmentParser.getTags()[tagIndex].pts > millisecond) { | 370 | if (segmentParser.getTags()[tagIndex].pts > offset) { |
359 | break; | 371 | break; |
360 | } | 372 | } |
361 | // we're seeking past this tag, so ignore it | 373 | // we're seeking past this tag, so ignore it |
... | @@ -367,7 +379,6 @@ var | ... | @@ -367,7 +379,6 @@ var |
367 | player.hls.sourceBuffer.appendBuffer(segmentParser.getNextTag().bytes, player); | 379 | player.hls.sourceBuffer.appendBuffer(segmentParser.getNextTag().bytes, player); |
368 | } | 380 | } |
369 | 381 | ||
370 | segmentXhr = null; | ||
371 | player.hls.mediaIndex++; | 382 | player.hls.mediaIndex++; |
372 | 383 | ||
373 | if (player.hls.mediaIndex === player.hls.media.segments.length) { | 384 | if (player.hls.mediaIndex === player.hls.media.segments.length) { | ... | ... |
test/manifest/remove-trs.js
0 → 100644
1 | var grunt = require('grunt'), | ||
2 | extname = require('path').extname; | ||
3 | |||
4 | grunt.file.recurse(process.cwd(), function(path) { | ||
5 | var json; | ||
6 | if (extname(path) === '.json') { | ||
7 | json = grunt.file.readJSON(path); | ||
8 | if (json.totalDuration) { | ||
9 | delete json.totalDuration; | ||
10 | grunt.file.write(path, JSON.stringify(json, null, ' ')); | ||
11 | } | ||
12 | } | ||
13 | }); |
... | @@ -473,6 +473,47 @@ test('ignores currentSrc if it doesn\'t have the "m3u8" extension', function() { | ... | @@ -473,6 +473,47 @@ test('ignores currentSrc if it doesn\'t have the "m3u8" extension', function() { |
473 | strictEqual(xhrUrls.length, 0, 'no request is made'); | 473 | strictEqual(xhrUrls.length, 0, 'no request is made'); |
474 | }); | 474 | }); |
475 | 475 | ||
476 | test('cancels outstanding XHRs when seeking', function() { | ||
477 | var | ||
478 | aborted = false, | ||
479 | opened = 0; | ||
480 | player.hls('manifest/media.m3u8'); | ||
481 | videojs.mediaSources[player.currentSrc()].trigger({ | ||
482 | type: 'sourceopen' | ||
483 | }); | ||
484 | player.hls.media = { | ||
485 | segments: [{ | ||
486 | uri: '0.ts', | ||
487 | duration: 10 | ||
488 | }, { | ||
489 | uri: '1.ts', | ||
490 | duration: 10 | ||
491 | }] | ||
492 | }; | ||
493 | |||
494 | // XHR requests will never complete | ||
495 | window.XMLHttpRequest = function() { | ||
496 | this.open = function() { | ||
497 | opened++; | ||
498 | }; | ||
499 | this.send = function() {}; | ||
500 | this.abort = function() { | ||
501 | aborted = true; | ||
502 | this.readyState = 4; | ||
503 | this.status = 0; | ||
504 | this.onreadystatechange(); | ||
505 | }; | ||
506 | }; | ||
507 | // trigger a segment download request | ||
508 | player.trigger('timeupdate'); | ||
509 | opened = 0; | ||
510 | // attempt to seek while the download is in progress | ||
511 | player.trigger('seeking'); | ||
512 | |||
513 | ok(aborted, 'XHR aborted'); | ||
514 | strictEqual(1, opened, 'opened new XHR'); | ||
515 | }); | ||
516 | |||
476 | 517 | ||
477 | module('segment controller', { | 518 | module('segment controller', { |
478 | setup: function() { | 519 | setup: function() { | ... | ... |
-
Please register or sign in to post a comment