651d468e by David LaPalomento

Merge pull request #229 from videojs/live-start-time

Live start time tweaks
2 parents 7744312d 8028f6b0
...@@ -39,7 +39,7 @@ ...@@ -39,7 +39,7 @@
39 "karma-sauce-launcher": "~0.1.8", 39 "karma-sauce-launcher": "~0.1.8",
40 "qunitjs": "^1.15.0", 40 "qunitjs": "^1.15.0",
41 "sinon": "1.10.2", 41 "sinon": "1.10.2",
42 "video.js": "^4.9.0" 42 "video.js": "^4.12.0"
43 }, 43 },
44 "dependencies": { 44 "dependencies": {
45 "pkcs7": "^0.2.2", 45 "pkcs7": "^0.2.2",
......
...@@ -164,6 +164,16 @@ videojs.Hls.prototype.src = function(src) { ...@@ -164,6 +164,16 @@ videojs.Hls.prototype.src = function(src) {
164 }); 164 });
165 } 165 }
166 166
167 // Start live playlists 30 seconds before the current time
168 // This is done using the old playlist because of a race condition
169 // where the playlist selected below may not be loaded quickly
170 // enough to have its segments available for review. When we receive
171 // a loadedplaylist event, we will call translateMediaIndex and
172 // maintain our position at the live point.
173 if (this.duration() === Infinity && this.mediaIndex === 0) {
174 this.mediaIndex = videojs.Hls.getMediaIndexForLive_(oldMediaPlaylist);
175 }
176
167 selectedPlaylist = this.selectPlaylist(); 177 selectedPlaylist = this.selectPlaylist();
168 oldBitrate = oldMediaPlaylist.attributes && 178 oldBitrate = oldMediaPlaylist.attributes &&
169 oldMediaPlaylist.attributes.BANDWIDTH || 0; 179 oldMediaPlaylist.attributes.BANDWIDTH || 0;
...@@ -179,11 +189,6 @@ videojs.Hls.prototype.src = function(src) { ...@@ -179,11 +189,6 @@ videojs.Hls.prototype.src = function(src) {
179 segmentDlTime = Infinity; 189 segmentDlTime = Infinity;
180 } 190 }
181 191
182 // start live playlists 30 seconds before the current time
183 if (this.duration() === Infinity && this.mediaIndex === 0 && selectedPlaylist.segments) {
184 this.mediaIndex = videojs.Hls.setMediaIndexForLive(selectedPlaylist);
185 }
186
187 // this threshold is to account for having a high latency on the manifest 192 // this threshold is to account for having a high latency on the manifest
188 // request which is a somewhat small file. 193 // request which is a somewhat small file.
189 threshold = 10; 194 threshold = 10;
...@@ -239,7 +244,14 @@ videojs.Hls.prototype.src = function(src) { ...@@ -239,7 +244,14 @@ videojs.Hls.prototype.src = function(src) {
239 }); 244 });
240 }; 245 };
241 246
242 videojs.Hls.setMediaIndexForLive = function(selectedPlaylist) { 247 /* Returns the media index for the live point in the current playlist, and updates
248 the current time to go along with it.
249 */
250 videojs.Hls.getMediaIndexForLive_ = function(selectedPlaylist) {
251 if (!selectedPlaylist.segments) {
252 return 0;
253 }
254
243 var tailIterator = selectedPlaylist.segments.length, 255 var tailIterator = selectedPlaylist.segments.length,
244 tailDuration = 0, 256 tailDuration = 0,
245 targetTail = (selectedPlaylist.targetDuration || 10) * 3; 257 targetTail = (selectedPlaylist.targetDuration || 10) * 3;
...@@ -279,6 +291,11 @@ videojs.Hls.prototype.play = function() { ...@@ -279,6 +291,11 @@ videojs.Hls.prototype.play = function() {
279 this.mediaIndex = 0; 291 this.mediaIndex = 0;
280 } 292 }
281 293
294 if (this.duration() === Infinity && this.playlists.media() && !this.player().hasClass('vjs-has-started')) {
295 this.mediaIndex = videojs.Hls.getMediaIndexForLive_(this.playlists.media());
296 this.setCurrentTime(this.getCurrentTimeByMediaIndex_(this.playlists.media(), this.mediaIndex));
297 }
298
282 // delegate back to the Flash implementation 299 // delegate back to the Flash implementation
283 return videojs.Flash.prototype.play.apply(this, arguments); 300 return videojs.Flash.prototype.play.apply(this, arguments);
284 }; 301 };
...@@ -936,7 +953,7 @@ videojs.Hls.translateMediaIndex = function(mediaIndex, original, update) { ...@@ -936,7 +953,7 @@ videojs.Hls.translateMediaIndex = function(mediaIndex, original, update) {
936 953
937 if (translatedMediaIndex >= update.segments.length || translatedMediaIndex < 0) { 954 if (translatedMediaIndex >= update.segments.length || translatedMediaIndex < 0) {
938 // recalculate the live point if the streams are too far out of sync 955 // recalculate the live point if the streams are too far out of sync
939 return videojs.Hls.setMediaIndexForLive(update) + 1; 956 return videojs.Hls.getMediaIndexForLive_(update) + 1;
940 } 957 }
941 958
942 // sync on media sequence 959 // sync on media sequence
...@@ -974,6 +991,29 @@ videojs.Hls.getMediaIndexByTime = function(playlist, time) { ...@@ -974,6 +991,29 @@ videojs.Hls.getMediaIndexByTime = function(playlist, time) {
974 }; 991 };
975 992
976 /** 993 /**
994 * Determine the current time in seconds in one playlist by a media index. This
995 * function iterates through the segments of a playlist up to the specified index
996 * and then returns the time up to that point.
997 *
998 * @param playlist {object} The playlist of the segments being searched.
999 * @param mediaIndex {number} The index of the target segment in the playlist.
1000 * @returns {number} The current time to that point, or 0 if none appropriate.
1001 */
1002 videojs.Hls.prototype.getCurrentTimeByMediaIndex_ = function(playlist, mediaIndex) {
1003 var index, time = 0;
1004
1005 if (!playlist.segments || mediaIndex === 0) {
1006 return 0;
1007 }
1008
1009 for (index = 0; index < mediaIndex; index++) {
1010 time += playlist.segments[index].duration;
1011 }
1012
1013 return time;
1014 };
1015
1016 /**
977 * A comparator function to sort two playlist object by bandwidth. 1017 * A comparator function to sort two playlist object by bandwidth.
978 * @param left {object} a media playlist object 1018 * @param left {object} a media playlist object
979 * @param right {object} a media playlist object 1019 * @param right {object} a media playlist object
......
...@@ -1249,6 +1249,22 @@ test('live playlist starts 30s before live', function() { ...@@ -1249,6 +1249,22 @@ test('live playlist starts 30s before live', function() {
1249 strictEqual(player.hls.mediaIndex, 6, 'mediaIndex is updated after the reload'); 1249 strictEqual(player.hls.mediaIndex, 6, 'mediaIndex is updated after the reload');
1250 }); 1250 });
1251 1251
1252 test('live playlist starts with correct currentTime value', function() {
1253 player.src({
1254 src: 'http://example.com/manifest/liveStart30sBefore.m3u8',
1255 type: 'application/vnd.apple.mpegurl'
1256 });
1257 openMediaSource(player);
1258
1259 standardXHRResponse(requests[0]);
1260
1261 player.hls.playlists.trigger('loadedmetadata');
1262
1263 player.hls.play();
1264
1265 strictEqual(player.currentTime(), 70, 'currentTime is updated at playback');
1266 });
1267
1252 test('mediaIndex is zero before the first segment loads', function() { 1268 test('mediaIndex is zero before the first segment loads', function() {
1253 window.manifests['first-seg-load'] = 1269 window.manifests['first-seg-load'] =
1254 '#EXTM3U\n' + 1270 '#EXTM3U\n' +
...@@ -1720,6 +1736,7 @@ test('calling play() at the end of a video resets the media index', function() { ...@@ -1720,6 +1736,7 @@ test('calling play() at the end of a video resets the media index', function() {
1720 player.hls.ended = function() { 1736 player.hls.ended = function() {
1721 return true; 1737 return true;
1722 }; 1738 };
1739
1723 player.play(); 1740 player.play();
1724 strictEqual(player.hls.mediaIndex, 0, 'index is 1 after the first segment'); 1741 strictEqual(player.hls.mediaIndex, 0, 'index is 1 after the first segment');
1725 }); 1742 });
......