Merge pull request #515 from 'tenacex-feature/byterange-support-dev' into development
Add support for EXT-X-BYTERANGE
Showing
3 changed files
with
85 additions
and
6 deletions
... | @@ -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); | ... | ... |
-
Please register or sign in to post a comment