fe47dd5e by David LaPalomento

Merge pull request #299 from dmlap/autoplay-live

Don't preload live videos
2 parents 24ab1402 f328f16d
...@@ -129,16 +129,6 @@ videojs.Hls.prototype.src = function(src) { ...@@ -129,16 +129,6 @@ videojs.Hls.prototype.src = function(src) {
129 }); 129 });
130 } 130 }
131 131
132 // Start live playlists 30 seconds before the current time
133 // This is done using the old playlist because of a race condition
134 // where the playlist selected below may not be loaded quickly
135 // enough to have its segments available for review. When we receive
136 // a loadedplaylist event, we will call translateMediaIndex and
137 // maintain our position at the live point.
138 if (this.duration() === Infinity && this.mediaIndex === 0) {
139 this.mediaIndex = videojs.Hls.getMediaIndexForLive_(oldMediaPlaylist);
140 }
141
142 selectedPlaylist = this.selectPlaylist(); 132 selectedPlaylist = this.selectPlaylist();
143 oldBitrate = oldMediaPlaylist.attributes && 133 oldBitrate = oldMediaPlaylist.attributes &&
144 oldMediaPlaylist.attributes.BANDWIDTH || 0; 134 oldMediaPlaylist.attributes.BANDWIDTH || 0;
...@@ -235,15 +225,18 @@ videojs.Hls.prototype.handleSourceOpen = function() { ...@@ -235,15 +225,18 @@ videojs.Hls.prototype.handleSourceOpen = function() {
235 sourceBuffer = this.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"'); 225 sourceBuffer = this.mediaSource.addSourceBuffer('video/flv; codecs="vp6,aac"');
236 226
237 this.sourceBuffer = sourceBuffer; 227 this.sourceBuffer = sourceBuffer;
238 sourceBuffer.appendBuffer(this.segmentParser_.getFlvHeader());
239
240 228
241 // if autoplay is enabled, begin playback. This is duplicative of 229 // if autoplay is enabled, begin playback. This is duplicative of
242 // code in video.js but is required because play() must be invoked 230 // code in video.js but is required because play() must be invoked
243 // *after* the media source has opened. 231 // *after* the media source has opened.
232 // NOTE: moving this invocation of play() after
233 // sourceBuffer.appendBuffer() below caused live streams with
234 // autoplay to stall
244 if (player.options().autoplay) { 235 if (player.options().autoplay) {
245 player.play(); 236 player.play();
246 } 237 }
238
239 sourceBuffer.appendBuffer(this.segmentParser_.getFlvHeader());
247 }; 240 };
248 241
249 // register event listeners to transform in-band metadata events into 242 // register event listeners to transform in-band metadata events into
...@@ -320,13 +313,19 @@ videojs.Hls.prototype.setupMetadataCueTranslation_ = function() { ...@@ -320,13 +313,19 @@ videojs.Hls.prototype.setupMetadataCueTranslation_ = function() {
320 * ended. 313 * ended.
321 */ 314 */
322 videojs.Hls.prototype.play = function() { 315 videojs.Hls.prototype.play = function() {
316 var media;
323 if (this.ended()) { 317 if (this.ended()) {
324 this.mediaIndex = 0; 318 this.mediaIndex = 0;
325 } 319 }
326 320
327 if (this.duration() === Infinity && this.playlists.media() && !this.player().hasClass('vjs-has-started')) { 321 // seek to the latest safe point in the media timeline when first
328 this.mediaIndex = videojs.Hls.getMediaIndexForLive_(this.playlists.media()); 322 // playing live streams
329 this.setCurrentTime(this.getCurrentTimeByMediaIndex_(this.playlists.media(), this.mediaIndex)); 323 if (this.duration() === Infinity &&
324 this.playlists.media() &&
325 !this.player().hasClass('vjs-has-started')) {
326 media = this.playlists.media();
327 this.mediaIndex = videojs.Hls.getMediaIndexForLive_(media);
328 this.setCurrentTime(videojs.Hls.Playlist.seekable(media).end(0));
330 } 329 }
331 330
332 // delegate back to the Flash implementation 331 // delegate back to the Flash implementation
...@@ -638,6 +637,14 @@ videojs.Hls.prototype.fillBuffer = function(offset) { ...@@ -638,6 +637,14 @@ videojs.Hls.prototype.fillBuffer = function(offset) {
638 return; 637 return;
639 } 638 }
640 639
640 // if this is a live video wait until playback has been requested to
641 // being buffering so we don't preload data that will never be
642 // played
643 if (!this.playlists.media().endList &&
644 !this.player().hasClass('vjs-has-started')) {
645 return;
646 }
647
641 // if a playlist switch is in progress, wait for it to finish 648 // if a playlist switch is in progress, wait for it to finish
642 if (this.playlists.state === 'SWITCHING_MEDIA') { 649 if (this.playlists.state === 'SWITCHING_MEDIA') {
643 return; 650 return;
......
...@@ -1427,6 +1427,7 @@ test('translates ID3 PTS values across discontinuities', function() { ...@@ -1427,6 +1427,7 @@ test('translates ID3 PTS values across discontinuities', function() {
1427 }; 1427 };
1428 1428
1429 // media playlist 1429 // media playlist
1430 player.trigger('play');
1430 requests.shift().respond(200, null, 1431 requests.shift().respond(200, null,
1431 '#EXTM3U\n' + 1432 '#EXTM3U\n' +
1432 '#EXTINF:10,\n' + 1433 '#EXTINF:10,\n' +
...@@ -1627,6 +1628,7 @@ test('updates the media index when a playlist reloads', function() { ...@@ -1627,6 +1628,7 @@ test('updates the media index when a playlist reloads', function() {
1627 type: 'application/vnd.apple.mpegurl' 1628 type: 'application/vnd.apple.mpegurl'
1628 }); 1629 });
1629 openMediaSource(player); 1630 openMediaSource(player);
1631 player.trigger('play');
1630 1632
1631 requests[0].respond(200, null, 1633 requests[0].respond(200, null,
1632 '#EXTM3U\n' + 1634 '#EXTM3U\n' +
...@@ -1658,18 +1660,22 @@ test('updates the media index when a playlist reloads', function() { ...@@ -1658,18 +1660,22 @@ test('updates the media index when a playlist reloads', function() {
1658 strictEqual(player.hls.mediaIndex, 2, 'mediaIndex is updated after the reload'); 1660 strictEqual(player.hls.mediaIndex, 2, 'mediaIndex is updated after the reload');
1659 }); 1661 });
1660 1662
1661 test('live playlist starts 30s before live', function() { 1663 test('live playlist starts three target durations before live', function() {
1664 var mediaPlaylist;
1662 player.src({ 1665 player.src({
1663 src: 'http://example.com/manifest/liveStart30sBefore.m3u8', 1666 src: 'http://example.com/manifest/liveStart30sBefore.m3u8',
1664 type: 'application/vnd.apple.mpegurl' 1667 type: 'application/vnd.apple.mpegurl'
1665 }); 1668 });
1666 openMediaSource(player); 1669 openMediaSource(player);
1670 standardXHRResponse(requests.shift());
1667 1671
1668 standardXHRResponse(requests[0]); 1672 equal(player.hls.mediaIndex, 0, 'waits for the first play to start buffering');
1673 equal(requests.length, 0, 'no outstanding segment request');
1669 1674
1670 player.hls.playlists.trigger('loadedmetadata'); 1675 player.play();
1671 1676 mediaPlaylist = player.hls.playlists.media();
1672 strictEqual(player.hls.mediaIndex, 6, 'mediaIndex is updated after the reload'); 1677 equal(player.hls.mediaIndex, 6, 'mediaIndex is updated at play');
1678 equal(player.currentTime(), videojs.Hls.Playlist.seekable(mediaPlaylist).end(0));
1673 }); 1679 });
1674 1680
1675 test('does not reset live currentTime if mediaIndex is one beyond the last available segment', function() { 1681 test('does not reset live currentTime if mediaIndex is one beyond the last available segment', function() {
...@@ -1703,7 +1709,9 @@ test('live playlist starts with correct currentTime value', function() { ...@@ -1703,7 +1709,9 @@ test('live playlist starts with correct currentTime value', function() {
1703 1709
1704 player.hls.play(); 1710 player.hls.play();
1705 1711
1706 strictEqual(player.currentTime(), 70, 'currentTime is updated at playback'); 1712 strictEqual(player.currentTime(),
1713 videojs.Hls.Playlist.seekable(player.hls.playlists.media()).end(0),
1714 'currentTime is updated at playback');
1707 }); 1715 });
1708 1716
1709 test('mediaIndex is zero before the first segment loads', function() { 1717 test('mediaIndex is zero before the first segment loads', function() {
...@@ -1798,6 +1806,7 @@ test('calls vjs_discontinuity() before appending bytes at a discontinuity', func ...@@ -1798,6 +1806,7 @@ test('calls vjs_discontinuity() before appending bytes at a discontinuity', func
1798 type: 'application/vnd.apple.mpegurl' 1806 type: 'application/vnd.apple.mpegurl'
1799 }); 1807 });
1800 openMediaSource(player); 1808 openMediaSource(player);
1809 player.trigger('play');
1801 player.currentTime = function() { return currentTime; }; 1810 player.currentTime = function() { return currentTime; };
1802 player.buffered = function() { 1811 player.buffered = function() {
1803 return videojs.createTimeRange(0, bufferEnd); 1812 return videojs.createTimeRange(0, bufferEnd);
...@@ -2221,7 +2230,7 @@ test('keys are requested when an encrypted segment is loaded', function() { ...@@ -2221,7 +2230,7 @@ test('keys are requested when an encrypted segment is loaded', function() {
2221 type: 'application/vnd.apple.mpegurl' 2230 type: 'application/vnd.apple.mpegurl'
2222 }); 2231 });
2223 openMediaSource(player); 2232 openMediaSource(player);
2224 2233 player.trigger('play');
2225 standardXHRResponse(requests.shift()); // playlist 2234 standardXHRResponse(requests.shift()); // playlist
2226 standardXHRResponse(requests.shift()); // first segment 2235 standardXHRResponse(requests.shift()); // first segment
2227 2236
...@@ -2314,7 +2323,6 @@ test('seeking should abort an outstanding key request and create a new one', fun ...@@ -2314,7 +2323,6 @@ test('seeking should abort an outstanding key request and create a new one', fun
2314 }); 2323 });
2315 openMediaSource(player); 2324 openMediaSource(player);
2316 2325
2317
2318 requests.shift().respond(200, null, 2326 requests.shift().respond(200, null,
2319 '#EXTM3U\n' + 2327 '#EXTM3U\n' +
2320 '#EXT-X-TARGETDURATION:15\n' + 2328 '#EXT-X-TARGETDURATION:15\n' +
...@@ -2346,6 +2354,7 @@ test('retries key requests once upon failure', function() { ...@@ -2346,6 +2354,7 @@ test('retries key requests once upon failure', function() {
2346 type: 'application/vnd.apple.mpegurl' 2354 type: 'application/vnd.apple.mpegurl'
2347 }); 2355 });
2348 openMediaSource(player); 2356 openMediaSource(player);
2357 player.trigger('play');
2349 2358
2350 requests.shift().respond(200, null, 2359 requests.shift().respond(200, null,
2351 '#EXTM3U\n' + 2360 '#EXTM3U\n' +
...@@ -2381,6 +2390,7 @@ test('skip segments if key requests fail more than once', function() { ...@@ -2381,6 +2390,7 @@ test('skip segments if key requests fail more than once', function() {
2381 type: 'application/vnd.apple.mpegurl' 2390 type: 'application/vnd.apple.mpegurl'
2382 }); 2391 });
2383 openMediaSource(player); 2392 openMediaSource(player);
2393 player.trigger('play');
2384 2394
2385 requests.shift().respond(200, null, 2395 requests.shift().respond(200, null,
2386 '#EXTM3U\n' + 2396 '#EXTM3U\n' +
...@@ -2419,6 +2429,7 @@ test('the key is supplied to the decrypter in the correct format', function() { ...@@ -2419,6 +2429,7 @@ test('the key is supplied to the decrypter in the correct format', function() {
2419 type: 'application/vnd.apple.mpegurl' 2429 type: 'application/vnd.apple.mpegurl'
2420 }); 2430 });
2421 openMediaSource(player); 2431 openMediaSource(player);
2432 player.trigger('play');
2422 2433
2423 requests.pop().respond(200, null, 2434 requests.pop().respond(200, null,
2424 '#EXTM3U\n' + 2435 '#EXTM3U\n' +
...@@ -2453,6 +2464,7 @@ test('supplies the media sequence of current segment as the IV by default, if no ...@@ -2453,6 +2464,7 @@ test('supplies the media sequence of current segment as the IV by default, if no
2453 type: 'application/vnd.apple.mpegurl' 2464 type: 'application/vnd.apple.mpegurl'
2454 }); 2465 });
2455 openMediaSource(player); 2466 openMediaSource(player);
2467 player.trigger('play');
2456 2468
2457 requests.pop().respond(200, null, 2469 requests.pop().respond(200, null,
2458 '#EXTM3U\n' + 2470 '#EXTM3U\n' +
...@@ -2492,6 +2504,7 @@ test('switching playlists with an outstanding key request does not stall playbac ...@@ -2492,6 +2504,7 @@ test('switching playlists with an outstanding key request does not stall playbac
2492 type: 'application/vnd.apple.mpegurl' 2504 type: 'application/vnd.apple.mpegurl'
2493 }); 2505 });
2494 openMediaSource(player); 2506 openMediaSource(player);
2507 player.trigger('play');
2495 2508
2496 // master playlist 2509 // master playlist
2497 standardXHRResponse(requests.shift()); 2510 standardXHRResponse(requests.shift());
...@@ -2532,7 +2545,8 @@ test('resolves relative key URLs against the playlist', function() { ...@@ -2532,7 +2545,8 @@ test('resolves relative key URLs against the playlist', function() {
2532 '#EXT-X-MEDIA-SEQUENCE:5\n' + 2545 '#EXT-X-MEDIA-SEQUENCE:5\n' +
2533 '#EXT-X-KEY:METHOD=AES-128,URI="key.php?r=52"\n' + 2546 '#EXT-X-KEY:METHOD=AES-128,URI="key.php?r=52"\n' +
2534 '#EXTINF:2.833,\n' + 2547 '#EXTINF:2.833,\n' +
2535 'http://media.example.com/fileSequence52-A.ts\n'); 2548 'http://media.example.com/fileSequence52-A.ts\n' +
2549 '#EXT-X-ENDLIST\n');
2536 standardXHRResponse(requests.shift()); // segment 2550 standardXHRResponse(requests.shift()); // segment
2537 2551
2538 equal(requests[0].url, 'https://example.com/key.php?r=52', 'resolves the key URL'); 2552 equal(requests[0].url, 'https://example.com/key.php?r=52', 'resolves the key URL');
...@@ -2552,6 +2566,7 @@ test('treats invalid keys as a key request failure', function() { ...@@ -2552,6 +2566,7 @@ test('treats invalid keys as a key request failure', function() {
2552 type: 'application/vnd.apple.mpegurl' 2566 type: 'application/vnd.apple.mpegurl'
2553 }); 2567 });
2554 openMediaSource(player); 2568 openMediaSource(player);
2569 player.trigger('play');
2555 requests.shift().respond(200, null, 2570 requests.shift().respond(200, null,
2556 '#EXTM3U\n' + 2571 '#EXTM3U\n' +
2557 '#EXT-X-MEDIA-SEQUENCE:5\n' + 2572 '#EXT-X-MEDIA-SEQUENCE:5\n' +
...@@ -2593,6 +2608,7 @@ test('live stream should not call endOfStream', function(){ ...@@ -2593,6 +2608,7 @@ test('live stream should not call endOfStream', function(){
2593 type: 'application/vnd.apple.mpegurl' 2608 type: 'application/vnd.apple.mpegurl'
2594 }); 2609 });
2595 openMediaSource(player); 2610 openMediaSource(player);
2611 player.trigger('play');
2596 requests[0].respond(200, null, 2612 requests[0].respond(200, null,
2597 '#EXTM3U\n' + 2613 '#EXTM3U\n' +
2598 '#EXT-X-MEDIA-SEQUENCE:0\n' + 2614 '#EXT-X-MEDIA-SEQUENCE:0\n' +
......