7080865c by David LaPalomento

Merge pull request #515 from 'tenacex-feature/byterange-support-dev' into development

Add support for EXT-X-BYTERANGE
2 parents 2a8faddc 16f9203f
...@@ -894,7 +894,8 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) { ...@@ -894,7 +894,8 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) {
894 // we have entered a state where we are fetching the same segment, 894 // we have entered a state where we are fetching the same segment,
895 // try to walk forward 895 // try to walk forward
896 if (this.lastSegmentLoaded_ && 896 if (this.lastSegmentLoaded_ &&
897 this.lastSegmentLoaded_ === this.playlistUriToUrl(segment.uri)) { 897 this.playlistUriToUrl(this.lastSegmentLoaded_.uri) === this.playlistUriToUrl(segment.uri) &&
898 this.lastSegmentLoaded_.byterange === segment.byterange) {
898 return this.fillBuffer(mediaIndex + 1); 899 return this.fillBuffer(mediaIndex + 1);
899 } 900 }
900 901
...@@ -961,6 +962,29 @@ videojs.HlsHandler.prototype.playlistUriToUrl = function(segmentRelativeUrl) { ...@@ -961,6 +962,29 @@ videojs.HlsHandler.prototype.playlistUriToUrl = function(segmentRelativeUrl) {
961 return playListUrl; 962 return playListUrl;
962 }; 963 };
963 964
965 /* Turns segment byterange into a string suitable for use in
966 * HTTP Range requests
967 */
968 videojs.HlsHandler.prototype.byterangeStr_ = function(byterange) {
969 var byterangeStart, byterangeEnd;
970
971 // `byterangeEnd` is one less than `offset + length` because the HTTP range
972 // header uses inclusive ranges
973 byterangeEnd = byterange.offset + byterange.length - 1;
974 byterangeStart = byterange.offset;
975 return "bytes=" + byterangeStart + "-" + byterangeEnd;
976 };
977
978 /* Defines headers for use in the xhr request for a particular segment.
979 */
980 videojs.HlsHandler.prototype.segmentXhrHeaders_ = function(segment) {
981 var headers = {};
982 if ('byterange' in segment) {
983 headers['Range'] = this.byterangeStr_(segment.byterange);
984 }
985 return headers;
986 };
987
964 /* 988 /*
965 * Sets `bandwidth`, `segmentXhrTime`, and appends to the `bytesReceived. 989 * Sets `bandwidth`, `segmentXhrTime`, and appends to the `bytesReceived.
966 * Expects an object with: 990 * Expects an object with:
...@@ -1054,7 +1078,8 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { ...@@ -1054,7 +1078,8 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
1054 // Set xhr timeout to 150% of the segment duration to allow us 1078 // Set xhr timeout to 150% of the segment duration to allow us
1055 // some time to switch renditions in the event of a catastrophic 1079 // some time to switch renditions in the event of a catastrophic
1056 // decrease in network performance or a server issue. 1080 // decrease in network performance or a server issue.
1057 timeout: (segment.duration * 1.5) * 1000 1081 timeout: (segment.duration * 1.5) * 1000,
1082 headers: this.segmentXhrHeaders_(segment)
1058 }, function(error, request) { 1083 }, function(error, request) {
1059 // This is a timeout of a previously aborted segment request 1084 // This is a timeout of a previously aborted segment request
1060 // so simply ignore it 1085 // so simply ignore it
...@@ -1085,7 +1110,7 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { ...@@ -1085,7 +1110,7 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) {
1085 return; 1110 return;
1086 } 1111 }
1087 1112
1088 self.lastSegmentLoaded_ = segmentInfo.uri; 1113 self.lastSegmentLoaded_ = segment;
1089 self.setBandwidth(request); 1114 self.setBandwidth(request);
1090 1115
1091 if (segment.key) { 1116 if (segment.key) {
......
...@@ -28,9 +28,13 @@ ...@@ -28,9 +28,13 @@
28 request.timedout = false; 28 request.timedout = false;
29 } 29 }
30 30
31 // videojs.xhr no longer consider status codes outside of 200 and 0 (for file uris) to be 31 // videojs.xhr no longer considers status codes outside of 200 and 0
32 // errors but the old XHR did so emulate that behavior 32 // (for file uris) to be errors, but the old XHR did, so emulate that
33 if (!error && response.statusCode !== 200 && response.statusCode !== 0) { 33 // behavior. Status 206 may be used in response to byterange requests.
34 if (!error &&
35 response.statusCode !== 200 &&
36 response.statusCode !== 206 &&
37 response.statusCode !== 0) {
34 error = new Error('XHR Failed with a response of: ' + 38 error = new Error('XHR Failed with a response of: ' +
35 (request && (request.response || request.responseText))); 39 (request && (request.response || request.responseText)));
36 } 40 }
......
...@@ -324,6 +324,56 @@ test('starts playing if autoplay is specified', function() { ...@@ -324,6 +324,56 @@ test('starts playing if autoplay is specified', function() {
324 strictEqual(1, plays, 'play was called'); 324 strictEqual(1, plays, 'play was called');
325 }); 325 });
326 326
327 test('XHR requests first byte range on play', function() {
328 player.src({
329 src: 'manifest/playlist.m3u8',
330 type: 'application/vnd.apple.mpegurl'
331 });
332 player.tech_.triggerReady();
333 clock.tick(1);
334 player.tech_.trigger('play');
335 openMediaSource(player);
336 standardXHRResponse(requests[0]);
337 equal(requests[1].headers.Range, "bytes=0-522827");
338 });
339
340 test('Seeking requests correct byte range', function() {
341 player.src({
342 src: 'manifest/playlist.m3u8',
343 type: 'application/vnd.apple.mpegurl'
344 });
345 player.tech_.triggerReady();
346 clock.tick(1);
347 player.tech_.trigger('play');
348 openMediaSource(player);
349 standardXHRResponse(requests[0]);
350 player.tech_.hls.sourceBuffer.trigger('updateend');
351 clock.tick(1);
352 player.currentTime(40);
353 clock.tick(1);
354 equal(requests[2].headers.Range, "bytes=2299992-2835603");
355 });
356
357 test('if buffered, will request second segment byte range', function() {
358 player.src({
359 src: 'manifest/playlist.m3u8',
360 type: 'application/vnd.apple.mpegurl'
361 });
362 player.tech_.triggerReady();
363 clock.tick(1);
364 player.tech_.trigger('play');
365 openMediaSource(player);
366 player.tech_.buffered = function() {
367 return videojs.createTimeRange(0, 20);
368 };
369 standardXHRResponse(requests[0]);
370 standardXHRResponse(requests[1]);
371 player.tech_.hls.sourceBuffer.trigger('updateend');
372 player.tech_.hls.checkBuffer_();
373 clock.tick(100);
374 equal(requests[2].headers.Range, "bytes=1823412-2299991");
375 });
376
327 test('autoplay seeks to the live point after playlist load', function() { 377 test('autoplay seeks to the live point after playlist load', function() {
328 var currentTime = 0; 378 var currentTime = 0;
329 player.autoplay(true); 379 player.autoplay(true);
......