Merge pull request #473 from videojs/firefox-live
Firefox live
Showing
3 changed files
with
54 additions
and
38 deletions
... | @@ -360,6 +360,10 @@ | ... | @@ -360,6 +360,10 @@ |
360 | return; | 360 | return; |
361 | } | 361 | } |
362 | 362 | ||
363 | if (update.uri !== outdated.uri) { | ||
364 | return; | ||
365 | } | ||
366 | |||
363 | // try using precise timing from first segment of the updated | 367 | // try using precise timing from first segment of the updated |
364 | // playlist | 368 | // playlist |
365 | if (update.segments.length) { | 369 | if (update.segments.length) { | ... | ... |
... | @@ -165,6 +165,8 @@ videojs.HlsHandler.prototype.src = function(src) { | ... | @@ -165,6 +165,8 @@ videojs.HlsHandler.prototype.src = function(src) { |
165 | } | 165 | } |
166 | this.playlists = new videojs.Hls.PlaylistLoader(this.source_.src, this.options_.withCredentials); | 166 | this.playlists = new videojs.Hls.PlaylistLoader(this.source_.src, this.options_.withCredentials); |
167 | 167 | ||
168 | this.tech_.on('canplay', this.setupFirstPlay.bind(this)); | ||
169 | |||
168 | this.playlists.on('loadedmetadata', function() { | 170 | this.playlists.on('loadedmetadata', function() { |
169 | oldMediaPlaylist = this.playlists.media(); | 171 | oldMediaPlaylist = this.playlists.media(); |
170 | 172 | ||
... | @@ -422,7 +424,11 @@ videojs.HlsHandler.prototype.setupFirstPlay = function() { | ... | @@ -422,7 +424,11 @@ videojs.HlsHandler.prototype.setupFirstPlay = function() { |
422 | this.sourceBuffer && | 424 | this.sourceBuffer && |
423 | 425 | ||
424 | // 4) the active media playlist is available | 426 | // 4) the active media playlist is available |
425 | media) { | 427 | media && |
428 | |||
429 | // 5) the video element or flash player is in a readyState of | ||
430 | // at least HAVE_FUTURE_DATA | ||
431 | this.tech_.readyState >= 3) { | ||
426 | 432 | ||
427 | // seek to the latest media position for live videos | 433 | // seek to the latest media position for live videos |
428 | seekable = this.seekable(); | 434 | seekable = this.seekable(); |
... | @@ -819,11 +825,14 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) { | ... | @@ -819,11 +825,14 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) { |
819 | var | 825 | var |
820 | tech = this.tech_, | 826 | tech = this.tech_, |
821 | currentTime = tech.currentTime(), | 827 | currentTime = tech.currentTime(), |
828 | hasBufferedContent = (this.tech_.buffered().length !== 0), | ||
822 | currentBuffered = this.findBufferedRange_(), | 829 | currentBuffered = this.findBufferedRange_(), |
830 | outsideBufferedRanges = !(currentBuffered && currentBuffered.length), | ||
823 | currentBufferedEnd = 0, | 831 | currentBufferedEnd = 0, |
824 | bufferedTime = 0, | 832 | bufferedTime = 0, |
825 | segment, | 833 | segment, |
826 | segmentInfo; | 834 | segmentInfo, |
835 | segmentTimestampOffset; | ||
827 | 836 | ||
828 | // if preload is set to "none", do not download segments until playback is requested | 837 | // if preload is set to "none", do not download segments until playback is requested |
829 | if (this.loadingState_ !== 'segments') { | 838 | if (this.loadingState_ !== 'segments') { |
... | @@ -906,9 +915,35 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) { | ... | @@ -906,9 +915,35 @@ videojs.HlsHandler.prototype.fillBuffer = function(mediaIndex) { |
906 | // the state of the buffer before a segment is appended will be | 915 | // the state of the buffer before a segment is appended will be |
907 | // stored here so that the actual segment duration can be | 916 | // stored here so that the actual segment duration can be |
908 | // determined after it has been appended | 917 | // determined after it has been appended |
909 | buffered: null | 918 | buffered: null, |
919 | // The target timestampOffset for this segment when we append it | ||
920 | // to the source buffer | ||
921 | timestampOffset: null | ||
910 | }; | 922 | }; |
911 | 923 | ||
924 | if (mediaIndex > 0) { | ||
925 | segmentTimestampOffset = videojs.Hls.Playlist.duration(segmentInfo.playlist, | ||
926 | segmentInfo.playlist.mediaSequence + mediaIndex) + this.playlists.expired_; | ||
927 | } | ||
928 | |||
929 | if (this.tech_.seeking() && outsideBufferedRanges) { | ||
930 | // If there are discontinuities in the playlist, we can't be sure of anything | ||
931 | // related to time so we reset the timestamp offset and start appending data | ||
932 | // anew on every seek | ||
933 | if (segmentInfo.playlist.discontinuityStarts.length) { | ||
934 | segmentInfo.timestampOffset = segmentTimestampOffset; | ||
935 | } | ||
936 | } else if (segment.discontinuity && currentBuffered.length) { | ||
937 | // If we aren't seeking and are crossing a discontinuity, we should set | ||
938 | // timestampOffset for new segments to be appended the end of the current | ||
939 | // buffered time-range | ||
940 | segmentInfo.timestampOffset = currentBuffered.end(0); | ||
941 | } else if (!hasBufferedContent && this.tech_.currentTime() > 0.05) { | ||
942 | // If we are trying to play at a position that is not zero but we aren't | ||
943 | // currently seeking according to the video element | ||
944 | segmentInfo.timestampOffset = segmentTimestampOffset; | ||
945 | } | ||
946 | |||
912 | this.loadSegment(segmentInfo); | 947 | this.loadSegment(segmentInfo); |
913 | }; | 948 | }; |
914 | 949 | ||
... | @@ -1068,7 +1103,7 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { | ... | @@ -1068,7 +1103,7 @@ videojs.HlsHandler.prototype.loadSegment = function(segmentInfo) { |
1068 | 1103 | ||
1069 | }; | 1104 | }; |
1070 | 1105 | ||
1071 | videojs.HlsHandler.prototype.drainBuffer = function(event) { | 1106 | videojs.HlsHandler.prototype.drainBuffer = function() { |
1072 | var | 1107 | var |
1073 | segmentInfo, | 1108 | segmentInfo, |
1074 | mediaIndex, | 1109 | mediaIndex, |
... | @@ -1077,11 +1112,7 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { | ... | @@ -1077,11 +1112,7 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { |
1077 | bytes, | 1112 | bytes, |
1078 | segment, | 1113 | segment, |
1079 | decrypter, | 1114 | decrypter, |
1080 | segIv, | 1115 | segIv; |
1081 | segmentTimestampOffset = 0, | ||
1082 | hasBufferedContent = (this.tech_.buffered().length !== 0), | ||
1083 | currentBuffered = this.findBufferedRange_(), | ||
1084 | outsideBufferedRanges = !(currentBuffered && currentBuffered.length); | ||
1085 | 1116 | ||
1086 | // if the buffer is empty or the source buffer hasn't been created | 1117 | // if the buffer is empty or the source buffer hasn't been created |
1087 | // yet, do nothing | 1118 | // yet, do nothing |
... | @@ -1109,7 +1140,6 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { | ... | @@ -1109,7 +1140,6 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { |
1109 | segment = playlist.segments[mediaIndex]; | 1140 | segment = playlist.segments[mediaIndex]; |
1110 | 1141 | ||
1111 | if (segment.key && !bytes) { | 1142 | if (segment.key && !bytes) { |
1112 | |||
1113 | // this is an encrypted segment | 1143 | // this is an encrypted segment |
1114 | // if the key download failed, we want to skip this segment | 1144 | // if the key download failed, we want to skip this segment |
1115 | // but if the key hasn't downloaded yet, we want to try again later | 1145 | // but if the key hasn't downloaded yet, we want to try again later |
... | @@ -1144,36 +1174,11 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { | ... | @@ -1144,36 +1174,11 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { |
1144 | } | 1174 | } |
1145 | } | 1175 | } |
1146 | 1176 | ||
1147 | event = event || {}; | 1177 | this.pendingSegment_.buffered = this.tech_.buffered(); |
1148 | |||
1149 | if (segmentInfo.mediaIndex > 0) { | ||
1150 | segmentTimestampOffset = videojs.Hls.Playlist.duration(segmentInfo.playlist, | ||
1151 | playlist.mediaSequence + segmentInfo.mediaIndex); | ||
1152 | } | ||
1153 | 1178 | ||
1154 | // If we have seeked into a non-buffered time-range, remove all buffered | 1179 | if (segmentInfo.timestampOffset !== null) { |
1155 | // time-ranges because they could have been incorrectly placed originally | 1180 | this.sourceBuffer.timestampOffset = segmentInfo.timestampOffset; |
1156 | if (this.tech_.seeking() && outsideBufferedRanges) { | ||
1157 | // If there are discontinuities in the playlist, we can't be sure of anything | ||
1158 | // related to time so we reset the timestamp offset and start appending data | ||
1159 | // anew on every seek | ||
1160 | if (segmentInfo.playlist.discontinuityStarts.length) { | ||
1161 | // Now that the forward buffer is clear, we have to set timestamp offset to | ||
1162 | // the start of the buffered region | ||
1163 | this.sourceBuffer.timestampOffset = segmentTimestampOffset; | ||
1164 | } | 1181 | } |
1165 | } else if (segment.discontinuity && currentBuffered.length) { | ||
1166 | // If we aren't seeking and are crossing a discontinuity, we should set | ||
1167 | // timestampOffset for new segments to be appended the end of the current | ||
1168 | // buffered time-range | ||
1169 | this.sourceBuffer.timestampOffset = currentBuffered.end(0); | ||
1170 | } else if (!hasBufferedContent && this.tech_.currentTime() > 0.05) { | ||
1171 | // If we are trying to play at a position that is not zero but we aren't | ||
1172 | // currently seeking according to the video element | ||
1173 | this.sourceBuffer.timestampOffset = segmentTimestampOffset; | ||
1174 | } | ||
1175 | |||
1176 | this.pendingSegment_.buffered = this.tech_.buffered(); | ||
1177 | 1182 | ||
1178 | // the segment is asynchronously added to the current buffered data | 1183 | // the segment is asynchronously added to the current buffered data |
1179 | this.sourceBuffer.appendBuffer(bytes); | 1184 | this.sourceBuffer.appendBuffer(bytes); | ... | ... |
... | @@ -335,6 +335,8 @@ test('autoplay seeks to the live point after playlist load', function() { | ... | @@ -335,6 +335,8 @@ test('autoplay seeks to the live point after playlist load', function() { |
335 | type: 'application/vnd.apple.mpegurl' | 335 | type: 'application/vnd.apple.mpegurl' |
336 | }); | 336 | }); |
337 | openMediaSource(player); | 337 | openMediaSource(player); |
338 | player.tech_.readyState = 3; | ||
339 | player.tech_.trigger('play'); | ||
338 | standardXHRResponse(requests.shift()); | 340 | standardXHRResponse(requests.shift()); |
339 | clock.tick(1); | 341 | clock.tick(1); |
340 | 342 | ||
... | @@ -355,6 +357,8 @@ test('autoplay seeks to the live point after media source open', function() { | ... | @@ -355,6 +357,8 @@ test('autoplay seeks to the live point after media source open', function() { |
355 | clock.tick(1); | 357 | clock.tick(1); |
356 | standardXHRResponse(requests.shift()); | 358 | standardXHRResponse(requests.shift()); |
357 | openMediaSource(player); | 359 | openMediaSource(player); |
360 | player.tech_.readyState = 3; | ||
361 | player.tech_.trigger('play'); | ||
358 | clock.tick(1); | 362 | clock.tick(1); |
359 | 363 | ||
360 | notEqual(currentTime, 0, 'seeked on autoplay'); | 364 | notEqual(currentTime, 0, 'seeked on autoplay'); |
... | @@ -406,6 +410,7 @@ test('calls `remove` on sourceBuffer to when loading a live segment', function() | ... | @@ -406,6 +410,7 @@ test('calls `remove` on sourceBuffer to when loading a live segment', function() |
406 | player.tech_.hls.playlists.trigger('loadedmetadata'); | 410 | player.tech_.hls.playlists.trigger('loadedmetadata'); |
407 | player.tech_.trigger('canplay'); | 411 | player.tech_.trigger('canplay'); |
408 | player.tech_.paused = function() { return false; }; | 412 | player.tech_.paused = function() { return false; }; |
413 | player.tech_.readyState = 3; | ||
409 | player.tech_.trigger('play'); | 414 | player.tech_.trigger('play'); |
410 | 415 | ||
411 | clock.tick(1); | 416 | clock.tick(1); |
... | @@ -1683,6 +1688,7 @@ test('live playlist starts three target durations before live', function() { | ... | @@ -1683,6 +1688,7 @@ test('live playlist starts three target durations before live', function() { |
1683 | equal(requests.length, 0, 'no outstanding segment request'); | 1688 | equal(requests.length, 0, 'no outstanding segment request'); |
1684 | 1689 | ||
1685 | player.tech_.paused = function() { return false; }; | 1690 | player.tech_.paused = function() { return false; }; |
1691 | player.tech_.readyState = 3; | ||
1686 | player.tech_.trigger('play'); | 1692 | player.tech_.trigger('play'); |
1687 | clock.tick(1); | 1693 | clock.tick(1); |
1688 | mediaPlaylist = player.tech_.hls.playlists.media(); | 1694 | mediaPlaylist = player.tech_.hls.playlists.media(); |
... | @@ -1703,6 +1709,7 @@ test('live playlist starts with correct currentTime value', function() { | ... | @@ -1703,6 +1709,7 @@ test('live playlist starts with correct currentTime value', function() { |
1703 | player.tech_.hls.playlists.trigger('loadedmetadata'); | 1709 | player.tech_.hls.playlists.trigger('loadedmetadata'); |
1704 | 1710 | ||
1705 | player.tech_.paused = function() { return false; }; | 1711 | player.tech_.paused = function() { return false; }; |
1712 | player.tech_.readyState = 3; | ||
1706 | player.tech_.trigger('play'); | 1713 | player.tech_.trigger('play'); |
1707 | clock.tick(1); | 1714 | clock.tick(1); |
1708 | 1715 | ... | ... |
-
Please register or sign in to post a comment