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
Showing
1 changed file
with
87 additions
and
21 deletions
... | @@ -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 | ... | ... |
-
Please register or sign in to post a comment