cf295e56 by jrivera

Fixed an issue with endOfStream logic and started updating segment durations whe…

…n enough information exists to do so
* Moved endOfstream and playlist updating code out of `updateendHandler`
* We now update segment durations when we have a previous segment with a known end-time
* Always trigger endOfStream when we append the last fragment in addition to triggering it if we are in a buffer that touches the end of the stream (duration) after an append
1 parent 4c388270
...@@ -297,6 +297,77 @@ videojs.Hls.findSoleUncommonTimeRangesEnd_ = function(original, update) { ...@@ -297,6 +297,77 @@ videojs.Hls.findSoleUncommonTimeRangesEnd_ = function(original, update) {
297 return result[0]; 297 return result[0];
298 }; 298 };
299 299
300 /**
301 * Updates segment with information about it's end-point in time and, optionally,
302 * the segment duration if we have enough information to determine a segment duration
303 * accurately.
304 * @param playlist {object} a media playlist object
305 * @param segmentIndex {number} the index of segment we last appended
306 * @param segmentEnd {number} the known of the segment referenced by segmentIndex
307 */
308 videojs.HlsHandler.prototype.updateSegmentMetadata_ = function(playlist, segmentIndex, segmentEnd) {
309 var
310 segment,
311 previousSegment;
312
313 if (!playlist) {
314 return;
315 }
316
317 segment = playlist.segments[segmentIndex];
318 previousSegment = playlist.segments[segmentIndex - 1];
319
320 if (segmentEnd && segment) {
321 segment.end = segmentEnd;
322
323 // fix up segment durations based on segment end data
324 if (!previousSegment) {
325 // first segment is always has a start time of 0 making it's duration
326 // equal to the segment end
327 segment.duration = segment.end;
328 } else if (previousSegment.end) {
329 segment.duration = segment.end - previousSegment.end;
330 }
331 }
332 };
333
334 /**
335 * Determines if we should call endOfStream on the media source based on the state
336 * of the buffer or if appened segment was the final segment in the playlist.
337 * @param playlist {object} a media playlist object
338 * @param segmentIndex {number} the index of segment we last appended
339 * @param currentBuffered {object} the buffered region that currentTime resides in
340 * @return {boolean} whether or not endOfStream was called on the MediaSource
341 */
342 videojs.HlsHandler.prototype.maybeEndOfStream_ = function(playlist, segmentIndex, currentBuffered) {
343 var
344 segments = playlist.segments,
345 appendedLastSegment,
346 bufferedToEnd;
347
348 if (!playlist) {
349 return false;
350 }
351
352 // determine a few boolean values to help make the branch below easier
353 // to read
354 appendedLastSegment = (segmentIndex === segments.length - 1);
355 bufferedToEnd = (currentBuffered.length &&
356 segments[segments.length - 1].end <= currentBuffered.end(0));
357
358 // if we've buffered to the end of the video, we need to call endOfStream
359 // so that MediaSources can trigger the `ended` event when it runs out of
360 // buffered data instead of waiting for me
361 if (playlist.endList &&
362 this.mediaSource.readyState === 'open' &&
363 (appendedLastSegment ||
364 bufferedToEnd)) {
365 this.mediaSource.endOfStream();
366 return true;
367 }
368 return false;
369 };
370
300 var parseCodecs = function(codecs) { 371 var parseCodecs = function(codecs) {
301 var result = { 372 var result = {
302 codecCount: 0, 373 codecCount: 0,
...@@ -507,22 +578,17 @@ videojs.HlsHandler.prototype.setCurrentTime = function(currentTime) { ...@@ -507,22 +578,17 @@ videojs.HlsHandler.prototype.setCurrentTime = function(currentTime) {
507 578
508 videojs.HlsHandler.prototype.duration = function() { 579 videojs.HlsHandler.prototype.duration = function() {
509 var 580 var
510 playlists = this.playlists, 581 playlists = this.playlists;
511 playlistDuration;
512 582
513 if (playlists) { 583 if (!playlists) {
514 playlistDuration = videojs.Hls.Playlist.duration(playlists.media());
515 } else {
516 return 0; 584 return 0;
517 } 585 }
518 586
519 if (playlistDuration === Infinity) { 587 if (this.mediaSource) {
520 return playlistDuration;
521 } else if (this.mediaSource) {
522 return this.mediaSource.duration; 588 return this.mediaSource.duration;
523 } else {
524 return playlistDuration;
525 } 589 }
590
591 return videojs.Hls.Playlist.duration(playlists.media());
526 }; 592 };
527 593
528 videojs.HlsHandler.prototype.seekable = function() { 594 videojs.HlsHandler.prototype.seekable = function() {
...@@ -563,6 +629,7 @@ videojs.HlsHandler.prototype.seekable = function() { ...@@ -563,6 +629,7 @@ videojs.HlsHandler.prototype.seekable = function() {
563 videojs.HlsHandler.prototype.updateDuration = function(playlist) { 629 videojs.HlsHandler.prototype.updateDuration = function(playlist) {
564 var oldDuration = this.mediaSource.duration, 630 var oldDuration = this.mediaSource.duration,
565 newDuration = videojs.Hls.Playlist.duration(playlist), 631 newDuration = videojs.Hls.Playlist.duration(playlist),
632 buffered = this.tech_.buffered(),
566 setDuration = function() { 633 setDuration = function() {
567 this.mediaSource.duration = newDuration; 634 this.mediaSource.duration = newDuration;
568 this.tech_.trigger('durationchange'); 635 this.tech_.trigger('durationchange');
...@@ -570,6 +637,10 @@ videojs.HlsHandler.prototype.updateDuration = function(playlist) { ...@@ -570,6 +637,10 @@ videojs.HlsHandler.prototype.updateDuration = function(playlist) {
570 this.mediaSource.removeEventListener('sourceopen', setDuration); 637 this.mediaSource.removeEventListener('sourceopen', setDuration);
571 }.bind(this); 638 }.bind(this);
572 639
640 if (buffered.length > 0) {
641 newDuration = Math.max(newDuration, buffered.end(buffered.length - 1));
642 }
643
573 // if the duration has changed, invalidate the cached value 644 // if the duration has changed, invalidate the cached value
574 if (oldDuration !== newDuration) { 645 if (oldDuration !== newDuration) {
575 // update the duration 646 // update the duration
...@@ -1255,7 +1326,7 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { ...@@ -1255,7 +1326,7 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
1255 1326
1256 this.pendingSegment_ = null; 1327 this.pendingSegment_ = null;
1257 1328
1258 playlist = this.playlists.media(); 1329 playlist = segmentInfo.playlist;
1259 segments = playlist.segments; 1330 segments = playlist.segments;
1260 currentMediaIndex = segmentInfo.mediaIndex + (segmentInfo.mediaSequence - playlist.mediaSequence); 1331 currentMediaIndex = segmentInfo.mediaIndex + (segmentInfo.mediaSequence - playlist.mediaSequence);
1261 currentBuffered = this.findBufferedRange_(); 1332 currentBuffered = this.findBufferedRange_();
...@@ -1287,20 +1358,15 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () { ...@@ -1287,20 +1358,15 @@ videojs.HlsHandler.prototype.updateEndHandler_ = function () {
1287 } 1358 }
1288 } 1359 }
1289 1360
1290
1291 timelineUpdate = videojs.Hls.findSoleUncommonTimeRangesEnd_(segmentInfo.buffered, 1361 timelineUpdate = videojs.Hls.findSoleUncommonTimeRangesEnd_(segmentInfo.buffered,
1292 this.tech_.buffered()); 1362 this.tech_.buffered());
1293 1363
1294 if (timelineUpdate && segment) { 1364 // Update segment meta-data (duration and end-point) based on timeline
1295 segment.end = timelineUpdate; 1365 this.updateSegmentMetadata_(playlist, currentMediaIndex, timelineUpdate);
1296 }
1297 1366
1298 // if we've buffered to the end of the video, let the MediaSource know 1367 // If we decide to signal the end of stream, then we can return instead
1299 if (this.playlists.media().endList && 1368 // of trying to fetch more segments
1300 currentBuffered.length && 1369 if (this.maybeEndOfStream_(playlist, currentMediaIndex, currentBuffered)) {
1301 segments[segments.length - 1].end <= currentBuffered.end(0) &&
1302 this.mediaSource.readyState === 'open') {
1303 this.mediaSource.endOfStream();
1304 return; 1370 return;
1305 } 1371 }
1306 1372
......