27758be3 by David LaPalomento

Add testcase for re-synchronization

Fix up the tests and remove debug logging. Put in one example case for playlist re-synchronization.
1 parent fd8b3a9c
...@@ -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 tech.currentTime_ = tech.currentTime; 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_) {
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]);
......