Merge pull request #260 from videojs/preciseDuration
Use presentation time stamp to calculate playlist duration
Showing
2 changed files
with
57 additions
and
4 deletions
... | @@ -712,11 +712,11 @@ videojs.Hls.prototype.drainBuffer = function(event) { | ... | @@ -712,11 +712,11 @@ videojs.Hls.prototype.drainBuffer = function(event) { |
712 | tags, | 712 | tags, |
713 | bytes, | 713 | bytes, |
714 | segment, | 714 | segment, |
715 | durationOffset, | ||
715 | decrypter, | 716 | decrypter, |
716 | segIv, | 717 | segIv, |
717 | |||
718 | ptsTime, | 718 | ptsTime, |
719 | segmentOffset, | 719 | segmentOffset = 0, |
720 | segmentBuffer = this.segmentBuffer_; | 720 | segmentBuffer = this.segmentBuffer_; |
721 | 721 | ||
722 | if (!segmentBuffer.length || !this.sourceBuffer) { | 722 | if (!segmentBuffer.length || !this.sourceBuffer) { |
... | @@ -773,10 +773,32 @@ videojs.Hls.prototype.drainBuffer = function(event) { | ... | @@ -773,10 +773,32 @@ videojs.Hls.prototype.drainBuffer = function(event) { |
773 | this.segmentParser_.flushTags(); | 773 | this.segmentParser_.flushTags(); |
774 | 774 | ||
775 | tags = []; | 775 | tags = []; |
776 | |||
776 | while (this.segmentParser_.tagsAvailable()) { | 777 | while (this.segmentParser_.tagsAvailable()) { |
777 | tags.push(this.segmentParser_.getNextTag()); | 778 | tags.push(this.segmentParser_.getNextTag()); |
778 | } | 779 | } |
779 | 780 | ||
781 | // This block of code uses the presentation timestamp of the ts segment to calculate its exact duration, since this | ||
782 | // may differ by fractions of a second from what is reported. Using the exact, calculated 'preciseDuration' allows | ||
783 | // for smoother seeking and calculation of the total playlist duration, which previously (especially in short videos) | ||
784 | // was reported erroneously and made the play head overrun the end of the progress bar. | ||
785 | if (tags.length > 0) { | ||
786 | segment.preciseTimestamp = tags[tags.length - 1].pts; | ||
787 | |||
788 | if (playlist.segments[mediaIndex - 1]) { | ||
789 | if (playlist.segments[mediaIndex - 1].preciseTimestamp) { | ||
790 | durationOffset = playlist.segments[mediaIndex - 1].preciseTimestamp; | ||
791 | } else { | ||
792 | durationOffset = (playlist.targetDuration * (mediaIndex - 1) + playlist.segments[mediaIndex - 1].duration) * 1000; | ||
793 | } | ||
794 | segment.preciseDuration = (segment.preciseTimestamp - durationOffset) / 1000; | ||
795 | } else if (mediaIndex === 0) { | ||
796 | segment.preciseDuration = segment.preciseTimestamp / 1000; | ||
797 | } | ||
798 | } | ||
799 | |||
800 | this.updateDuration(this.playlists.media()); | ||
801 | |||
780 | // if we're refilling the buffer after a seek, scan through the muxed | 802 | // if we're refilling the buffer after a seek, scan through the muxed |
781 | // FLV tags until we find the one that is closest to the desired | 803 | // FLV tags until we find the one that is closest to the desired |
782 | // playback time | 804 | // playback time |
... | @@ -948,7 +970,7 @@ videojs.Hls.getPlaylistDuration = function(playlist, startIndex, endIndex) { | ... | @@ -948,7 +970,7 @@ videojs.Hls.getPlaylistDuration = function(playlist, startIndex, endIndex) { |
948 | 970 | ||
949 | for (; i >= startIndex; i--) { | 971 | for (; i >= startIndex; i--) { |
950 | segment = playlist.segments[i]; | 972 | segment = playlist.segments[i]; |
951 | dur += (segment.duration !== undefined ? segment.duration : playlist.targetDuration) || 0; | 973 | dur += segment.preciseDuration || segment.duration || playlist.targetDuration || 0; |
952 | } | 974 | } |
953 | 975 | ||
954 | return dur; | 976 | return dur; | ... | ... |
... | @@ -269,7 +269,7 @@ test('sets the duration if one is available on the playlist', function() { | ... | @@ -269,7 +269,7 @@ test('sets the duration if one is available on the playlist', function() { |
269 | standardXHRResponse(requests[0]); | 269 | standardXHRResponse(requests[0]); |
270 | strictEqual(calls, 1, 'duration is set'); | 270 | strictEqual(calls, 1, 'duration is set'); |
271 | standardXHRResponse(requests[1]); | 271 | standardXHRResponse(requests[1]); |
272 | strictEqual(calls, 1, 'duration is set'); | 272 | strictEqual(calls, 2, 'duration is set'); |
273 | }); | 273 | }); |
274 | 274 | ||
275 | test('calculates the duration if needed', function() { | 275 | test('calculates the duration if needed', function() { |
... | @@ -1063,6 +1063,37 @@ test('flushes the parser after each segment', function() { | ... | @@ -1063,6 +1063,37 @@ test('flushes the parser after each segment', function() { |
1063 | strictEqual(flushes, 1, 'tags are flushed at the end of a segment'); | 1063 | strictEqual(flushes, 1, 'tags are flushed at the end of a segment'); |
1064 | }); | 1064 | }); |
1065 | 1065 | ||
1066 | test('calculates preciseTimestamp and preciseDuration for a new segment', function() { | ||
1067 | // mock out the segment parser | ||
1068 | videojs.Hls.SegmentParser = function() { | ||
1069 | var tagsAvailable = true, | ||
1070 | tag = { pts : 200000 }; | ||
1071 | this.getFlvHeader = function() { | ||
1072 | return []; | ||
1073 | }; | ||
1074 | this.parseSegmentBinaryData = function() {}; | ||
1075 | this.flushTags = function() {}; | ||
1076 | this.tagsAvailable = function() { return tagsAvailable; }; | ||
1077 | this.getNextTag = function() { tagsAvailable = false; return tag; }; | ||
1078 | this.metadataStream = { | ||
1079 | on: Function.prototype | ||
1080 | }; | ||
1081 | }; | ||
1082 | |||
1083 | player.src({ | ||
1084 | src: 'manifest/media.m3u8', | ||
1085 | type: 'application/vnd.apple.mpegurl' | ||
1086 | }); | ||
1087 | openMediaSource(player); | ||
1088 | |||
1089 | standardXHRResponse(requests[0]); | ||
1090 | strictEqual(player.duration(), 40, 'player duration is read from playlist on load'); | ||
1091 | standardXHRResponse(requests[1]); | ||
1092 | strictEqual(player.hls.playlists.media().segments[0].preciseTimestamp, 200000, 'preciseTimestamp is calculated and stored'); | ||
1093 | strictEqual(player.hls.playlists.media().segments[0].preciseDuration, 200, 'preciseDuration is calculated and stored'); | ||
1094 | strictEqual(player.duration(), 230, 'player duration is calculated using preciseDuration'); | ||
1095 | }); | ||
1096 | |||
1066 | test('exposes in-band metadata events as cues', function() { | 1097 | test('exposes in-band metadata events as cues', function() { |
1067 | var track; | 1098 | var track; |
1068 | player.src({ | 1099 | player.src({ | ... | ... |
-
Please register or sign in to post a comment