140c0810 by Brandon Bay

Live start time tweaks

Moving live start time calculation earlier in the code flow to
eliminate a race condition, repeating it on first play to create a
smooth experience, and making sure currentTime is updated to match
these changes.
1 parent 4643387e
...@@ -66,7 +66,7 @@ ...@@ -66,7 +66,7 @@
66 width="600" 66 width="600"
67 controls> 67 controls>
68 <source 68 <source
69 src="http://solutions.brightcove.com/jwhisenant/hls/apple/bipbop/bipbopall.m3u8" 69 src="http://10.1.12.182:9090/event/adaptive-streaming/brightcove/play/event-master.m3u8?init=3"
70 type="application/x-mpegURL"> 70 type="application/x-mpegURL">
71 </video> 71 </video>
72 <script> 72 <script>
......
...@@ -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,10 @@ videojs.Hls.prototype.src = function(src) { ...@@ -239,7 +244,10 @@ 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) {
243 var tailIterator = selectedPlaylist.segments.length, 251 var tailIterator = selectedPlaylist.segments.length,
244 tailDuration = 0, 252 tailDuration = 0,
245 targetTail = (selectedPlaylist.targetDuration || 10) * 3; 253 targetTail = (selectedPlaylist.targetDuration || 10) * 3;
...@@ -279,6 +287,11 @@ videojs.Hls.prototype.play = function() { ...@@ -279,6 +287,11 @@ videojs.Hls.prototype.play = function() {
279 this.mediaIndex = 0; 287 this.mediaIndex = 0;
280 } 288 }
281 289
290 if (this.playlists.media() && !this.el().classList.contains('vjs-has-started')) {
291 this.mediaIndex = videojs.Hls.getMediaIndexForLive(this.playlists.media());
292 this.setCurrentTime(this.setCurrentTimeByMediaIndex(this.playlists.media(), this.mediaIndex));
293 }
294
282 // delegate back to the Flash implementation 295 // delegate back to the Flash implementation
283 return videojs.Flash.prototype.play.apply(this, arguments); 296 return videojs.Flash.prototype.play.apply(this, arguments);
284 }; 297 };
...@@ -936,7 +949,7 @@ videojs.Hls.translateMediaIndex = function(mediaIndex, original, update) { ...@@ -936,7 +949,7 @@ videojs.Hls.translateMediaIndex = function(mediaIndex, original, update) {
936 949
937 if (translatedMediaIndex >= update.segments.length || translatedMediaIndex < 0) { 950 if (translatedMediaIndex >= update.segments.length || translatedMediaIndex < 0) {
938 // recalculate the live point if the streams are too far out of sync 951 // recalculate the live point if the streams are too far out of sync
939 return videojs.Hls.setMediaIndexForLive(update) + 1; 952 return videojs.Hls.getMediaIndexForLive(update) + 1;
940 } 953 }
941 954
942 // sync on media sequence 955 // sync on media sequence
...@@ -973,6 +986,20 @@ videojs.Hls.getMediaIndexByTime = function(playlist, time) { ...@@ -973,6 +986,20 @@ videojs.Hls.getMediaIndexByTime = function(playlist, time) {
973 return -1; 986 return -1;
974 }; 987 };
975 988
989 videojs.Hls.prototype.setCurrentTimeByMediaIndex = function(playlist, mediaIndex) {
990 var index, time = 0;
991
992 if (!playlist.segments || mediaIndex === 0) {
993 return 0;
994 }
995
996 for (index = 0; index < mediaIndex - 1; index++) {
997 time += playlist.segments[index].duration;
998 }
999
1000 return time;
1001 };
1002
976 /** 1003 /**
977 * A comparator function to sort two playlist object by bandwidth. 1004 * A comparator function to sort two playlist object by bandwidth.
978 * @param left {object} a media playlist object 1005 * @param left {object} a media playlist object
......
...@@ -1249,6 +1249,21 @@ test('live playlist starts 30s before live', function() { ...@@ -1249,6 +1249,21 @@ 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 player.hls.play();
1263
1264 strictEqual(player.currentTime(), 60, 'currentTime is updated at playback');
1265 });
1266
1252 test('mediaIndex is zero before the first segment loads', function() { 1267 test('mediaIndex is zero before the first segment loads', function() {
1253 window.manifests['first-seg-load'] = 1268 window.manifests['first-seg-load'] =
1254 '#EXTM3U\n' + 1269 '#EXTM3U\n' +
......