Ensure overlapping buffered edges are not interpreted as updates
If the old and new buffered ranges have a shared start or end point, that edge should not be interpreted as a new buffered boundary. Fix up a number of the tests. Some tests are still failing.
Showing
4 changed files
with
150 additions
and
297 deletions
... | @@ -187,20 +187,20 @@ | ... | @@ -187,20 +187,20 @@ |
187 | * active media playlist. When called with a single argument, | 187 | * active media playlist. When called with a single argument, |
188 | * triggers the playlist loader to asynchronously switch to the | 188 | * triggers the playlist loader to asynchronously switch to the |
189 | * specified media playlist. Calling this method while the | 189 | * specified media playlist. Calling this method while the |
190 | * loader is in the HAVE_NOTHING or HAVE_MASTER states causes an | 190 | * loader is in the HAVE_NOTHING causes an error to be emitted |
191 | * error to be emitted but otherwise has no effect. | 191 | * but otherwise has no effect. |
192 | * @param playlist (optional) {object} the parsed media playlist | 192 | * @param playlist (optional) {object} the parsed media playlist |
193 | * object to switch to | 193 | * object to switch to |
194 | */ | 194 | */ |
195 | loader.media = function(playlist) { | 195 | loader.media = function(playlist) { |
196 | var mediaChange = false; | 196 | var startingState = loader.state, mediaChange; |
197 | // getter | 197 | // getter |
198 | if (!playlist) { | 198 | if (!playlist) { |
199 | return loader.media_; | 199 | return loader.media_; |
200 | } | 200 | } |
201 | 201 | ||
202 | // setter | 202 | // setter |
203 | if (loader.state === 'HAVE_NOTHING' || loader.state === 'HAVE_MASTER') { | 203 | if (loader.state === 'HAVE_NOTHING') { |
204 | throw new Error('Cannot switch media playlist from ' + loader.state); | 204 | throw new Error('Cannot switch media playlist from ' + loader.state); |
205 | } | 205 | } |
206 | 206 | ||
... | @@ -213,7 +213,7 @@ | ... | @@ -213,7 +213,7 @@ |
213 | playlist = loader.master.playlists[playlist]; | 213 | playlist = loader.master.playlists[playlist]; |
214 | } | 214 | } |
215 | 215 | ||
216 | mediaChange = playlist.uri !== loader.media_.uri; | 216 | mediaChange = !loader.media_ || playlist.uri !== loader.media_.uri; |
217 | 217 | ||
218 | // switch to fully loaded playlists immediately | 218 | // switch to fully loaded playlists immediately |
219 | if (loader.master.playlists[playlist.uri].endList) { | 219 | if (loader.master.playlists[playlist.uri].endList) { |
... | @@ -258,7 +258,17 @@ | ... | @@ -258,7 +258,17 @@ |
258 | withCredentials: withCredentials | 258 | withCredentials: withCredentials |
259 | }, function(error, request) { | 259 | }, function(error, request) { |
260 | haveMetadata(error, request, playlist.uri); | 260 | haveMetadata(error, request, playlist.uri); |
261 | |||
262 | if (error) { | ||
263 | return; | ||
264 | } | ||
265 | |||
266 | // fire loadedmetadata the first time a media playlist is loaded | ||
267 | if (startingState === 'HAVE_MASTER') { | ||
268 | loader.trigger('loadedmetadata'); | ||
269 | } else { | ||
261 | loader.trigger('mediachange'); | 270 | loader.trigger('mediachange'); |
271 | } | ||
262 | }); | 272 | }); |
263 | }; | 273 | }; |
264 | 274 | ||
... | @@ -320,19 +330,13 @@ | ... | @@ -320,19 +330,13 @@ |
320 | loader.master.playlists[loader.master.playlists[i].uri] = loader.master.playlists[i]; | 330 | loader.master.playlists[loader.master.playlists[i].uri] = loader.master.playlists[i]; |
321 | } | 331 | } |
322 | 332 | ||
323 | request = xhr({ | 333 | loader.trigger('loadedplaylist'); |
324 | uri: resolveUrl(srcUrl, parser.manifest.playlists[0].uri), | 334 | if (!request) { |
325 | withCredentials: withCredentials | 335 | // no media playlist was specifically selected so start |
326 | }, function(error, request) { | 336 | // from the first listed one |
327 | // pass along the URL specified in the master playlist | 337 | loader.media(parser.manifest.playlists[0]); |
328 | haveMetadata(error, | ||
329 | request, | ||
330 | parser.manifest.playlists[0].uri); | ||
331 | if (!error) { | ||
332 | loader.trigger('loadedmetadata'); | ||
333 | } | 338 | } |
334 | }); | 339 | return; |
335 | return loader.trigger('loadedplaylist'); | ||
336 | } | 340 | } |
337 | 341 | ||
338 | // loaded a media playlist | 342 | // loaded a media playlist |
... | @@ -468,8 +472,8 @@ | ... | @@ -468,8 +472,8 @@ |
468 | } | 472 | } |
469 | 473 | ||
470 | // the playback position is outside the range of available | 474 | // the playback position is outside the range of available |
471 | // segments so return the last one | 475 | // segments so return the length |
472 | return this.media_.segments.length - 1; | 476 | return this.media_.segments.length; |
473 | }; | 477 | }; |
474 | 478 | ||
475 | videojs.Hls.PlaylistLoader = PlaylistLoader; | 479 | videojs.Hls.PlaylistLoader = PlaylistLoader; | ... | ... |
... | @@ -190,7 +190,8 @@ videojs.Hls.prototype.src = function(src) { | ... | @@ -190,7 +190,8 @@ videojs.Hls.prototype.src = function(src) { |
190 | var updatedPlaylist = this.playlists.media(); | 190 | var updatedPlaylist = this.playlists.media(); |
191 | 191 | ||
192 | if (!updatedPlaylist) { | 192 | if (!updatedPlaylist) { |
193 | // do nothing before an initial media playlist has been activated | 193 | // select the initial variant |
194 | this.playlists.media(this.selectPlaylist()); | ||
194 | return; | 195 | return; |
195 | } | 196 | } |
196 | 197 | ||
... | @@ -254,7 +255,7 @@ videojs.Hls.prototype.handleSourceOpen = function() { | ... | @@ -254,7 +255,7 @@ videojs.Hls.prototype.handleSourceOpen = function() { |
254 | 255 | ||
255 | // Returns the array of time range edge objects that were additively | 256 | // Returns the array of time range edge objects that were additively |
256 | // modified between two TimeRanges. | 257 | // modified between two TimeRanges. |
257 | var bufferedAdditions = function(original, update) { | 258 | videojs.Hls.bufferedAdditions_ = function(original, update) { |
258 | var result = [], edges = [], | 259 | var result = [], edges = [], |
259 | i, inOriginalRanges; | 260 | i, inOriginalRanges; |
260 | 261 | ||
... | @@ -271,6 +272,15 @@ var bufferedAdditions = function(original, update) { | ... | @@ -271,6 +272,15 @@ var bufferedAdditions = function(original, update) { |
271 | var leftTime, rightTime; | 272 | var leftTime, rightTime; |
272 | leftTime = left.start !== undefined ? left.start : left.end; | 273 | leftTime = left.start !== undefined ? left.start : left.end; |
273 | rightTime = right.start !== undefined ? right.start : right.end; | 274 | rightTime = right.start !== undefined ? right.start : right.end; |
275 | |||
276 | // when two times are equal, ensure the original edge covers the | ||
277 | // update | ||
278 | if (leftTime === rightTime) { | ||
279 | if (left.original) { | ||
280 | return left.start !== undefined ? -1 : 1; | ||
281 | } | ||
282 | return right.start !== undefined ? -1 : 1; | ||
283 | } | ||
274 | return leftTime - rightTime; | 284 | return leftTime - rightTime; |
275 | }); | 285 | }); |
276 | 286 | ||
... | @@ -349,7 +359,7 @@ videojs.Hls.prototype.setupSourceBuffer_ = function() { | ... | @@ -349,7 +359,7 @@ videojs.Hls.prototype.setupSourceBuffer_ = function() { |
349 | // annotate the segment with any start and end time information | 359 | // annotate the segment with any start and end time information |
350 | // added by the media processing | 360 | // added by the media processing |
351 | segment = segmentInfo.playlist.segments[segmentInfo.mediaIndex]; | 361 | segment = segmentInfo.playlist.segments[segmentInfo.mediaIndex]; |
352 | timelineUpdates = bufferedAdditions(segmentInfo.buffered, | 362 | timelineUpdates = videojs.Hls.bufferedAdditions_(segmentInfo.buffered, |
353 | this.tech_.buffered()); | 363 | this.tech_.buffered()); |
354 | timelineUpdates.forEach(function(update) { | 364 | timelineUpdates.forEach(function(update) { |
355 | if (update.start !== undefined) { | 365 | if (update.start !== undefined) { |
... | @@ -1112,15 +1122,16 @@ videojs.Hls.prototype.drainBuffer = function(event) { | ... | @@ -1112,15 +1122,16 @@ videojs.Hls.prototype.drainBuffer = function(event) { |
1112 | this.sourceBuffer.timestampOffset = currentBuffered.end(0); | 1122 | this.sourceBuffer.timestampOffset = currentBuffered.end(0); |
1113 | } | 1123 | } |
1114 | 1124 | ||
1115 | // the segment is asynchronously added to the current buffered data | ||
1116 | if (currentBuffered.length) { | 1125 | if (currentBuffered.length) { |
1117 | this.sourceBuffer.videoBuffer_.appendWindowStart = Math.min(this.tech_.currentTime(), currentBuffered.end(0)); | 1126 | // Chrome 45 stalls if appends overlap the playhead |
1118 | } else if (this.sourceBuffer.videoBuffer_) { | 1127 | this.sourceBuffer.appendWindowStart = Math.min(this.tech_.currentTime(), currentBuffered.end(0)); |
1119 | this.sourceBuffer.videoBuffer_.appendWindowStart = 0; | 1128 | } else { |
1129 | this.sourceBuffer.appendWindowStart = 0; | ||
1120 | } | 1130 | } |
1121 | this.pendingSegment_ = segmentBuffer.shift(); | 1131 | this.pendingSegment_ = segmentBuffer.shift(); |
1122 | this.pendingSegment_.buffered = this.tech_.buffered(); | 1132 | this.pendingSegment_.buffered = this.tech_.buffered(); |
1123 | 1133 | ||
1134 | // the segment is asynchronously added to the current buffered data | ||
1124 | this.sourceBuffer.appendBuffer(bytes); | 1135 | this.sourceBuffer.appendBuffer(bytes); |
1125 | }; | 1136 | }; |
1126 | 1137 | ... | ... |
... | @@ -69,13 +69,16 @@ | ... | @@ -69,13 +69,16 @@ |
69 | }); | 69 | }); |
70 | 70 | ||
71 | test('moves to HAVE_MASTER after loading a master playlist', function() { | 71 | test('moves to HAVE_MASTER after loading a master playlist', function() { |
72 | var loader = new videojs.Hls.PlaylistLoader('master.m3u8'); | 72 | var loader = new videojs.Hls.PlaylistLoader('master.m3u8'), state; |
73 | loader.on('loadedplaylist', function() { | ||
74 | state = loader.state; | ||
75 | }); | ||
73 | requests.pop().respond(200, null, | 76 | requests.pop().respond(200, null, |
74 | '#EXTM3U\n' + | 77 | '#EXTM3U\n' + |
75 | '#EXT-X-STREAM-INF:\n' + | 78 | '#EXT-X-STREAM-INF:\n' + |
76 | 'media.m3u8\n'); | 79 | 'media.m3u8\n'); |
77 | ok(loader.master, 'the master playlist is available'); | 80 | ok(loader.master, 'the master playlist is available'); |
78 | strictEqual(loader.state, 'HAVE_MASTER', 'the state is correct'); | 81 | strictEqual(state, 'HAVE_MASTER', 'the state at loadedplaylist correct'); |
79 | }); | 82 | }); |
80 | 83 | ||
81 | test('jumps to HAVE_METADATA when initialized with a media playlist', function() { | 84 | test('jumps to HAVE_METADATA when initialized with a media playlist', function() { |
... | @@ -453,6 +456,20 @@ | ... | @@ -453,6 +456,20 @@ |
453 | 'updated the active media'); | 456 | 'updated the active media'); |
454 | }); | 457 | }); |
455 | 458 | ||
459 | test('can switch playlists immediately after the master is downloaded', function() { | ||
460 | var loader = new videojs.Hls.PlaylistLoader('master.m3u8'); | ||
461 | loader.on('loadedplaylist', function() { | ||
462 | loader.media('high.m3u8'); | ||
463 | }); | ||
464 | requests.pop().respond(200, null, | ||
465 | '#EXTM3U\n' + | ||
466 | '#EXT-X-STREAM-INF:BANDWIDTH=1\n' + | ||
467 | 'low.m3u8\n' + | ||
468 | '#EXT-X-STREAM-INF:BANDWIDTH=2\n' + | ||
469 | 'high.m3u8\n'); | ||
470 | equal(requests[0].url, urlTo('high.m3u8'), 'switched variants immediately'); | ||
471 | }); | ||
472 | |||
456 | test('can switch media playlists based on URI', function() { | 473 | test('can switch media playlists based on URI', function() { |
457 | var loader = new videojs.Hls.PlaylistLoader('master.m3u8'); | 474 | var loader = new videojs.Hls.PlaylistLoader('master.m3u8'); |
458 | requests.pop().respond(200, null, | 475 | requests.pop().respond(200, null, |
... | @@ -624,9 +641,6 @@ | ... | @@ -624,9 +641,6 @@ |
624 | 'low.m3u8\n' + | 641 | 'low.m3u8\n' + |
625 | '#EXT-X-STREAM-INF:BANDWIDTH=2\n' + | 642 | '#EXT-X-STREAM-INF:BANDWIDTH=2\n' + |
626 | 'high.m3u8\n'); | 643 | 'high.m3u8\n'); |
627 | throws(function() { | ||
628 | loader.media('high.m3u8'); | ||
629 | }, 'throws an error from HAVE_MASTER'); | ||
630 | }); | 644 | }); |
631 | 645 | ||
632 | test('throws an error if a switch to an unrecognized playlist is requested', function() { | 646 | test('throws an error if a switch to an unrecognized playlist is requested', function() { |
... | @@ -757,10 +771,8 @@ | ... | @@ -757,10 +771,8 @@ |
757 | '#EXTINF:5,\n' + | 771 | '#EXTINF:5,\n' + |
758 | '1.ts\n' + | 772 | '1.ts\n' + |
759 | '#EXT-X-ENDLIST\n'); | 773 | '#EXT-X-ENDLIST\n'); |
760 | equal(loader.getMediaIndexForTime_(4), 0, 'rounds down exact matches'); | 774 | equal(loader.getMediaIndexForTime_(4), 1, 'rounds up exact matches'); |
761 | equal(loader.getMediaIndexForTime_(3.7), 0, 'rounds down'); | 775 | equal(loader.getMediaIndexForTime_(3.7), 0, 'rounds down'); |
762 | // FIXME: the test below should pass for HLSv3 | ||
763 | //equal(loader.getMediaIndexForTime_(4.2), 0, 'rounds down'); | ||
764 | equal(loader.getMediaIndexForTime_(4.5), 1, 'rounds up at 0.5'); | 776 | equal(loader.getMediaIndexForTime_(4.5), 1, 'rounds up at 0.5'); |
765 | }); | 777 | }); |
766 | 778 | ... | ... |
... | @@ -686,73 +686,59 @@ test('downloads media playlists after loading the master', function() { | ... | @@ -686,73 +686,59 @@ test('downloads media playlists after loading the master', function() { |
686 | }); | 686 | }); |
687 | openMediaSource(player); | 687 | openMediaSource(player); |
688 | 688 | ||
689 | // set bandwidth to an appropriate number so we don't switch | 689 | player.tech_.hls.bandwidth = 20e10; |
690 | player.tech_.hls.bandwidth = 200000; | ||
691 | standardXHRResponse(requests[0]); | 690 | standardXHRResponse(requests[0]); |
692 | standardXHRResponse(requests[1]); | 691 | standardXHRResponse(requests[1]); |
693 | standardXHRResponse(requests[2]); | 692 | standardXHRResponse(requests[2]); |
694 | 693 | ||
695 | strictEqual(requests[0].url, 'manifest/master.m3u8', 'master playlist requested'); | 694 | strictEqual(requests[0].url, 'manifest/master.m3u8', 'master playlist requested'); |
696 | strictEqual(requests[1].url, | 695 | strictEqual(requests[1].url, |
697 | absoluteUrl('manifest/media.m3u8'), | 696 | absoluteUrl('manifest/media3.m3u8'), |
698 | 'media playlist requested'); | 697 | 'media playlist requested'); |
699 | strictEqual(requests[2].url, | 698 | strictEqual(requests[2].url, |
700 | absoluteUrl('manifest/media-00001.ts'), | 699 | absoluteUrl('manifest/media3-00001.ts'), |
701 | 'first segment requested'); | 700 | 'first segment requested'); |
702 | }); | 701 | }); |
703 | 702 | ||
704 | test('upshift if initial bandwidth is high', function() { | 703 | test('upshifts if the initial bandwidth hint is high', function() { |
705 | player.src({ | 704 | player.src({ |
706 | src: 'manifest/master.m3u8', | 705 | src: 'manifest/master.m3u8', |
707 | type: 'application/vnd.apple.mpegurl' | 706 | type: 'application/vnd.apple.mpegurl' |
708 | }); | 707 | }); |
709 | openMediaSource(player); | 708 | openMediaSource(player); |
710 | 709 | ||
710 | player.tech_.hls.bandwidth = 10e20; | ||
711 | standardXHRResponse(requests[0]); | 711 | standardXHRResponse(requests[0]); |
712 | |||
713 | player.tech_.hls.playlists.setBandwidth = function() { | ||
714 | player.tech_.hls.playlists.bandwidth = 1000000000; | ||
715 | }; | ||
716 | |||
717 | standardXHRResponse(requests[1]); | 712 | standardXHRResponse(requests[1]); |
718 | standardXHRResponse(requests[2]); | 713 | standardXHRResponse(requests[2]); |
719 | 714 | ||
720 | standardXHRResponse(requests[3]); | ||
721 | |||
722 | strictEqual(requests[0].url, 'manifest/master.m3u8', 'master playlist requested'); | 715 | strictEqual(requests[0].url, 'manifest/master.m3u8', 'master playlist requested'); |
723 | strictEqual(requests[1].url, | 716 | strictEqual(requests[1].url, |
724 | absoluteUrl('manifest/media.m3u8'), | ||
725 | 'media playlist requested'); | ||
726 | strictEqual(requests[2].url, | ||
727 | absoluteUrl('manifest/media3.m3u8'), | 717 | absoluteUrl('manifest/media3.m3u8'), |
728 | 'media playlist requested'); | 718 | 'media playlist requested'); |
729 | strictEqual(requests[3].url, | 719 | strictEqual(requests[2].url, |
730 | absoluteUrl('manifest/media3-00001.ts'), | 720 | absoluteUrl('manifest/media3-00001.ts'), |
731 | 'first segment requested'); | 721 | 'first segment requested'); |
732 | }); | 722 | }); |
733 | 723 | ||
734 | test('dont downshift if bandwidth is low', function() { | 724 | test('downshifts if the initial bandwidth hint is low', function() { |
735 | player.src({ | 725 | player.src({ |
736 | src: 'manifest/master.m3u8', | 726 | src: 'manifest/master.m3u8', |
737 | type: 'application/vnd.apple.mpegurl' | 727 | type: 'application/vnd.apple.mpegurl' |
738 | }); | 728 | }); |
739 | openMediaSource(player); | 729 | openMediaSource(player); |
740 | 730 | ||
731 | player.tech_.hls.bandwidth = 100; | ||
741 | standardXHRResponse(requests[0]); | 732 | standardXHRResponse(requests[0]); |
742 | |||
743 | player.tech_.hls.playlists.setBandwidth = function() { | ||
744 | player.tech_.hls.playlists.bandwidth = 100; | ||
745 | }; | ||
746 | |||
747 | standardXHRResponse(requests[1]); | 733 | standardXHRResponse(requests[1]); |
748 | standardXHRResponse(requests[2]); | 734 | standardXHRResponse(requests[2]); |
749 | 735 | ||
750 | strictEqual(requests[0].url, 'manifest/master.m3u8', 'master playlist requested'); | 736 | strictEqual(requests[0].url, 'manifest/master.m3u8', 'master playlist requested'); |
751 | strictEqual(requests[1].url, | 737 | strictEqual(requests[1].url, |
752 | absoluteUrl('manifest/media.m3u8'), | 738 | absoluteUrl('manifest/media1.m3u8'), |
753 | 'media playlist requested'); | 739 | 'media playlist requested'); |
754 | strictEqual(requests[2].url, | 740 | strictEqual(requests[2].url, |
755 | absoluteUrl('manifest/media-00001.ts'), | 741 | absoluteUrl('manifest/media1-00001.ts'), |
756 | 'first segment requested'); | 742 | 'first segment requested'); |
757 | }); | 743 | }); |
758 | 744 | ||
... | @@ -822,7 +808,7 @@ test('buffer checks are noops when only the master is ready', function() { | ... | @@ -822,7 +808,7 @@ test('buffer checks are noops when only the master is ready', function() { |
822 | 808 | ||
823 | strictEqual(1, requests.length, 'one request was made'); | 809 | strictEqual(1, requests.length, 'one request was made'); |
824 | strictEqual(requests[0].url, | 810 | strictEqual(requests[0].url, |
825 | absoluteUrl('manifest/media.m3u8'), | 811 | absoluteUrl('manifest/media1.m3u8'), |
826 | 'media playlist requested'); | 812 | 'media playlist requested'); |
827 | }); | 813 | }); |
828 | 814 | ||
... | @@ -876,11 +862,9 @@ test('selects a playlist after segment downloads', function() { | ... | @@ -876,11 +862,9 @@ test('selects a playlist after segment downloads', function() { |
876 | return player.tech_.hls.playlists.master.playlists[0]; | 862 | return player.tech_.hls.playlists.master.playlists[0]; |
877 | }; | 863 | }; |
878 | 864 | ||
879 | standardXHRResponse(requests[0]); | 865 | standardXHRResponse(requests[0]); // master |
880 | 866 | standardXHRResponse(requests[1]); // media | |
881 | player.tech_.hls.bandwidth = 3000000; | 867 | standardXHRResponse(requests[2]); // segment |
882 | standardXHRResponse(requests[1]); | ||
883 | standardXHRResponse(requests[2]); | ||
884 | 868 | ||
885 | strictEqual(calls, 2, 'selects after the initial segment'); | 869 | strictEqual(calls, 2, 'selects after the initial segment'); |
886 | player.currentTime = function() { | 870 | player.currentTime = function() { |
... | @@ -889,6 +873,7 @@ test('selects a playlist after segment downloads', function() { | ... | @@ -889,6 +873,7 @@ test('selects a playlist after segment downloads', function() { |
889 | player.buffered = function() { | 873 | player.buffered = function() { |
890 | return videojs.createTimeRange(0, 2); | 874 | return videojs.createTimeRange(0, 2); |
891 | }; | 875 | }; |
876 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
892 | player.tech_.hls.checkBuffer_(); | 877 | player.tech_.hls.checkBuffer_(); |
893 | 878 | ||
894 | standardXHRResponse(requests[3]); | 879 | standardXHRResponse(requests[3]); |
... | @@ -896,9 +881,7 @@ test('selects a playlist after segment downloads', function() { | ... | @@ -896,9 +881,7 @@ test('selects a playlist after segment downloads', function() { |
896 | strictEqual(calls, 3, 'selects after additional segments'); | 881 | strictEqual(calls, 3, 'selects after additional segments'); |
897 | }); | 882 | }); |
898 | 883 | ||
899 | test('moves to the next segment if there is a network error', function() { | 884 | test('reports an error if a segment is unreachable', function() { |
900 | var mediaIndex; | ||
901 | |||
902 | player.src({ | 885 | player.src({ |
903 | src: 'manifest/master.m3u8', | 886 | src: 'manifest/master.m3u8', |
904 | type: 'application/vnd.apple.mpegurl' | 887 | type: 'application/vnd.apple.mpegurl' |
... | @@ -909,62 +892,8 @@ test('moves to the next segment if there is a network error', function() { | ... | @@ -909,62 +892,8 @@ test('moves to the next segment if there is a network error', function() { |
909 | standardXHRResponse(requests[0]); | 892 | standardXHRResponse(requests[0]); |
910 | standardXHRResponse(requests[1]); | 893 | standardXHRResponse(requests[1]); |
911 | 894 | ||
912 | mediaIndex = player.tech_.hls.mediaIndex; | ||
913 | player.trigger('timeupdate'); | ||
914 | |||
915 | requests[2].respond(400); | 895 | requests[2].respond(400); |
916 | strictEqual(mediaIndex + 1, player.tech_.hls.mediaIndex, 'media index is incremented'); | 896 | strictEqual(player.tech_.hls.mediaSource.error_, 'network', 'network error is triggered'); |
917 | }); | ||
918 | |||
919 | test('updates playlist timeline offsets if it detects a desynchronization', function() { | ||
920 | var buffered = [], currentTime = 0; | ||
921 | |||
922 | player.src({ | ||
923 | src: 'manifest/master.m3u8', | ||
924 | type: 'application/vnd.apple.mpegurl' | ||
925 | }); | ||
926 | openMediaSource(player); | ||
927 | standardXHRResponse(requests.shift()); // master | ||
928 | requests.shift().respond(200, null, | ||
929 | '#EXTM3U\n' + | ||
930 | '#EXT-X-MEDIA-SEQUENCE:2\n' + | ||
931 | '#EXTINF:10,\n' + | ||
932 | '2.ts\n' + | ||
933 | '#EXTINF:10,\n' + | ||
934 | '3.ts\n'); // media | ||
935 | player.tech_.buffered = function() { return videojs.createTimeRange(buffered); }; | ||
936 | player.tech_.currentTime = function() { return currentTime; }; | ||
937 | player.tech_.paused = function() { return false; }; | ||
938 | player.tech_.trigger('play'); | ||
939 | clock.tick(1); | ||
940 | standardXHRResponse(requests.shift()); // segment 0 | ||
941 | equal(player.tech_.hls.mediaIndex, 1, 'incremented mediaIndex'); | ||
942 | |||
943 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
944 | buffered.push([0, 10]); | ||
945 | |||
946 | // force a playlist switch | ||
947 | player.tech_.hls.playlists.media('media1.m3u8'); | ||
948 | requests = requests.filter(function(request) { | ||
949 | return !request.aborted; | ||
950 | }); | ||
951 | requests.shift().respond(200, null, | ||
952 | '#EXTM3U\n' + | ||
953 | '#EXT-X-MEDIA-SEQUENCE:9999\n' + | ||
954 | '#EXTINF:10,\n' + | ||
955 | '3.ts\n' + | ||
956 | '#EXTINF:10,\n' + | ||
957 | '4.ts\n' + | ||
958 | '#EXTINF:10,\n' + | ||
959 | '5.ts\n'); // media1 | ||
960 | player.tech_.hls.checkBuffer_(); | ||
961 | standardXHRResponse(requests.shift()); | ||
962 | |||
963 | buffered.push([20, 30]); | ||
964 | currentTime = 8; | ||
965 | |||
966 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
967 | equal(player.tech_.hls.mediaIndex, 0, 'prepared to request the missing segment'); | ||
968 | }); | 897 | }); |
969 | 898 | ||
970 | test('updates the duration after switching playlists', function() { | 899 | test('updates the duration after switching playlists', function() { |
... | @@ -974,19 +903,22 @@ test('updates the duration after switching playlists', function() { | ... | @@ -974,19 +903,22 @@ test('updates the duration after switching playlists', function() { |
974 | type: 'application/vnd.apple.mpegurl' | 903 | type: 'application/vnd.apple.mpegurl' |
975 | }); | 904 | }); |
976 | openMediaSource(player); | 905 | openMediaSource(player); |
906 | |||
907 | player.tech_.hls.bandwidth = 1e20; | ||
908 | standardXHRResponse(requests[0]); // master | ||
909 | standardXHRResponse(requests[1]); // media3 | ||
910 | |||
977 | player.tech_.hls.selectPlaylist = function() { | 911 | player.tech_.hls.selectPlaylist = function() { |
978 | selectedPlaylist = true; | 912 | selectedPlaylist = true; |
979 | 913 | ||
980 | // this duraiton should be overwritten by the playlist change | 914 | // this duration should be overwritten by the playlist change |
981 | player.tech_.hls.mediaSource.duration = -Infinity; | 915 | player.tech_.hls.mediaSource.duration = -Infinity; |
982 | 916 | ||
983 | return player.tech_.hls.playlists.master.playlists[1]; | 917 | return player.tech_.hls.playlists.master.playlists[1]; |
984 | }; | 918 | }; |
985 | 919 | ||
986 | standardXHRResponse(requests[0]); | 920 | standardXHRResponse(requests[2]); // segment 0 |
987 | standardXHRResponse(requests[1]); | 921 | standardXHRResponse(requests[3]); // media1 |
988 | standardXHRResponse(requests[2]); | ||
989 | standardXHRResponse(requests[3]); | ||
990 | ok(selectedPlaylist, 'selected playlist'); | 922 | ok(selectedPlaylist, 'selected playlist'); |
991 | ok(player.tech_.hls.mediaSource.duration !== -Infinity, 'updates the duration'); | 923 | ok(player.tech_.hls.mediaSource.duration !== -Infinity, 'updates the duration'); |
992 | }); | 924 | }); |
... | @@ -1058,21 +990,6 @@ test('selects a playlist below the current bandwidth', function() { | ... | @@ -1058,21 +990,6 @@ test('selects a playlist below the current bandwidth', function() { |
1058 | 'the low bitrate stream is selected'); | 990 | 'the low bitrate stream is selected'); |
1059 | }); | 991 | }); |
1060 | 992 | ||
1061 | test('scales the bandwidth estimate for the first segment', function() { | ||
1062 | player.src({ | ||
1063 | src: 'manifest/master.m3u8', | ||
1064 | type: 'application/vnd.apple.mpegurl' | ||
1065 | }); | ||
1066 | openMediaSource(player); | ||
1067 | |||
1068 | requests[0].bandwidth = 500; | ||
1069 | requests.shift().respond(200, null, | ||
1070 | '#EXTM3U\n' + | ||
1071 | '#EXT-X-PLAYLIST-TYPE:VOD\n' + | ||
1072 | '#EXT-X-TARGETDURATION:10\n'); | ||
1073 | equal(player.tech_.hls.bandwidth, 500 * 5, 'scaled the bandwidth estimate by 5'); | ||
1074 | }); | ||
1075 | |||
1076 | test('allows initial bandwidth to be provided', function() { | 993 | test('allows initial bandwidth to be provided', function() { |
1077 | player.src({ | 994 | player.src({ |
1078 | src: 'manifest/master.m3u8', | 995 | src: 'manifest/master.m3u8', |
... | @@ -1242,6 +1159,7 @@ test('downloads the next segment if the buffer is getting low', function() { | ... | @@ -1242,6 +1159,7 @@ test('downloads the next segment if the buffer is getting low', function() { |
1242 | player.tech_.buffered = function() { | 1159 | player.tech_.buffered = function() { |
1243 | return videojs.createTimeRange(0, 19.999); | 1160 | return videojs.createTimeRange(0, 19.999); |
1244 | }; | 1161 | }; |
1162 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
1245 | player.tech_.hls.checkBuffer_(); | 1163 | player.tech_.hls.checkBuffer_(); |
1246 | 1164 | ||
1247 | standardXHRResponse(requests[2]); | 1165 | standardXHRResponse(requests[2]); |
... | @@ -1253,8 +1171,12 @@ test('downloads the next segment if the buffer is getting low', function() { | ... | @@ -1253,8 +1171,12 @@ test('downloads the next segment if the buffer is getting low', function() { |
1253 | }); | 1171 | }); |
1254 | 1172 | ||
1255 | test('buffers based on the correct TimeRange if multiple ranges exist', function() { | 1173 | test('buffers based on the correct TimeRange if multiple ranges exist', function() { |
1174 | var currentTime, buffered; | ||
1256 | player.tech_.currentTime = function() { | 1175 | player.tech_.currentTime = function() { |
1257 | return 8; | 1176 | return currentTime; |
1177 | }; | ||
1178 | player.tech_.buffered = function() { | ||
1179 | return videojs.createTimeRange(buffered); | ||
1258 | }; | 1180 | }; |
1259 | 1181 | ||
1260 | player.src({ | 1182 | player.src({ |
... | @@ -1262,37 +1184,28 @@ test('buffers based on the correct TimeRange if multiple ranges exist', function | ... | @@ -1262,37 +1184,28 @@ test('buffers based on the correct TimeRange if multiple ranges exist', function |
1262 | type: 'application/vnd.apple.mpegurl' | 1184 | type: 'application/vnd.apple.mpegurl' |
1263 | }); | 1185 | }); |
1264 | openMediaSource(player); | 1186 | openMediaSource(player); |
1265 | player.tech_.buffered = function() { | 1187 | currentTime = 8; |
1266 | return videojs.createTimeRange([[0, 10], [50, 160]]); | 1188 | buffered = [[0, 10], [20, 40]]; |
1267 | }; | ||
1268 | 1189 | ||
1269 | standardXHRResponse(requests[0]); | 1190 | standardXHRResponse(requests[0]); |
1270 | standardXHRResponse(requests[1]); | 1191 | standardXHRResponse(requests[1]); |
1271 | 1192 | ||
1272 | strictEqual(requests.length, 2, 'made two requests'); | 1193 | strictEqual(requests.length, 2, 'made two requests'); |
1273 | strictEqual(requests[1].url, | 1194 | strictEqual(requests[1].url, |
1274 | absoluteUrl('manifest/media-00001.ts'), | 1195 | absoluteUrl('manifest/media-00002.ts'), |
1275 | 'made segment request'); | 1196 | 'made segment request'); |
1276 | 1197 | ||
1277 | player.tech_.currentTime = function() { | 1198 | currentTime = 22; |
1278 | return 55; | 1199 | player.tech_.hls.sourceBuffer.trigger('updateend'); |
1279 | }; | ||
1280 | |||
1281 | player.tech_.hls.checkBuffer_(); | 1200 | player.tech_.hls.checkBuffer_(); |
1282 | |||
1283 | strictEqual(requests.length, 2, 'made no additional requests'); | 1201 | strictEqual(requests.length, 2, 'made no additional requests'); |
1284 | 1202 | ||
1285 | player.tech_.currentTime = function() { | 1203 | buffered = [[0, 10], [20, 30]]; |
1286 | return 134; | ||
1287 | }; | ||
1288 | |||
1289 | player.tech_.hls.checkBuffer_(); | 1204 | player.tech_.hls.checkBuffer_(); |
1290 | standardXHRResponse(requests[2]); | 1205 | standardXHRResponse(requests[2]); |
1291 | |||
1292 | strictEqual(requests.length, 3, 'made three requests'); | 1206 | strictEqual(requests.length, 3, 'made three requests'); |
1293 | |||
1294 | strictEqual(requests[2].url, | 1207 | strictEqual(requests[2].url, |
1295 | absoluteUrl('manifest/media-00002.ts'), | 1208 | absoluteUrl('manifest/media-00004.ts'), |
1296 | 'made segment request'); | 1209 | 'made segment request'); |
1297 | }); | 1210 | }); |
1298 | 1211 | ||
... | @@ -1334,15 +1247,8 @@ test('only appends one segment at a time', function() { | ... | @@ -1334,15 +1247,8 @@ test('only appends one segment at a time', function() { |
1334 | standardXHRResponse(requests.pop()); // media.m3u8 | 1247 | standardXHRResponse(requests.pop()); // media.m3u8 |
1335 | standardXHRResponse(requests.pop()); // segment 0 | 1248 | standardXHRResponse(requests.pop()); // segment 0 |
1336 | 1249 | ||
1337 | player.tech_.hls.sourceBuffer.updating = true; | ||
1338 | player.tech_.hls.sourceBuffer.appendBuffer = function() { | ||
1339 | appends++; | ||
1340 | }; | ||
1341 | |||
1342 | player.tech_.hls.checkBuffer_(); | 1250 | player.tech_.hls.checkBuffer_(); |
1343 | standardXHRResponse(requests.pop()); // segment 1 | 1251 | equal(requests.length, 0, 'did not request while updating'); |
1344 | player.tech_.hls.checkBuffer_(); // should be a no-op | ||
1345 | equal(appends, 0, 'did not append while updating'); | ||
1346 | }); | 1252 | }); |
1347 | 1253 | ||
1348 | QUnit.skip('records the min and max PTS values for a segment', function() { | 1254 | QUnit.skip('records the min and max PTS values for a segment', function() { |
... | @@ -1424,7 +1330,6 @@ QUnit.skip('records PTS values for audio-only segments', function() { | ... | @@ -1424,7 +1330,6 @@ QUnit.skip('records PTS values for audio-only segments', function() { |
1424 | }); | 1330 | }); |
1425 | 1331 | ||
1426 | test('waits to download new segments until the media playlist is stable', function() { | 1332 | test('waits to download new segments until the media playlist is stable', function() { |
1427 | var media; | ||
1428 | player.src({ | 1333 | player.src({ |
1429 | src: 'manifest/master.m3u8', | 1334 | src: 'manifest/master.m3u8', |
1430 | type: 'application/vnd.apple.mpegurl' | 1335 | type: 'application/vnd.apple.mpegurl' |
... | @@ -1432,22 +1337,19 @@ test('waits to download new segments until the media playlist is stable', functi | ... | @@ -1432,22 +1337,19 @@ test('waits to download new segments until the media playlist is stable', functi |
1432 | openMediaSource(player); | 1337 | openMediaSource(player); |
1433 | standardXHRResponse(requests.shift()); // master | 1338 | standardXHRResponse(requests.shift()); // master |
1434 | player.tech_.hls.bandwidth = 1; // make sure we stay on the lowest variant | 1339 | player.tech_.hls.bandwidth = 1; // make sure we stay on the lowest variant |
1435 | standardXHRResponse(requests.shift()); // media | 1340 | standardXHRResponse(requests.shift()); // media1 |
1436 | 1341 | ||
1437 | // mock a playlist switch | 1342 | // force a playlist switch |
1438 | media = player.tech_.hls.playlists.media(); | 1343 | player.tech_.hls.playlists.media('media3.m3u8'); |
1439 | player.tech_.hls.playlists.media = function() { | ||
1440 | return media; | ||
1441 | }; | ||
1442 | player.tech_.hls.playlists.state = 'SWITCHING_MEDIA'; | ||
1443 | 1344 | ||
1444 | standardXHRResponse(requests.shift()); // segment 0 | 1345 | standardXHRResponse(requests.shift()); // segment 0 |
1346 | player.tech_.hls.sourceBuffer.trigger('updateend'); | ||
1445 | 1347 | ||
1446 | equal(requests.length, 0, 'no requests outstanding'); | 1348 | equal(requests.length, 1, 'only the playlist request outstanding'); |
1447 | player.tech_.hls.checkBuffer_(); | 1349 | player.tech_.hls.checkBuffer_(); |
1448 | equal(requests.length, 0, 'delays segment fetching'); | 1350 | equal(requests.length, 1, 'delays segment fetching'); |
1449 | 1351 | ||
1450 | player.tech_.hls.playlists.state = 'LOADED_METADATA'; | 1352 | standardXHRResponse(requests.shift()); // media3 |
1451 | player.tech_.hls.checkBuffer_(); | 1353 | player.tech_.hls.checkBuffer_(); |
1452 | equal(requests.length, 1, 'resumes segment fetching'); | 1354 | equal(requests.length, 1, 'resumes segment fetching'); |
1453 | }); | 1355 | }); |
... | @@ -1841,52 +1743,24 @@ QUnit.skip('translates ID3 PTS values across discontinuities', function() { | ... | @@ -1841,52 +1743,24 @@ QUnit.skip('translates ID3 PTS values across discontinuities', function() { |
1841 | equal(track.cues[1].endTime, 11, 'second cue ended at the correct time'); | 1743 | equal(track.cues[1].endTime, 11, 'second cue ended at the correct time'); |
1842 | }); | 1744 | }); |
1843 | 1745 | ||
1844 | test('adjusts the segment offsets for out-of-buffer seeking', function() { | ||
1845 | player.src({ | ||
1846 | src: 'manifest/media.m3u8', | ||
1847 | type: 'application/vnd.apple.mpegurl' | ||
1848 | }); | ||
1849 | openMediaSource(player); | ||
1850 | standardXHRResponse(requests.shift()); // media | ||
1851 | player.tech_.hls.sourceBuffer.buffered = function() { | ||
1852 | return videojs.createTimeRange(0, 20); | ||
1853 | }; | ||
1854 | equal(player.tech_.hls.mediaIndex, 0, 'starts at zero'); | ||
1855 | |||
1856 | player.tech_.setCurrentTime(35); | ||
1857 | clock.tick(1); | ||
1858 | // drop the aborted segment | ||
1859 | requests.shift(); | ||
1860 | equal(player.tech_.hls.mediaIndex, 3, 'moved the mediaIndex'); | ||
1861 | standardXHRResponse(requests.shift()); | ||
1862 | }); | ||
1863 | |||
1864 | test('seeks between buffered time ranges', function() { | 1746 | test('seeks between buffered time ranges', function() { |
1865 | player.src({ | 1747 | player.src({ |
1866 | src: 'manifest/media.m3u8', | 1748 | src: 'media.m3u8', |
1867 | type: 'application/vnd.apple.mpegurl' | 1749 | type: 'application/vnd.apple.mpegurl' |
1868 | }); | 1750 | }); |
1869 | openMediaSource(player); | 1751 | openMediaSource(player); |
1870 | standardXHRResponse(requests.shift()); // media | 1752 | standardXHRResponse(requests.shift()); // media |
1871 | player.tech_.buffered = function() { | 1753 | player.tech_.buffered = function() { |
1872 | return { | 1754 | return videojs.createTimeRange([[0, 10], [20, 30]]); |
1873 | length: 2, | ||
1874 | ranges_: [[0, 10], [20, 30]], | ||
1875 | start: function(i) { | ||
1876 | return this.ranges_[i][0]; | ||
1877 | }, | ||
1878 | end: function(i) { | ||
1879 | return this.ranges_[i][1]; | ||
1880 | } | ||
1881 | }; | ||
1882 | }; | 1755 | }; |
1883 | 1756 | ||
1884 | player.tech_.setCurrentTime(15); | 1757 | player.tech_.setCurrentTime(15); |
1885 | clock.tick(1); | 1758 | clock.tick(1); |
1886 | // drop the aborted segment | 1759 | // drop the aborted segment |
1887 | requests.shift(); | 1760 | requests.shift(); |
1888 | equal(player.tech_.hls.mediaIndex, 1, 'updated the mediaIndex'); | 1761 | equal(requests[0].url, |
1889 | standardXHRResponse(requests.shift()); | 1762 | absoluteUrl('media-00002.ts'), |
1763 | 'requested the correct segment'); | ||
1890 | }); | 1764 | }); |
1891 | 1765 | ||
1892 | test('does not modify the media index for in-buffer seeking', function() { | 1766 | test('does not modify the media index for in-buffer seeking', function() { |
... | @@ -1979,40 +1853,6 @@ test('duration is Infinity for live playlists', function() { | ... | @@ -1979,40 +1853,6 @@ test('duration is Infinity for live playlists', function() { |
1979 | 'duration is infinity'); | 1853 | 'duration is infinity'); |
1980 | }); | 1854 | }); |
1981 | 1855 | ||
1982 | test('updates the media index when a playlist reloads', function() { | ||
1983 | player.src({ | ||
1984 | src: 'http://example.com/live-updating.m3u8', | ||
1985 | type: 'application/vnd.apple.mpegurl' | ||
1986 | }); | ||
1987 | openMediaSource(player); | ||
1988 | player.tech_.trigger('play'); | ||
1989 | |||
1990 | requests[0].respond(200, null, | ||
1991 | '#EXTM3U\n' + | ||
1992 | '#EXTINF:10,\n' + | ||
1993 | '0.ts\n' + | ||
1994 | '#EXTINF:10,\n' + | ||
1995 | '1.ts\n' + | ||
1996 | '#EXTINF:10,\n' + | ||
1997 | '2.ts\n'); | ||
1998 | standardXHRResponse(requests[1]); | ||
1999 | // play the stream until 2.ts is playing | ||
2000 | player.tech_.hls.mediaIndex = 3; | ||
2001 | // trigger a playlist refresh | ||
2002 | player.tech_.hls.playlists.trigger('mediaupdatetimeout'); | ||
2003 | requests[2].respond(200, null, | ||
2004 | '#EXTM3U\n' + | ||
2005 | '#EXT-X-MEDIA-SEQUENCE:1\n' + | ||
2006 | '#EXTINF:10,\n' + | ||
2007 | '1.ts\n' + | ||
2008 | '#EXTINF:10,\n' + | ||
2009 | '2.ts\n' + | ||
2010 | '#EXTINF:10,\n' + | ||
2011 | '3.ts\n'); | ||
2012 | |||
2013 | strictEqual(player.tech_.hls.mediaIndex, 2, 'mediaIndex is updated after the reload'); | ||
2014 | }); | ||
2015 | |||
2016 | test('live playlist starts three target durations before live', function() { | 1856 | test('live playlist starts three target durations before live', function() { |
2017 | var mediaPlaylist; | 1857 | var mediaPlaylist; |
2018 | player.src({ | 1858 | player.src({ |
... | @@ -2040,30 +1880,11 @@ test('live playlist starts three target durations before live', function() { | ... | @@ -2040,30 +1880,11 @@ test('live playlist starts three target durations before live', function() { |
2040 | player.tech_.trigger('play'); | 1880 | player.tech_.trigger('play'); |
2041 | clock.tick(1); | 1881 | clock.tick(1); |
2042 | mediaPlaylist = player.tech_.hls.playlists.media(); | 1882 | mediaPlaylist = player.tech_.hls.playlists.media(); |
2043 | equal(player.tech_.hls.mediaIndex, 1, 'mediaIndex is updated at play'); | ||
2044 | equal(player.currentTime(), player.tech_.hls.seekable().end(0), 'seeked to the seekable end'); | 1883 | equal(player.currentTime(), player.tech_.hls.seekable().end(0), 'seeked to the seekable end'); |
2045 | 1884 | ||
2046 | equal(requests.length, 1, 'begins buffering'); | 1885 | equal(requests.length, 1, 'begins buffering'); |
2047 | }); | 1886 | }); |
2048 | 1887 | ||
2049 | test('does not reset live currentTime if mediaIndex is one beyond the last available segment', function() { | ||
2050 | var playlist = { | ||
2051 | mediaSequence: 20, | ||
2052 | targetDuration: 9, | ||
2053 | segments: [{ | ||
2054 | duration: 3 | ||
2055 | }, { | ||
2056 | duration: 3 | ||
2057 | }, { | ||
2058 | duration: 3 | ||
2059 | }] | ||
2060 | }; | ||
2061 | |||
2062 | equal(playlist.segments.length, | ||
2063 | videojs.Hls.translateMediaIndex(playlist.segments.length, playlist, playlist), | ||
2064 | 'did not change mediaIndex'); | ||
2065 | }); | ||
2066 | |||
2067 | test('live playlist starts with correct currentTime value', function() { | 1888 | test('live playlist starts with correct currentTime value', function() { |
2068 | player.src({ | 1889 | player.src({ |
2069 | src: 'http://example.com/manifest/liveStart30sBefore.m3u8', | 1890 | src: 'http://example.com/manifest/liveStart30sBefore.m3u8', |
... | @@ -2117,39 +1938,6 @@ test('resets the time to a seekable position when resuming a live stream ' + | ... | @@ -2117,39 +1938,6 @@ test('resets the time to a seekable position when resuming a live stream ' + |
2117 | player.tech_.trigger('seeked'); | 1938 | player.tech_.trigger('seeked'); |
2118 | }); | 1939 | }); |
2119 | 1940 | ||
2120 | test('mediaIndex is zero before the first segment loads', function() { | ||
2121 | window.manifests['first-seg-load'] = | ||
2122 | '#EXTM3U\n' + | ||
2123 | '#EXTINF:10,\n' + | ||
2124 | '0.ts\n'; | ||
2125 | player.src({ | ||
2126 | src: 'http://example.com/first-seg-load.m3u8', | ||
2127 | type: 'application/vnd.apple.mpegurl' | ||
2128 | }); | ||
2129 | openMediaSource(player); | ||
2130 | |||
2131 | strictEqual(player.tech_.hls.mediaIndex, 0, 'mediaIndex is zero'); | ||
2132 | }); | ||
2133 | |||
2134 | test('mediaIndex returns correctly at playlist boundaries', function() { | ||
2135 | player.src({ | ||
2136 | src: 'http://example.com/master.m3u8', | ||
2137 | type: 'application/vnd.apple.mpegurl' | ||
2138 | }); | ||
2139 | |||
2140 | openMediaSource(player); | ||
2141 | standardXHRResponse(requests.shift()); // master | ||
2142 | standardXHRResponse(requests.shift()); // media | ||
2143 | |||
2144 | strictEqual(player.tech_.hls.mediaIndex, 0, 'mediaIndex is zero at first segment'); | ||
2145 | |||
2146 | // seek to end | ||
2147 | player.tech_.setCurrentTime(40); | ||
2148 | clock.tick(1); | ||
2149 | |||
2150 | strictEqual(player.tech_.hls.mediaIndex, 3, 'mediaIndex is 3 at last segment'); | ||
2151 | }); | ||
2152 | |||
2153 | test('reloads out-of-date live playlists when switching variants', function() { | 1941 | test('reloads out-of-date live playlists when switching variants', function() { |
2154 | player.src({ | 1942 | player.src({ |
2155 | src: 'http://example.com/master.m3u8', | 1943 | src: 'http://example.com/master.m3u8', |
... | @@ -3209,4 +2997,42 @@ test('does not download segments if preload option set to none', function() { | ... | @@ -3209,4 +2997,42 @@ test('does not download segments if preload option set to none', function() { |
3209 | equal(requests.length, 0, 'did not download any segments'); | 2997 | equal(requests.length, 0, 'did not download any segments'); |
3210 | }); | 2998 | }); |
3211 | 2999 | ||
3000 | module('Buffer Inspection'); | ||
3001 | |||
3002 | test('detects time range edges added by updates', function() { | ||
3003 | var edges; | ||
3004 | |||
3005 | edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[0, 10]]), | ||
3006 | videojs.createTimeRange([[0, 11]])); | ||
3007 | deepEqual(edges, [{ end: 11 }], 'detected a forward addition'); | ||
3008 | |||
3009 | edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[5, 10]]), | ||
3010 | videojs.createTimeRange([[0, 10]])); | ||
3011 | deepEqual(edges, [{ start: 0 }], 'detected a backward addition'); | ||
3012 | |||
3013 | edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[5, 10]]), | ||
3014 | videojs.createTimeRange([[0, 11]])); | ||
3015 | deepEqual(edges, [ | ||
3016 | { start: 0 }, { end: 11 } | ||
3017 | ], 'detected forward and backward additions'); | ||
3018 | |||
3019 | edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[0, 10]]), | ||
3020 | videojs.createTimeRange([[0, 10]])); | ||
3021 | deepEqual(edges, [], 'detected no addition'); | ||
3022 | |||
3023 | edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([]), | ||
3024 | videojs.createTimeRange([[0, 10]])); | ||
3025 | deepEqual(edges, [ | ||
3026 | { start: 0 }, | ||
3027 | { end: 10 } | ||
3028 | ], 'detected an initial addition'); | ||
3029 | |||
3030 | edges = videojs.Hls.bufferedAdditions_(videojs.createTimeRange([[0, 10]]), | ||
3031 | videojs.createTimeRange([[0, 10], [20, 30]])); | ||
3032 | deepEqual(edges, [ | ||
3033 | { start: 20 }, | ||
3034 | { end: 30} | ||
3035 | ], 'detected a non-contiguous addition'); | ||
3036 | }); | ||
3037 | |||
3212 | })(window, window.videojs); | 3038 | })(window, window.videojs); | ... | ... |
-
Please register or sign in to post a comment