Add testcase for re-synchronization
Fix up the tests and remove debug logging. Put in one example case for playlist re-synchronization.
Showing
2 changed files
with
89 additions
and
25 deletions
... | @@ -344,11 +344,9 @@ videojs.Hls.prototype.setupSourceBuffer_ = function() { | ... | @@ -344,11 +344,9 @@ videojs.Hls.prototype.setupSourceBuffer_ = function() { |
344 | if (this.tech_.currentTime() < this.tech_.buffered().start(i)) { | 344 | if (this.tech_.currentTime() < this.tech_.buffered().start(i)) { |
345 | // found the misidentified segment's buffered time range | 345 | // found the misidentified segment's buffered time range |
346 | // adjust the media index to fill the gap | 346 | // adjust the media index to fill the gap |
347 | var mi = this.mediaIndex; | ||
348 | currentBuffered = this.findCurrentBuffered_(); | 347 | currentBuffered = this.findCurrentBuffered_(); |
349 | this.playlists.updateTimelineOffset(segmentInfo.mediaIndex, this.tech_.buffered().start(i)); | 348 | this.playlists.updateTimelineOffset(segmentInfo.mediaIndex, this.tech_.buffered().start(i)); |
350 | this.mediaIndex = this.playlists.getMediaIndexForTime_(currentBuffered.end(0) + 1); | 349 | this.mediaIndex = this.playlists.getMediaIndexForTime_(currentBuffered.end(0) + 1); |
351 | console.log(mi, '->', this.mediaIndex, 'expired:', this.tech_.buffered().start(i)); | ||
352 | break; | 350 | break; |
353 | } | 351 | } |
354 | } | 352 | } | ... | ... |
... | @@ -42,7 +42,25 @@ var | ... | @@ -42,7 +42,25 @@ var |
42 | // patch over some methods of the provided tech so it can be tested | 42 | // patch over some methods of the provided tech so it can be tested |
43 | // synchronously with sinon's fake timers | 43 | // synchronously with sinon's fake timers |
44 | mockTech = function(tech) { | 44 | mockTech = function(tech) { |
45 | if (tech.isMocked_) { | ||
46 | // make this function idempotent because HTML and Flash based | ||
47 | // playback have very different lifecycles. For HTML, the tech | ||
48 | // is available on player creation. For Flash, the tech isn't | ||
49 | // ready until the source has been loaded and one tick has | ||
50 | // expired. | ||
51 | return; | ||
52 | } | ||
53 | |||
54 | tech.isMocked_ = true; | ||
55 | |||
56 | tech.paused_ = !tech.autoplay(); | ||
57 | tech.paused = function() { | ||
58 | return tech.paused_; | ||
59 | }; | ||
60 | |||
61 | if (!tech.currentTime_) { | ||
45 | tech.currentTime_ = tech.currentTime; | 62 | tech.currentTime_ = tech.currentTime; |
63 | } | ||
46 | tech.currentTime = function() { | 64 | tech.currentTime = function() { |
47 | return tech.time_ === undefined ? tech.currentTime_() : tech.time_; | 65 | return tech.time_ === undefined ? tech.currentTime_() : tech.time_; |
48 | }; | 66 | }; |
... | @@ -61,6 +79,19 @@ var | ... | @@ -61,6 +79,19 @@ var |
61 | return tech.src_ === undefined ? tech.currentSrc_() : tech.src_; | 79 | return tech.src_ === undefined ? tech.currentSrc_() : tech.src_; |
62 | }; | 80 | }; |
63 | 81 | ||
82 | tech.play_ = tech.play; | ||
83 | tech.play = function() { | ||
84 | tech.play_(); | ||
85 | tech.paused_ = false; | ||
86 | tech.trigger('play'); | ||
87 | }; | ||
88 | tech.pause_ = tech.pause_; | ||
89 | tech.pause = function() { | ||
90 | tech.pause_(); | ||
91 | tech.paused_ = true; | ||
92 | tech.trigger('pause'); | ||
93 | }; | ||
94 | |||
64 | tech.setCurrentTime = function(time) { | 95 | tech.setCurrentTime = function(time) { |
65 | tech.time_ = time; | 96 | tech.time_ = time; |
66 | 97 | ||
... | @@ -95,6 +126,7 @@ var | ... | @@ -95,6 +126,7 @@ var |
95 | // ensure the Flash tech is ready | 126 | // ensure the Flash tech is ready |
96 | player.tech_.triggerReady(); | 127 | player.tech_.triggerReady(); |
97 | clock.tick(1); | 128 | clock.tick(1); |
129 | mockTech(player.tech_); | ||
98 | 130 | ||
99 | // simulate the sourceopen event | 131 | // simulate the sourceopen event |
100 | player.tech_.hls.mediaSource.readyState = 'open'; | 132 | player.tech_.hls.mediaSource.readyState = 'open'; |
... | @@ -197,9 +229,11 @@ var | ... | @@ -197,9 +229,11 @@ var |
197 | constructor: function() {}, | 229 | constructor: function() {}, |
198 | abort: function() {}, | 230 | abort: function() {}, |
199 | buffered: videojs.createTimeRange(), | 231 | buffered: videojs.createTimeRange(), |
200 | appendBuffer: function() {} | 232 | appendBuffer: function() {}, |
233 | remove: function() {} | ||
201 | }))(); | 234 | }))(); |
202 | }, | 235 | }, |
236 | endOfStream: function() {} | ||
203 | }), | 237 | }), |
204 | 238 | ||
205 | // do a shallow copy of the properties of source onto the target object | 239 | // do a shallow copy of the properties of source onto the target object |
... | @@ -882,6 +916,57 @@ test('moves to the next segment if there is a network error', function() { | ... | @@ -882,6 +916,57 @@ test('moves to the next segment if there is a network error', function() { |
882 | strictEqual(mediaIndex + 1, player.tech_.hls.mediaIndex, 'media index is incremented'); | 916 | strictEqual(mediaIndex + 1, player.tech_.hls.mediaIndex, 'media index is incremented'); |
883 | }); | 917 | }); |
884 | 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 | }); | ||
969 | |||
885 | test('updates the duration after switching playlists', function() { | 970 | test('updates the duration after switching playlists', function() { |
886 | var selectedPlaylist = false; | 971 | var selectedPlaylist = false; |
887 | player.src({ | 972 | player.src({ |
... | @@ -1172,33 +1257,14 @@ test('buffers based on the correct TimeRange if multiple ranges exist', function | ... | @@ -1172,33 +1257,14 @@ test('buffers based on the correct TimeRange if multiple ranges exist', function |
1172 | return 8; | 1257 | return 8; |
1173 | }; | 1258 | }; |
1174 | 1259 | ||
1175 | player.tech_.buffered = function() { | ||
1176 | return { | ||
1177 | start: function(num) { | ||
1178 | switch (num) { | ||
1179 | case 0: | ||
1180 | return 0; | ||
1181 | case 1: | ||
1182 | return 50; | ||
1183 | } | ||
1184 | }, | ||
1185 | end: function(num) { | ||
1186 | switch (num) { | ||
1187 | case 0: | ||
1188 | return 10; | ||
1189 | case 1: | ||
1190 | return 160; | ||
1191 | } | ||
1192 | }, | ||
1193 | length: 2 | ||
1194 | }; | ||
1195 | }; | ||
1196 | |||
1197 | player.src({ | 1260 | player.src({ |
1198 | src: 'manifest/media.m3u8', | 1261 | src: 'manifest/media.m3u8', |
1199 | type: 'application/vnd.apple.mpegurl' | 1262 | type: 'application/vnd.apple.mpegurl' |
1200 | }); | 1263 | }); |
1201 | openMediaSource(player); | 1264 | openMediaSource(player); |
1265 | player.tech_.buffered = function() { | ||
1266 | return videojs.createTimeRange([[0, 10], [50, 160]]); | ||
1267 | }; | ||
1202 | 1268 | ||
1203 | standardXHRResponse(requests[0]); | 1269 | standardXHRResponse(requests[0]); |
1204 | standardXHRResponse(requests[1]); | 1270 | standardXHRResponse(requests[1]); | ... | ... |
-
Please register or sign in to post a comment