f914eaeb by David LaPalomento

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.
1 parent 06825eef
...@@ -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) {
......
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() {
......