Manage seekable ranges for live streams
Use the experimental implementation of MediaSource.addSeekableRange_() to allow HLS to specify greater seekable ranges then the current buffered region. Requires https://github.com/videojs/videojs-contrib-media-sources/pull/55.
Showing
2 changed files
with
31 additions
and
21 deletions
... | @@ -493,25 +493,26 @@ videojs.HlsHandler.prototype.updateDuration = function(playlist) { | ... | @@ -493,25 +493,26 @@ videojs.HlsHandler.prototype.updateDuration = function(playlist) { |
493 | setDuration = function() { | 493 | setDuration = function() { |
494 | this.mediaSource.duration = newDuration; | 494 | this.mediaSource.duration = newDuration; |
495 | this.tech_.trigger('durationchange'); | 495 | this.tech_.trigger('durationchange'); |
496 | |||
497 | // update seekable | ||
498 | if (seekable.length !== 0 && newDuration === Infinity) { | ||
499 | this.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0)); | ||
500 | } | ||
496 | this.mediaSource.removeEventListener('sourceopen', setDuration); | 501 | this.mediaSource.removeEventListener('sourceopen', setDuration); |
497 | }.bind(this), | 502 | }.bind(this), |
498 | seekable = this.seekable(); | 503 | seekable = this.seekable(); |
499 | 504 | ||
500 | // TODO: Move to videojs-contrib-media-sources | ||
501 | if (seekable.length && newDuration === Infinity) { | ||
502 | if (isNaN(oldDuration)) { | ||
503 | oldDuration = 0; | ||
504 | } | ||
505 | newDuration = Math.max(oldDuration, | ||
506 | seekable.end(0) + playlist.targetDuration * 3); | ||
507 | } | ||
508 | |||
509 | // if the duration has changed, invalidate the cached value | 505 | // if the duration has changed, invalidate the cached value |
510 | if (oldDuration !== newDuration) { | 506 | if (oldDuration !== newDuration) { |
507 | // update the duration | ||
511 | if (this.mediaSource.readyState !== 'open') { | 508 | if (this.mediaSource.readyState !== 'open') { |
512 | this.mediaSource.addEventListener('sourceopen', setDuration); | 509 | this.mediaSource.addEventListener('sourceopen', setDuration); |
513 | } else if (!this.sourceBuffer || !this.sourceBuffer.updating) { | 510 | } else if (!this.sourceBuffer || !this.sourceBuffer.updating) { |
514 | this.mediaSource.duration = newDuration; | 511 | this.mediaSource.duration = newDuration; |
512 | // update seekable | ||
513 | if (seekable.length !== 0 && newDuration === Infinity) { | ||
514 | this.mediaSource.addSeekableRange_(seekable.start(0), seekable.end(0)); | ||
515 | } | ||
515 | this.tech_.trigger('durationchange'); | 516 | this.tech_.trigger('durationchange'); |
516 | } | 517 | } |
517 | } | 518 | } | ... | ... |
... | @@ -134,11 +134,6 @@ var | ... | @@ -134,11 +134,6 @@ var |
134 | type: 'sourceopen', | 134 | type: 'sourceopen', |
135 | swfId: player.tech_.el().id | 135 | swfId: player.tech_.el().id |
136 | }); | 136 | }); |
137 | |||
138 | // endOfStream triggers an exception if flash isn't available | ||
139 | player.tech_.hls.mediaSource.endOfStream = function(error) { | ||
140 | this.error_ = error; | ||
141 | }; | ||
142 | }, | 137 | }, |
143 | standardXHRResponse = function(request) { | 138 | standardXHRResponse = function(request) { |
144 | if (!request.url) { | 139 | if (!request.url) { |
... | @@ -170,6 +165,11 @@ var | ... | @@ -170,6 +165,11 @@ var |
170 | // a no-op MediaSource implementation to allow synchronous testing | 165 | // a no-op MediaSource implementation to allow synchronous testing |
171 | MockMediaSource = videojs.extend(videojs.EventTarget, { | 166 | MockMediaSource = videojs.extend(videojs.EventTarget, { |
172 | constructor: function() {}, | 167 | constructor: function() {}, |
168 | duration: NaN, | ||
169 | seekable: videojs.createTimeRange(), | ||
170 | addSeekableRange_: function(start, end) { | ||
171 | this.seekable = videojs.createTimeRange(start, end); | ||
172 | }, | ||
173 | addSourceBuffer: function() { | 173 | addSourceBuffer: function() { |
174 | return new (videojs.extend(videojs.EventTarget, { | 174 | return new (videojs.extend(videojs.EventTarget, { |
175 | constructor: function() {}, | 175 | constructor: function() {}, |
... | @@ -179,7 +179,10 @@ var | ... | @@ -179,7 +179,10 @@ var |
179 | remove: function() {} | 179 | remove: function() {} |
180 | }))(); | 180 | }))(); |
181 | }, | 181 | }, |
182 | endOfStream: function() {} | 182 | // endOfStream triggers an exception if flash isn't available |
183 | endOfStream: function(error) { | ||
184 | this.error_ = error; | ||
185 | } | ||
183 | }), | 186 | }), |
184 | 187 | ||
185 | // do a shallow copy of the properties of source onto the target object | 188 | // do a shallow copy of the properties of source onto the target object |
... | @@ -1385,7 +1388,7 @@ test('seeking in an empty playlist is a non-erroring noop', function() { | ... | @@ -1385,7 +1388,7 @@ test('seeking in an empty playlist is a non-erroring noop', function() { |
1385 | equal(requests.length, requestsLength, 'made no additional requests'); | 1388 | equal(requests.length, requestsLength, 'made no additional requests'); |
1386 | }); | 1389 | }); |
1387 | 1390 | ||
1388 | test('tech\'s duration reports Infinity for live playlists', function() { | 1391 | test('sets seekable and duration for live playlists', function() { |
1389 | player.src({ | 1392 | player.src({ |
1390 | src: 'http://example.com/manifest/missingEndlist.m3u8', | 1393 | src: 'http://example.com/manifest/missingEndlist.m3u8', |
1391 | type: 'application/vnd.apple.mpegurl' | 1394 | type: 'application/vnd.apple.mpegurl' |
... | @@ -1394,13 +1397,19 @@ test('tech\'s duration reports Infinity for live playlists', function() { | ... | @@ -1394,13 +1397,19 @@ test('tech\'s duration reports Infinity for live playlists', function() { |
1394 | 1397 | ||
1395 | standardXHRResponse(requests[0]); | 1398 | standardXHRResponse(requests[0]); |
1396 | 1399 | ||
1397 | strictEqual(player.tech_.duration(), | 1400 | equal(player.tech_.hls.mediaSource.seekable.length, |
1398 | Infinity, | 1401 | 1, |
1399 | 'duration on the tech is infinity'); | 1402 | 'set one seekable range'); |
1403 | equal(player.tech_.hls.mediaSource.seekable.start(0), | ||
1404 | player.tech_.hls.seekable().start(0), | ||
1405 | 'set seekable start'); | ||
1406 | equal(player.tech_.hls.mediaSource.seekable.end(0), | ||
1407 | player.tech_.hls.seekable().end(0), | ||
1408 | 'set seekable end'); | ||
1400 | 1409 | ||
1401 | notEqual(player.tech_.hls.mediaSource.duration, | 1410 | strictEqual(player.tech_.hls.mediaSource.duration, |
1402 | Infinity, | 1411 | Infinity, |
1403 | 'duration on the mediaSource is not infinity'); | 1412 | 'duration on the mediaSource is infinity'); |
1404 | }); | 1413 | }); |
1405 | 1414 | ||
1406 | test('live playlist starts three target durations before live', function() { | 1415 | test('live playlist starts three target durations before live', function() { | ... | ... |
-
Please register or sign in to post a comment