UNPKG

12.7 kBJavaScriptView Raw
1import QUnit from 'qunit';
2import {
3 default as SyncController,
4 syncPointStrategies as strategies } from '../src/sync-controller.js';
5import { playlistWithDuration } from './test-helpers.js';
6
7function getStrategy(name) {
8 for (let i = 0; i < strategies.length; i++) {
9 if (strategies[i].name === name) {
10 return strategies[i];
11 }
12 }
13 throw new Error('No sync-strategy named "${name}" was found!');
14}
15
16QUnit.module('SyncController', {
17 beforeEach() {
18 this.syncController = new SyncController();
19 }
20});
21
22QUnit.test('returns correct sync point for VOD strategy', function(assert) {
23 let playlist = playlistWithDuration(40);
24 let duration = 40;
25 let timeline = 0;
26 let vodStrategy = getStrategy('VOD');
27 let syncPoint = vodStrategy.run(this.syncController, playlist, duration, timeline);
28
29 assert.deepEqual(syncPoint, { time: 0, segmentIndex: 0 }, 'sync point found for vod');
30
31 duration = Infinity;
32 syncPoint = vodStrategy.run(this.syncController, playlist, duration, timeline);
33
34 assert.equal(syncPoint, null, 'no syncpoint found for non vod ');
35});
36
37QUnit.test('returns correct sync point for ProgramDateTime strategy', function(assert) {
38 let strategy = getStrategy('ProgramDateTime');
39 let datetime = new Date(2012, 11, 12, 12, 12, 12);
40 let playlist = playlistWithDuration(40);
41 let timeline = 0;
42 let duration = Infinity;
43 let syncPoint;
44
45 syncPoint = strategy.run(this.syncController, playlist, duration, timeline);
46
47 assert.equal(syncPoint, null, 'no syncpoint when datetimeToDisplayTime not set');
48
49 playlist.dateTimeObject = datetime;
50
51 this.syncController.setDateTimeMapping(playlist);
52
53 let newPlaylist = playlistWithDuration(40);
54
55 syncPoint = strategy.run(this.syncController, newPlaylist, duration, timeline);
56
57 assert.equal(syncPoint, null, 'no syncpoint when datetimeObject not set on playlist');
58
59 newPlaylist.dateTimeObject = new Date(2012, 11, 12, 12, 12, 22);
60
61 syncPoint = strategy.run(this.syncController, newPlaylist, duration, timeline);
62
63 assert.deepEqual(syncPoint, {
64 time: 10,
65 segmentIndex: 0
66 }, 'syncpoint found for ProgramDateTime set');
67});
68
69QUnit.test('returns correct sync point for Segment strategy', function(assert) {
70 let strategy = getStrategy('Segment');
71 let playlist = {
72 segments: [
73 { timeline: 0 },
74 { timeline: 0 },
75 { timeline: 1, start: 10 },
76 { timeline: 1, start: 20 },
77 { timeline: 1 },
78 { timeline: 1 },
79 { timeline: 1, start: 50 },
80 { timeline: 1, start: 60 }
81 ]
82 };
83 let currentTimeline;
84 let syncPoint;
85
86 currentTimeline = 0;
87 syncPoint = strategy.run(this.syncController, playlist, 80, currentTimeline, 0);
88 assert.equal(syncPoint, null, 'no syncpoint for timeline 0');
89
90 currentTimeline = 1;
91 syncPoint = strategy.run(this.syncController, playlist, 80, currentTimeline, 30);
92 assert.deepEqual(syncPoint, { time: 20, segmentIndex: 3 },
93 'closest sync point found');
94
95 syncPoint = strategy.run(this.syncController, playlist, 80, currentTimeline, 40);
96 assert.deepEqual(syncPoint, { time: 50, segmentIndex: 6 },
97 'closest sync point found');
98
99 syncPoint = strategy.run(this.syncController, playlist, 80, currentTimeline, 50);
100 assert.deepEqual(syncPoint, { time: 50, segmentIndex: 6 },
101 'exact sync point found');
102});
103
104QUnit.test('returns correct sync point for Discontinuity strategy', function(assert) {
105 let strategy = getStrategy('Discontinuity');
106 let playlist = {
107 targetDuration: 10,
108 discontinuitySequence: 2,
109 discontinuityStarts: [2, 5],
110 segments: [
111 { timeline: 2, start: 20, end: 30, duration: 10 },
112 { timeline: 2, start: 30, end: 40, duration: 10 },
113 { timeline: 3, start: 40, end: 50, duration: 10, discontinuity: true },
114 { timeline: 3, start: 50, end: 60, duration: 10 },
115 { timeline: 3, start: 60, end: 70, duration: 10 },
116 { timeline: 4, start: 70, end: 80, duration: 10, discontinuity: true },
117 { timeline: 4, start: 80, end: 90, duration: 10 },
118 { timeline: 4, start: 90, end: 100, duration: 10 }
119 ]
120 };
121 let segmentInfo = {
122 playlist,
123 segment: playlist.segments[2],
124 mediaIndex: 2
125 };
126 let currentTimeline = 3;
127 let syncPoint;
128
129 syncPoint = strategy.run(this.syncController, playlist, 100, currentTimeline, 0);
130 assert.equal(syncPoint, null, 'no sync point when no discontinuities saved');
131
132 this.syncController.saveDiscontinuitySyncInfo_(segmentInfo);
133
134 syncPoint = strategy.run(this.syncController, playlist, 100, currentTimeline, 55);
135 assert.deepEqual(syncPoint, { time: 40, segmentIndex: 2 },
136 'found sync point for timeline 3');
137
138 segmentInfo.mediaIndex = 6;
139 segmentInfo.segment = playlist.segments[6];
140 currentTimeline = 4;
141
142 this.syncController.saveDiscontinuitySyncInfo_(segmentInfo);
143
144 syncPoint = strategy.run(this.syncController, playlist, 100, currentTimeline, 90);
145 assert.deepEqual(syncPoint, { time: 70, segmentIndex: 5 },
146 'found sync point for timeline 4');
147});
148
149QUnit.test('returns correct sync point for Playlist strategy', function(assert) {
150 let strategy = getStrategy('Playlist');
151 let playlist = { mediaSequence: 100 };
152 let syncPoint;
153
154 syncPoint = strategy.run(this.syncController, playlist, 40, 0);
155 assert.equal(syncPoint, null, 'no sync point if no sync info');
156
157 playlist.mediaSequence = 102;
158 playlist.syncInfo = { time: 10, mediaSequence: 100};
159
160 syncPoint = strategy.run(this.syncController, playlist, 40, 0);
161 assert.deepEqual(syncPoint, { time: 10, segmentIndex: -2 },
162 'found sync point in playlist');
163});
164
165QUnit.test('saves expired info onto new playlist for sync point', function(assert) {
166 let oldPlaylist = playlistWithDuration(50);
167 let newPlaylist = playlistWithDuration(50);
168
169 oldPlaylist.mediaSequence = 100;
170 newPlaylist.mediaSequence = 103;
171
172 oldPlaylist.segments[0].start = 390;
173 oldPlaylist.segments[1].start = 400;
174
175 this.syncController.saveExpiredSegmentInfo(oldPlaylist, newPlaylist);
176
177 assert.deepEqual(newPlaylist.syncInfo, { mediaSequence: 101, time: 400 },
178 'saved correct info for expired segment onto new playlist');
179});
180
181QUnit.test('Correctly updates time mapping and discontinuity info when probing segments',
182 function(assert) {
183 let syncCon = this.syncController;
184 let playlist = playlistWithDuration(60);
185
186 playlist.discontinuityStarts = [3];
187 playlist.discontinuitySequence = 0;
188 playlist.segments[3].discontinuity = true;
189 playlist.segments.forEach((segment, i) => {
190 if (i >= playlist.discontinuityStarts[0]) {
191 segment.timeline = 1;
192 } else {
193 segment.timeline = 0;
194 }
195 });
196
197 syncCon.probeTsSegment_ = function(segmentInfo) {
198 return {
199 // offset segment timing to make things interesting
200 start: segmentInfo.mediaIndex * 10 + 5 + (6 * segmentInfo.timeline),
201 end: segmentInfo.mediaIndex * 10 + 10 + 5 + (6 * segmentInfo.timeline)
202 };
203 };
204
205 let segment = playlist.segments[0];
206 let segmentInfo = {
207 mediaIndex: 0,
208 playlist,
209 timeline: 0,
210 timestampOffset: 0,
211 startOfSegment: 0,
212 segment
213 };
214
215 syncCon.probeSegmentInfo(segmentInfo);
216 assert.ok(syncCon.timelines[0], 'created mapping object for timeline 0');
217 assert.deepEqual(syncCon.timelines[0], { time: 0, mapping: -5 },
218 'mapping object correct');
219 assert.equal(segment.start, 0, 'correctly calculated segment start');
220 assert.equal(segment.end, 10, 'correctly calculated segment end');
221 assert.ok(syncCon.discontinuities[1], 'created discontinuity info for timeline 1');
222 assert.deepEqual(syncCon.discontinuities[1], { time: 30, accuracy: 3 },
223 'discontinuity sync info correct');
224
225 segmentInfo.timestampOffset = null;
226 segmentInfo.startOfSegment = 10;
227 segmentInfo.mediaIndex = 1;
228 segment = playlist.segments[1];
229 segmentInfo.segment = segment;
230
231 syncCon.probeSegmentInfo(segmentInfo);
232 assert.equal(segment.start, 10, 'correctly calculated segment start');
233 assert.equal(segment.end, 20, 'correctly calculated segment end');
234 assert.deepEqual(syncCon.discontinuities[1], { time: 30, accuracy: 2 },
235 'discontinuity sync info correctly updated with new accuracy');
236
237 segmentInfo.timestampOffset = 30;
238 segmentInfo.startOfSegment = 30;
239 segmentInfo.mediaIndex = 3;
240 segmentInfo.timeline = 1;
241 segment = playlist.segments[3];
242 segmentInfo.segment = segment;
243
244 syncCon.probeSegmentInfo(segmentInfo);
245 assert.ok(syncCon.timelines[1], 'created mapping object for timeline 1');
246 assert.deepEqual(syncCon.timelines[1], { time: 30, mapping: -11 },
247 'mapping object correct');
248 assert.equal(segment.start, 30, 'correctly calculated segment start');
249 assert.equal(segment.end, 40, 'correctly calculated segment end');
250 assert.deepEqual(syncCon.discontinuities[1], { time: 30, accuracy: 0 },
251 'discontinuity sync info correctly updated with new accuracy');
252 });
253
254QUnit.test('Correctly calculates expired time', function(assert) {
255 let playlist = {
256 targetDuration: 10,
257 mediaSequence: 100,
258 discontinuityStarts: [],
259 syncInfo: {
260 time: 50,
261 mediaSequence: 95
262 },
263 segments: [
264 {
265 duration: 10,
266 uri: '0.ts'
267 },
268 {
269 duration: 10,
270 uri: '1.ts'
271 },
272 {
273 duration: 10,
274 uri: '2.ts'
275 },
276 {
277 duration: 10,
278 uri: '3.ts'
279 },
280 {
281 duration: 10,
282 uri: '4.ts'
283 }
284 ]
285 };
286
287 let expired = this.syncController.getExpiredTime(playlist, Infinity);
288
289 assert.equal(expired, 100, 'estimated expired time using segmentSync');
290
291 playlist = {
292 targetDuration: 10,
293 discontinuityStarts: [],
294 mediaSequence: 100,
295 segments: [
296 {
297 duration: 10,
298 uri: '0.ts'
299 },
300 {
301 duration: 10,
302 uri: '1.ts',
303 start: 108.5,
304 end: 118.4
305 },
306 {
307 duration: 10,
308 uri: '2.ts'
309 },
310 {
311 duration: 10,
312 uri: '3.ts'
313 },
314 {
315 duration: 10,
316 uri: '4.ts'
317 }
318 ]
319 };
320
321 expired = this.syncController.getExpiredTime(playlist, Infinity);
322
323 assert.equal(expired, 98.5, 'estimated expired time using segmentSync');
324
325 playlist = {
326 discontinuityStarts: [],
327 targetDuration: 10,
328 mediaSequence: 100,
329 syncInfo: {
330 time: 50,
331 mediaSequence: 95
332 },
333 segments: [
334 {
335 duration: 10,
336 uri: '0.ts'
337 },
338 {
339 duration: 10,
340 uri: '1.ts',
341 start: 108.5,
342 end: 118.5
343 },
344 {
345 duration: 10,
346 uri: '2.ts'
347 },
348 {
349 duration: 10,
350 uri: '3.ts'
351 },
352 {
353 duration: 10,
354 uri: '4.ts'
355 }
356 ]
357 };
358
359 expired = this.syncController.getExpiredTime(playlist, Infinity);
360
361 assert.equal(expired, 98.5, 'estimated expired time using segmentSync');
362
363 playlist = {
364 targetDuration: 10,
365 discontinuityStarts: [],
366 mediaSequence: 100,
367 syncInfo: {
368 time: 90.8,
369 mediaSequence: 99
370 },
371 segments: [
372 {
373 duration: 10,
374 uri: '0.ts'
375 },
376 {
377 duration: 10,
378 uri: '1.ts'
379 },
380 {
381 duration: 10,
382 uri: '2.ts',
383 start: 118.5,
384 end: 128.5
385 },
386 {
387 duration: 10,
388 uri: '3.ts'
389 },
390 {
391 duration: 10,
392 uri: '4.ts'
393 }
394 ]
395 };
396
397 expired = this.syncController.getExpiredTime(playlist, Infinity);
398
399 assert.equal(expired, 100.8, 'estimated expired time using segmentSync');
400
401 playlist = {
402 targetDuration: 10,
403 discontinuityStarts: [],
404 mediaSequence: 100,
405 endList: true,
406 segments: [
407 {
408 duration: 10,
409 uri: '0.ts'
410 },
411 {
412 duration: 10,
413 uri: '1.ts'
414 },
415 {
416 duration: 10,
417 uri: '2.ts'
418 },
419 {
420 duration: 10,
421 uri: '3.ts'
422 },
423 {
424 duration: 10,
425 uri: '4.ts'
426 }
427 ]
428 };
429
430 expired = this.syncController.getExpiredTime(playlist, 50);
431
432 assert.equal(expired, 0, 'estimated expired time using segmentSync');
433
434 playlist = {
435 targetDuration: 10,
436 discontinuityStarts: [],
437 mediaSequence: 100,
438 endList: true,
439 segments: [
440 {
441 start: 0.006,
442 duration: 10,
443 uri: '0.ts',
444 end: 9.982
445 },
446 {
447 duration: 10,
448 uri: '1.ts'
449 },
450 {
451 duration: 10,
452 uri: '2.ts'
453 },
454 {
455 duration: 10,
456 uri: '3.ts'
457 },
458 {
459 duration: 10,
460 uri: '4.ts'
461 }
462 ]
463 };
464
465 expired = this.syncController.getExpiredTime(playlist, 50);
466
467 assert.equal(expired, 0, 'estimated expired time using segmentSync');
468});