e3c5271b by David LaPalomento

Merge pull request #473 from videojs/firefox-live

Firefox live
2 parents 7462bdce 2286959f
...@@ -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,37 +1174,12 @@ videojs.HlsHandler.prototype.drainBuffer = function(event) { ...@@ -1144,37 +1174,12 @@ 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 }
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 } 1181 }
1175 1182
1176 this.pendingSegment_.buffered = this.tech_.buffered();
1177
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);
1180 }; 1185 };
......
...@@ -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
......