Merge pull request #476 from videojs/firefox-live
Fix the fixes for Firefox
Showing
4 changed files
with
61 additions
and
7 deletions
... | @@ -94,6 +94,10 @@ | ... | @@ -94,6 +94,10 @@ |
94 | 94 | ||
95 | PlaylistLoader.prototype.init.call(this); | 95 | PlaylistLoader.prototype.init.call(this); |
96 | 96 | ||
97 | // a flag that disables "expired time"-tracking this setting has | ||
98 | // no effect when not playing a live stream | ||
99 | this.trackExpiredTime_ = false; | ||
100 | |||
97 | if (!srcUrl) { | 101 | if (!srcUrl) { |
98 | throw new Error('A non-empty playlist URL is required'); | 102 | throw new Error('A non-empty playlist URL is required'); |
99 | } | 103 | } |
... | @@ -267,6 +271,12 @@ | ... | @@ -267,6 +271,12 @@ |
267 | loader.bandwidth = xhr.bandwidth; | 271 | loader.bandwidth = xhr.bandwidth; |
268 | }; | 272 | }; |
269 | 273 | ||
274 | // In a live list, don't keep track of the expired time until | ||
275 | // HLS tells us that "first play" has commenced | ||
276 | loader.on('firstplay', function() { | ||
277 | this.trackExpiredTime_ = true; | ||
278 | }); | ||
279 | |||
270 | // live playlist staleness timeout | 280 | // live playlist staleness timeout |
271 | loader.on('mediaupdatetimeout', function() { | 281 | loader.on('mediaupdatetimeout', function() { |
272 | if (loader.state !== 'HAVE_METADATA') { | 282 | if (loader.state !== 'HAVE_METADATA') { |
... | @@ -360,6 +370,14 @@ | ... | @@ -360,6 +370,14 @@ |
360 | return; | 370 | return; |
361 | } | 371 | } |
362 | 372 | ||
373 | // don't track expired time until this flag is truthy | ||
374 | if (!this.trackExpiredTime_) { | ||
375 | return; | ||
376 | } | ||
377 | |||
378 | // if the update was the result of a rendition switch do not | ||
379 | // attempt to calculate expired_ since media-sequences need not | ||
380 | // correlate between renditions/variants | ||
363 | if (update.uri !== outdated.uri) { | 381 | if (update.uri !== outdated.uri) { |
364 | return; | 382 | return; |
365 | } | 383 | } | ... | ... |
... | @@ -165,7 +165,7 @@ videojs.HlsHandler.prototype.src = function(src) { | ... | @@ -165,7 +165,7 @@ videojs.HlsHandler.prototype.src = function(src) { |
165 | } | 165 | } |
166 | this.playlists = new videojs.Hls.PlaylistLoader(this.source_.src, this.options_.withCredentials); | 166 | this.playlists = new videojs.Hls.PlaylistLoader(this.source_.src, this.options_.withCredentials); |
167 | 167 | ||
168 | this.tech_.on('canplay', this.setupFirstPlay.bind(this)); | 168 | this.tech_.one('canplay', this.setupFirstPlay.bind(this)); |
169 | 169 | ||
170 | this.playlists.on('loadedmetadata', function() { | 170 | this.playlists.on('loadedmetadata', function() { |
171 | oldMediaPlaylist = this.playlists.media(); | 171 | oldMediaPlaylist = this.playlists.media(); |
... | @@ -428,7 +428,10 @@ videojs.HlsHandler.prototype.setupFirstPlay = function() { | ... | @@ -428,7 +428,10 @@ videojs.HlsHandler.prototype.setupFirstPlay = function() { |
428 | 428 | ||
429 | // 5) the video element or flash player is in a readyState of | 429 | // 5) the video element or flash player is in a readyState of |
430 | // at least HAVE_FUTURE_DATA | 430 | // at least HAVE_FUTURE_DATA |
431 | this.tech_.readyState >= 3) { | 431 | this.tech_.readyState() >= 1) { |
432 | |||
433 | // trigger the playlist loader to start "expired time"-tracking | ||
434 | this.playlists.trigger('firstplay'); | ||
432 | 435 | ||
433 | // seek to the latest media position for live videos | 436 | // seek to the latest media position for live videos |
434 | seekable = this.seekable(); | 437 | seekable = this.seekable(); | ... | ... |
... | @@ -177,8 +177,37 @@ | ... | @@ -177,8 +177,37 @@ |
177 | strictEqual(loader.state, 'HAVE_METADATA', 'the state is correct'); | 177 | strictEqual(loader.state, 'HAVE_METADATA', 'the state is correct'); |
178 | }); | 178 | }); |
179 | 179 | ||
180 | test('does not increment expired seconds before firstplay is triggered', function() { | ||
181 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); | ||
182 | requests.pop().respond(200, null, | ||
183 | '#EXTM3U\n' + | ||
184 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | ||
185 | '#EXTINF:10,\n' + | ||
186 | '0.ts\n' + | ||
187 | '#EXTINF:10,\n' + | ||
188 | '1.ts\n' + | ||
189 | '#EXTINF:10,\n' + | ||
190 | '2.ts\n' + | ||
191 | '#EXTINF:10,\n' + | ||
192 | '3.ts\n'); | ||
193 | clock.tick(10 * 1000); // 10s, one target duration | ||
194 | requests.pop().respond(200, null, | ||
195 | '#EXTM3U\n' + | ||
196 | '#EXT-X-MEDIA-SEQUENCE:1\n' + | ||
197 | '#EXTINF:10,\n' + | ||
198 | '1.ts\n' + | ||
199 | '#EXTINF:10,\n' + | ||
200 | '2.ts\n' + | ||
201 | '#EXTINF:10,\n' + | ||
202 | '3.ts\n' + | ||
203 | '#EXTINF:10,\n' + | ||
204 | '4.ts\n'); | ||
205 | equal(loader.expired_, 0, 'expired one segment'); | ||
206 | }); | ||
207 | |||
180 | test('increments expired seconds after a segment is removed', function() { | 208 | test('increments expired seconds after a segment is removed', function() { |
181 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); | 209 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); |
210 | loader.trigger('firstplay'); | ||
182 | requests.pop().respond(200, null, | 211 | requests.pop().respond(200, null, |
183 | '#EXTM3U\n' + | 212 | '#EXTM3U\n' + |
184 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | 213 | '#EXT-X-MEDIA-SEQUENCE:0\n' + |
... | @@ -207,6 +236,7 @@ | ... | @@ -207,6 +236,7 @@ |
207 | 236 | ||
208 | test('increments expired seconds after a discontinuity', function() { | 237 | test('increments expired seconds after a discontinuity', function() { |
209 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); | 238 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); |
239 | loader.trigger('firstplay'); | ||
210 | requests.pop().respond(200, null, | 240 | requests.pop().respond(200, null, |
211 | '#EXTM3U\n' + | 241 | '#EXTM3U\n' + |
212 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | 242 | '#EXT-X-MEDIA-SEQUENCE:0\n' + |
... | @@ -249,6 +279,7 @@ | ... | @@ -249,6 +279,7 @@ |
249 | 279 | ||
250 | test('tracks expired seconds properly when two discontinuities expire at once', function() { | 280 | test('tracks expired seconds properly when two discontinuities expire at once', function() { |
251 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); | 281 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); |
282 | loader.trigger('firstplay'); | ||
252 | requests.pop().respond(200, null, | 283 | requests.pop().respond(200, null, |
253 | '#EXTM3U\n' + | 284 | '#EXTM3U\n' + |
254 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | 285 | '#EXT-X-MEDIA-SEQUENCE:0\n' + |
... | @@ -274,6 +305,7 @@ | ... | @@ -274,6 +305,7 @@ |
274 | 305 | ||
275 | test('estimates expired if an entire window elapses between live playlist updates', function() { | 306 | test('estimates expired if an entire window elapses between live playlist updates', function() { |
276 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); | 307 | var loader = new videojs.Hls.PlaylistLoader('live.m3u8'); |
308 | loader.trigger('firstplay'); | ||
277 | requests.pop().respond(200, null, | 309 | requests.pop().respond(200, null, |
278 | '#EXTM3U\n' + | 310 | '#EXTM3U\n' + |
279 | '#EXT-X-MEDIA-SEQUENCE:0\n' + | 311 | '#EXT-X-MEDIA-SEQUENCE:0\n' + |
... | @@ -826,6 +858,7 @@ | ... | @@ -826,6 +858,7 @@ |
826 | 858 | ||
827 | test('prefers precise segment timing when tracking expired time', function() { | 859 | test('prefers precise segment timing when tracking expired time', function() { |
828 | var loader = new videojs.Hls.PlaylistLoader('media.m3u8'); | 860 | var loader = new videojs.Hls.PlaylistLoader('media.m3u8'); |
861 | loader.trigger('firstplay'); | ||
829 | requests.shift().respond(200, null, | 862 | requests.shift().respond(200, null, |
830 | '#EXTM3U\n' + | 863 | '#EXTM3U\n' + |
831 | '#EXT-X-MEDIA-SEQUENCE:1001\n' + | 864 | '#EXT-X-MEDIA-SEQUENCE:1001\n' + | ... | ... |
... | @@ -335,7 +335,7 @@ test('autoplay seeks to the live point after playlist load', function() { | ... | @@ -335,7 +335,7 @@ test('autoplay seeks to the live point after playlist load', function() { |
335 | type: 'application/vnd.apple.mpegurl' | 335 | type: 'application/vnd.apple.mpegurl' |
336 | }); | 336 | }); |
337 | openMediaSource(player); | 337 | openMediaSource(player); |
338 | player.tech_.readyState = 3; | 338 | player.tech_.readyState = function(){return 1;}; |
339 | player.tech_.trigger('play'); | 339 | player.tech_.trigger('play'); |
340 | standardXHRResponse(requests.shift()); | 340 | standardXHRResponse(requests.shift()); |
341 | clock.tick(1); | 341 | clock.tick(1); |
... | @@ -357,7 +357,7 @@ test('autoplay seeks to the live point after media source open', function() { | ... | @@ -357,7 +357,7 @@ test('autoplay seeks to the live point after media source open', function() { |
357 | clock.tick(1); | 357 | clock.tick(1); |
358 | standardXHRResponse(requests.shift()); | 358 | standardXHRResponse(requests.shift()); |
359 | openMediaSource(player); | 359 | openMediaSource(player); |
360 | player.tech_.readyState = 3; | 360 | player.tech_.readyState = function(){return 1;}; |
361 | player.tech_.trigger('play'); | 361 | player.tech_.trigger('play'); |
362 | clock.tick(1); | 362 | clock.tick(1); |
363 | 363 | ||
... | @@ -410,7 +410,7 @@ test('calls `remove` on sourceBuffer to when loading a live segment', function() | ... | @@ -410,7 +410,7 @@ test('calls `remove` on sourceBuffer to when loading a live segment', function() |
410 | player.tech_.hls.playlists.trigger('loadedmetadata'); | 410 | player.tech_.hls.playlists.trigger('loadedmetadata'); |
411 | player.tech_.trigger('canplay'); | 411 | player.tech_.trigger('canplay'); |
412 | player.tech_.paused = function() { return false; }; | 412 | player.tech_.paused = function() { return false; }; |
413 | player.tech_.readyState = 3; | 413 | player.tech_.readyState = function(){return 1;}; |
414 | player.tech_.trigger('play'); | 414 | player.tech_.trigger('play'); |
415 | 415 | ||
416 | clock.tick(1); | 416 | clock.tick(1); |
... | @@ -1688,7 +1688,7 @@ test('live playlist starts three target durations before live', function() { | ... | @@ -1688,7 +1688,7 @@ test('live playlist starts three target durations before live', function() { |
1688 | equal(requests.length, 0, 'no outstanding segment request'); | 1688 | equal(requests.length, 0, 'no outstanding segment request'); |
1689 | 1689 | ||
1690 | player.tech_.paused = function() { return false; }; | 1690 | player.tech_.paused = function() { return false; }; |
1691 | player.tech_.readyState = 3; | 1691 | player.tech_.readyState = function(){return 1;}; |
1692 | player.tech_.trigger('play'); | 1692 | player.tech_.trigger('play'); |
1693 | clock.tick(1); | 1693 | clock.tick(1); |
1694 | mediaPlaylist = player.tech_.hls.playlists.media(); | 1694 | mediaPlaylist = player.tech_.hls.playlists.media(); |
... | @@ -1709,7 +1709,7 @@ test('live playlist starts with correct currentTime value', function() { | ... | @@ -1709,7 +1709,7 @@ test('live playlist starts with correct currentTime value', function() { |
1709 | player.tech_.hls.playlists.trigger('loadedmetadata'); | 1709 | player.tech_.hls.playlists.trigger('loadedmetadata'); |
1710 | 1710 | ||
1711 | player.tech_.paused = function() { return false; }; | 1711 | player.tech_.paused = function() { return false; }; |
1712 | player.tech_.readyState = 3; | 1712 | player.tech_.readyState = function(){return 1;}; |
1713 | player.tech_.trigger('play'); | 1713 | player.tech_.trigger('play'); |
1714 | clock.tick(1); | 1714 | clock.tick(1); |
1715 | 1715 | ... | ... |
-
Please register or sign in to post a comment