eyevinn-channel-engine 3.1.6 → 3.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/engine/session.js +184 -62
- package/engine/session_live.js +108 -38
- package/engine/session_state.js +9 -2
- package/engine/stream_switcher.js +13 -6
- package/engine/util.js +3 -0
- package/package.json +1 -1
- package/spec/engine/init_switching_spec.js +118 -153
- package/spec/engine/stream_switcher_spec.js +91 -68
package/engine/session.js
CHANGED
|
@@ -43,9 +43,20 @@ class Session {
|
|
|
43
43
|
this.playheadDiffThreshold = DEFAULT_PLAYHEAD_DIFF_THRESHOLD;
|
|
44
44
|
this.maxTickInterval = DEFAULT_MAX_TICK_INTERVAL;
|
|
45
45
|
this.diffCompensation = null;
|
|
46
|
+
this.prevVodMediaSeq = {
|
|
47
|
+
video: null,
|
|
48
|
+
audio: null
|
|
49
|
+
}
|
|
46
50
|
this.waitingForNextVod = false;
|
|
47
51
|
this.leaderIsSettingNextVod = false;
|
|
48
|
-
this.
|
|
52
|
+
this.isSwitchingBackToV2L = false;
|
|
53
|
+
this.switchDataForSession = {
|
|
54
|
+
mediaSeq: null,
|
|
55
|
+
discSeq: null,
|
|
56
|
+
mediaSeqOffset: null,
|
|
57
|
+
transitionSegments: null,
|
|
58
|
+
reloadBehind: null,
|
|
59
|
+
}
|
|
49
60
|
|
|
50
61
|
if (config) {
|
|
51
62
|
if (config.sessionId) {
|
|
@@ -139,7 +150,14 @@ class Session {
|
|
|
139
150
|
state = await this._playheadState.getState();
|
|
140
151
|
|
|
141
152
|
const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
142
|
-
if (isLeader &&
|
|
153
|
+
if (isLeader &&
|
|
154
|
+
[
|
|
155
|
+
SessionState.VOD_NEXT_INIT,
|
|
156
|
+
SessionState.VOD_NEXT_INITIATING,
|
|
157
|
+
SessionState.VOD_RELOAD_INIT,
|
|
158
|
+
SessionState.VOD_RELOAD_INITIATING
|
|
159
|
+
].indexOf(sessionState.state) !== -1) {
|
|
160
|
+
|
|
143
161
|
const firstDuration = await this._getFirstDuration(manifest);
|
|
144
162
|
const tickInterval = firstDuration < 2 ? 2 : firstDuration;
|
|
145
163
|
debug(`[${this._sessionId}]: I am the leader and updated tick interval to ${tickInterval} sec`);
|
|
@@ -152,13 +170,13 @@ class Session {
|
|
|
152
170
|
} else {
|
|
153
171
|
const reqTickInterval = playheadState.tickInterval;
|
|
154
172
|
const timeSpentInIncrement = (tsIncrementEnd - tsIncrementBegin) / 1000;
|
|
155
|
-
let tickInterval =
|
|
173
|
+
let tickInterval = reqTickInterval - timeSpentInIncrement;
|
|
156
174
|
const delta = await this._getCurrentDeltaTime();
|
|
157
175
|
if (delta != 0) {
|
|
158
176
|
debug(`[${this._sessionId}]: Delta time is != 0 need will adjust ${delta}sec to tick interval`);
|
|
159
177
|
tickInterval += delta;
|
|
160
178
|
}
|
|
161
|
-
const position = await this._getCurrentPlayheadPosition() * 1000;
|
|
179
|
+
const position = (await this._getCurrentPlayheadPosition()) * 1000;
|
|
162
180
|
const timePosition = Date.now() - playheadState.playheadRef;
|
|
163
181
|
const diff = position - timePosition;
|
|
164
182
|
debug(`[${this._sessionId}]: ${timePosition}:${position}:${diff > 0 ? '+' : ''}${diff}ms`);
|
|
@@ -214,7 +232,7 @@ class Session {
|
|
|
214
232
|
if (this.disabledPlayhead) {
|
|
215
233
|
return {
|
|
216
234
|
sessionId: this._sessionId,
|
|
217
|
-
}
|
|
235
|
+
};
|
|
218
236
|
} else {
|
|
219
237
|
const playheadState = await this._playheadState.getValues(["tickMs", "mediaSeq", "vodMediaSeqVideo"]);
|
|
220
238
|
const state = await this._playheadState.getState();
|
|
@@ -243,11 +261,16 @@ class Session {
|
|
|
243
261
|
await this._playheadStateStore.reset(this._sessionId);
|
|
244
262
|
}
|
|
245
263
|
|
|
264
|
+
async getSessionState() {
|
|
265
|
+
const state = await this._sessionState.get("state");
|
|
266
|
+
return state;
|
|
267
|
+
}
|
|
268
|
+
|
|
246
269
|
async getTruncatedVodSegments(vodUri, duration) {
|
|
247
270
|
try {
|
|
248
271
|
const hlsVod = await this._truncateSlate(null, duration, vodUri);
|
|
249
272
|
let vodSegments = hlsVod.getMediaSegments();
|
|
250
|
-
Object.keys(vodSegments).forEach(bw => vodSegments[bw].unshift({ discontinuity: true, cue: { in: true } }));
|
|
273
|
+
Object.keys(vodSegments).forEach((bw) => vodSegments[bw].unshift({ discontinuity: true, cue: { in: true } }));
|
|
251
274
|
return vodSegments;
|
|
252
275
|
} catch (exc) {
|
|
253
276
|
debug(`[${this._sessionId}]: Failed to generate truncated VOD!`);
|
|
@@ -257,20 +280,25 @@ class Session {
|
|
|
257
280
|
|
|
258
281
|
async setCurrentMediaSequenceSegments(segments, mSeqOffset, reloadBehind) {
|
|
259
282
|
if (!this._sessionState) {
|
|
260
|
-
throw new Error(
|
|
283
|
+
throw new Error("Session not ready");
|
|
261
284
|
}
|
|
285
|
+
this.isSwitchingBackToV2L = true;
|
|
262
286
|
|
|
263
|
-
this.
|
|
287
|
+
this.switchDataForSession.reloadBehind = reloadBehind;
|
|
288
|
+
this.switchDataForSession.transitionSegments = segments;
|
|
289
|
+
this.switchDataForSession.mediaSeqOffset = mSeqOffset;
|
|
264
290
|
|
|
265
291
|
let isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
266
292
|
if (!isLeader) {
|
|
267
293
|
debug(`[${this._sessionId}]: FOLLOWER: Invalidate cache to ensure having the correct VOD!`);
|
|
268
294
|
await this._sessionState.clearCurrentVodCache();
|
|
295
|
+
|
|
269
296
|
let vodReloaded = await this._sessionState.get("vodReloaded");
|
|
270
297
|
let attempts = 9;
|
|
271
298
|
while (!isLeader && !vodReloaded && attempts > 0) {
|
|
272
299
|
debug(`[${this._sessionId}]: FOLLOWER: I arrived before LEADER. Waiting (1000ms) for LEADER to reload currentVod in store! (tries left=${attempts})`);
|
|
273
300
|
await timer(1000);
|
|
301
|
+
await this._sessionStateStore.clearLeaderCache();
|
|
274
302
|
isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
275
303
|
vodReloaded = await this._sessionState.get("vodReloaded");
|
|
276
304
|
attempts--;
|
|
@@ -282,43 +310,26 @@ class Session {
|
|
|
282
310
|
if (!isLeader || vodReloaded) {
|
|
283
311
|
debug(`[${this._sessionId}]: FOLLOWER: leader is alive, and has presumably updated currentVod. Clearing the cache now`);
|
|
284
312
|
await this._sessionState.clearCurrentVodCache();
|
|
285
|
-
this.isSwitchingBackFromLive = false;
|
|
286
313
|
return;
|
|
287
314
|
}
|
|
288
|
-
|
|
315
|
+
debug(`[${this._sessionId}]: NEW LEADER: Setting state=VOD_RELOAD_INIT`);
|
|
316
|
+
this.isSwitchingBackToV2L = true;
|
|
317
|
+
await this._sessionState.set("state", SessionState.VOD_RELOAD_INIT)
|
|
289
318
|
|
|
290
|
-
|
|
291
|
-
let
|
|
292
|
-
let
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
debug(`[${this._sessionId}]: LEADER: Wait for session to initialize next VOD. (tries left=${ATTEMPTS})`);
|
|
319
|
+
} else {
|
|
320
|
+
let vodReloaded = await this._sessionState.get("vodReloaded");
|
|
321
|
+
let attempts = 9;
|
|
322
|
+
while (!vodReloaded && attempts > 0) {
|
|
323
|
+
debug(`[${this._sessionId}]: LEADER: Waiting (1000ms) to buy some time reloading vod and adding it to store! (tries left=${attempts})`);
|
|
296
324
|
await timer(1000);
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
if (ATTEMPTS === 0) {
|
|
301
|
-
throw new Error("Failed to reload currentVod! Need a new LEADER!");
|
|
325
|
+
vodReloaded = await this._sessionState.get("vodReloaded");
|
|
326
|
+
attempts--;
|
|
302
327
|
}
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
let currentVod = await this._sessionState.getCurrentVod();
|
|
307
|
-
let currentMseq = playheadState.vodMediaSeqVideo + mSeqOffset;
|
|
308
|
-
if (currentMseq > currentVod.getLiveMediaSequencesCount() - 1) {
|
|
309
|
-
currentMseq = currentVod.getLiveMediaSequencesCount() - 1;
|
|
328
|
+
if (attempts === 0) {
|
|
329
|
+
debug(`[${this._sessionId}]: LEADER: WARNING! Vod was never Reloaded!`);
|
|
330
|
+
return;
|
|
310
331
|
}
|
|
311
|
-
// TODO: Support reloading with audioSegments as well
|
|
312
|
-
await currentVod.reload(currentMseq, segments, null, reloadBehind);
|
|
313
|
-
await this._sessionState.setCurrentVod(currentVod, { ttl: (currentVod.getDuration() * 1000) });
|
|
314
|
-
await this._sessionState.set("vodReloaded", 1);
|
|
315
|
-
await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
316
|
-
await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
317
|
-
await this._playheadState.set("vodMediaSeqVideo", 0);
|
|
318
|
-
await this._playheadState.set("vodMediaSeqAudio", 0);
|
|
319
|
-
await this._playheadState.set("playheadRef", Date.now());
|
|
320
332
|
}
|
|
321
|
-
this.isSwitchingBackFromLive = false;
|
|
322
333
|
}
|
|
323
334
|
|
|
324
335
|
async getCurrentMediaSequenceSegments() {
|
|
@@ -329,6 +340,18 @@ class Session {
|
|
|
329
340
|
if (isLeader) {
|
|
330
341
|
await this._sessionState.set("vodReloaded", 0);
|
|
331
342
|
}
|
|
343
|
+
|
|
344
|
+
// Only read data from store if state is VOD_PLAYING
|
|
345
|
+
let state = await this.getSessionState();
|
|
346
|
+
let tries = 12;
|
|
347
|
+
while (state !== SessionState.VOD_PLAYING && tries > 0) {
|
|
348
|
+
const waitTimeMs = 500;
|
|
349
|
+
debug(`[${this._sessionId}]: state=${state} - Waiting ${waitTimeMs}ms_${tries} until Leader has finished loading next vod.`);
|
|
350
|
+
await timer(waitTimeMs);
|
|
351
|
+
tries--;
|
|
352
|
+
state = await this.getSessionState();
|
|
353
|
+
}
|
|
354
|
+
|
|
332
355
|
const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
|
|
333
356
|
if (playheadState.vodMediaSeqVideo === 0) {
|
|
334
357
|
if (!isLeader) {
|
|
@@ -340,7 +363,7 @@ class Session {
|
|
|
340
363
|
if (currentVod) {
|
|
341
364
|
try {
|
|
342
365
|
const mediaSegments = currentVod.getLiveMediaSequenceSegments(playheadState.vodMediaSeqVideo);
|
|
343
|
-
debug(`[${this._sessionId}]: Requesting all segments from Media Sequence: ${playheadState.vodMediaSeqVideo}`);
|
|
366
|
+
debug(`[${this._sessionId}]: Requesting all segments from Media Sequence: ${playheadState.vodMediaSeqVideo}_${currentVod.getLiveMediaSequencesCount()}`);
|
|
344
367
|
return mediaSegments;
|
|
345
368
|
} catch (err) {
|
|
346
369
|
logerror(this._sessionId, err);
|
|
@@ -354,25 +377,31 @@ class Session {
|
|
|
354
377
|
|
|
355
378
|
async setCurrentMediaAndDiscSequenceCount(_mediaSeq, _discSeq) {
|
|
356
379
|
if (!this._sessionState) {
|
|
357
|
-
throw new Error(
|
|
380
|
+
throw new Error("Session not ready");
|
|
358
381
|
}
|
|
359
|
-
|
|
360
|
-
this.isSwitchingBackFromLive = true;
|
|
361
382
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
await this._sessionState.set("discSeq", _discSeq);
|
|
367
|
-
debug(`[${this._sessionId}]: Setting current media and discontinuity count -> [${_mediaSeq}]:[${_discSeq}]`);
|
|
368
|
-
}
|
|
369
|
-
this.isSwitchingBackFromLive = false;
|
|
383
|
+
this.isSwitchingBackToV2L = true;
|
|
384
|
+
|
|
385
|
+
this.switchDataForSession.mediaSeq = _mediaSeq;
|
|
386
|
+
this.switchDataForSession.discSeq = _discSeq;
|
|
370
387
|
}
|
|
371
388
|
|
|
372
389
|
async getCurrentMediaAndDiscSequenceCount() {
|
|
373
390
|
if (!this._sessionState) {
|
|
374
391
|
throw new Error('Session not ready');
|
|
375
392
|
}
|
|
393
|
+
|
|
394
|
+
// Only read data from store if state is VOD_PLAYING
|
|
395
|
+
let state = await this.getSessionState();
|
|
396
|
+
let tries = 12;
|
|
397
|
+
while (state !== SessionState.VOD_PLAYING && tries > 0) {
|
|
398
|
+
const waitTimeMs = 500;
|
|
399
|
+
debug(`[${this._sessionId}]: state=${state} - Waiting ${waitTimeMs}ms_${tries} until Leader has finished loading next vod.`);
|
|
400
|
+
await timer(waitTimeMs);
|
|
401
|
+
tries--;
|
|
402
|
+
state = await this.getSessionState();
|
|
403
|
+
}
|
|
404
|
+
|
|
376
405
|
const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
|
|
377
406
|
const discSeqOffset = await this._sessionState.get("discSeq");
|
|
378
407
|
if (playheadState.vodMediaSeqVideo === 0) {
|
|
@@ -407,8 +436,8 @@ class Session {
|
|
|
407
436
|
}
|
|
408
437
|
// Be sure that the leader is not in the middle of setting new vod data in store.
|
|
409
438
|
// Followers will never run this part...
|
|
410
|
-
let tries =
|
|
411
|
-
while(tries > 0 && this.leaderIsSettingNextVod) {
|
|
439
|
+
let tries = 12;
|
|
440
|
+
while (tries > 0 && this.leaderIsSettingNextVod) {
|
|
412
441
|
debug(`[${this._sessionId}]: Leader is setting the next vod. Waiting 500ms_${tries}`);
|
|
413
442
|
await timer(500);
|
|
414
443
|
tries--;
|
|
@@ -417,17 +446,23 @@ class Session {
|
|
|
417
446
|
const sessionState = await this._sessionState.getValues(["vodMediaSeqVideo", "discSeq"]);
|
|
418
447
|
const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
|
|
419
448
|
|
|
449
|
+
if (this.prevVodMediaSeq.video === null) {
|
|
450
|
+
this.prevVodMediaSeq.video = playheadState.vodMediaSeqVideo;
|
|
451
|
+
}
|
|
452
|
+
|
|
420
453
|
if (playheadState.vodMediaSeqVideo > sessionState.vodMediaSeqVideo) {
|
|
421
454
|
debug(`[${this._sessionId}]: Recently Loaded Next Vod. PlayheadState not up-to-date. Return the last generated m3u8`);
|
|
422
455
|
const m3u8 = await this._playheadState.getLastM3u8();
|
|
423
456
|
if (m3u8) {
|
|
457
|
+
this.prevVodMediaSeq.video = playheadState.vodMediaSeqVideo;
|
|
424
458
|
return m3u8;
|
|
425
459
|
} else {
|
|
426
460
|
debug(`[${this._sessionId}]: We don't have any previously generated m3u8`);
|
|
427
461
|
}
|
|
428
462
|
}
|
|
429
463
|
|
|
430
|
-
if (playheadState.vodMediaSeqVideo < 2) {
|
|
464
|
+
if (playheadState.vodMediaSeqVideo < 2 || playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video) {
|
|
465
|
+
debug(`[${this._sessionId}]: current[${playheadState.vodMediaSeqVideo}]_prev[${this.prevVodMediaSeq.video}]`);
|
|
431
466
|
const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
432
467
|
if (!isLeader) {
|
|
433
468
|
debug(`[${this._sessionId}]: Not a leader and first|second media sequence in a VOD is requested. Invalidate cache to ensure having the correct VOD.`);
|
|
@@ -441,6 +476,7 @@ class Session {
|
|
|
441
476
|
debug(`[${this._sessionId}]: [${playheadState.vodMediaSeqVideo}]_[${currentVod.getLiveMediaSequencesCount()}]`);
|
|
442
477
|
const m3u8 = currentVod.getLiveMediaSequences(playheadState.mediaSeq, bw, playheadState.vodMediaSeqVideo, sessionState.discSeq, this.targetDurationPadding, this.forceTargetDuration);
|
|
443
478
|
debug(`[${this._sessionId}]: [${playheadState.mediaSeq + playheadState.vodMediaSeqVideo}][${sessionState.discSeq}][+${this.targetDurationPadding || 0}] Current media manifest for ${bw} requested`);
|
|
479
|
+
this.prevVodMediaSeq.video = playheadState.vodMediaSeqVideo;
|
|
444
480
|
return m3u8;
|
|
445
481
|
} catch (err) {
|
|
446
482
|
logerror(this._sessionId, err);
|
|
@@ -482,13 +518,6 @@ class Session {
|
|
|
482
518
|
async incrementAsync() {
|
|
483
519
|
await this._tickAsync();
|
|
484
520
|
const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
485
|
-
// Avoid reading old values if new ones are on their way...
|
|
486
|
-
let tries = 10;
|
|
487
|
-
while (tries > 0 && this.isSwitchingBackFromLive) {
|
|
488
|
-
debug(`[${this._sessionId}]: Leader is currently setting new data in store. Waiting 500ms_${tries}`);
|
|
489
|
-
await timer(500);
|
|
490
|
-
tries--;
|
|
491
|
-
}
|
|
492
521
|
|
|
493
522
|
let sessionState = await this._sessionState.getValues(
|
|
494
523
|
["state", "mediaSeq", "discSeq", "vodMediaSeqVideo", "vodMediaSeqAudio"]);
|
|
@@ -502,15 +531,17 @@ class Session {
|
|
|
502
531
|
sessionState.discSeq === null) {
|
|
503
532
|
debug(`[${this._sessionId}]: Session is not ready yet`);
|
|
504
533
|
debug(sessionState);
|
|
534
|
+
await this._sessionState.clearCurrentVodCache();
|
|
505
535
|
if (isLeader) {
|
|
506
536
|
debug(`[${this._sessionId}]: I am the leader, trying to initiate the session`);
|
|
507
537
|
sessionState.state = await this._sessionState.set("state", SessionState.VOD_INIT);
|
|
508
538
|
}
|
|
509
539
|
return null;
|
|
510
540
|
}
|
|
511
|
-
if (sessionState.state === SessionState.VOD_NEXT_INITIATING) {
|
|
541
|
+
if (sessionState.state === SessionState.VOD_NEXT_INITIATING || sessionState.state === SessionState.VOD_RELOAD_INITIATING) {
|
|
512
542
|
if (isLeader) {
|
|
513
|
-
|
|
543
|
+
const leaderAction = sessionState.state === SessionState.VOD_NEXT_INITIATING ? "initiated" : "reloaded";
|
|
544
|
+
debug(`[${this._sessionId}]: I am the leader and have just ${leaderAction} next VOD, let's move to VOD_PLAYING`);
|
|
514
545
|
sessionState.state = await this._sessionState.set("state", SessionState.VOD_PLAYING);
|
|
515
546
|
} else {
|
|
516
547
|
debug(`[${this._sessionId}]: Return the last generated m3u8 to give the leader some time`);
|
|
@@ -532,6 +563,11 @@ class Session {
|
|
|
532
563
|
sessionState.state = await this._sessionState.set("state", SessionState.VOD_NEXT_INIT);
|
|
533
564
|
}
|
|
534
565
|
|
|
566
|
+
if (this.isSwitchingBackToV2L) {
|
|
567
|
+
sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INIT);
|
|
568
|
+
this.isSwitchingBackToV2L = false;
|
|
569
|
+
}
|
|
570
|
+
|
|
535
571
|
playheadState.mediaSeq = await this._playheadState.set("mediaSeq", sessionState.mediaSeq);
|
|
536
572
|
playheadState.vodMediaSeqVideo = await this._playheadState.set("vodMediaSeqVideo", sessionState.vodMediaSeqVideo);
|
|
537
573
|
playheadState.vodMediaSeqAudio = await this._playheadState.set("vodMediaSeqAudio", sessionState.vodMediaSeqAudio);
|
|
@@ -809,6 +845,10 @@ class Session {
|
|
|
809
845
|
case SessionState.VOD_INIT:
|
|
810
846
|
case SessionState.VOD_INIT_BY_ID:
|
|
811
847
|
try {
|
|
848
|
+
// Needed if store was reset
|
|
849
|
+
await this._sessionStateStore.clearLeaderCache();
|
|
850
|
+
isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
851
|
+
|
|
812
852
|
let nextVodPromise;
|
|
813
853
|
if (sessionState.state === SessionState.VOD_INIT) {
|
|
814
854
|
debug(`[${this._sessionId}]: state=VOD_INIT`);
|
|
@@ -898,6 +938,17 @@ class Session {
|
|
|
898
938
|
currentVod = await this._sessionState.getCurrentVod();
|
|
899
939
|
this.waitingForNextVod = false;
|
|
900
940
|
}
|
|
941
|
+
} else {
|
|
942
|
+
// Handle edge case where store has been reset, but leader has not cleared cache.
|
|
943
|
+
if (this.prevVodMediaSeq.video === null) {
|
|
944
|
+
this.prevVodMediaSeq.video = sessionState.vodMediaSeqVideo;
|
|
945
|
+
}
|
|
946
|
+
if (this.prevVodMediaSeq.video < sessionState.vodMediaSeqVideo) {
|
|
947
|
+
debug(`[${this._sessionId}]: current[${sessionState.vodMediaSeqVideo}], prev[${this.prevVodMediaSeq.video}], total[${currentVod.getLiveMediaSequencesCount()}]`);
|
|
948
|
+
await this._sessionState.clearCurrentVodCache();
|
|
949
|
+
currentVod = await this._sessionState.getCurrentVod();
|
|
950
|
+
this.prevVodMediaSeq.video = sessionState.vodMediaSeqVideo;
|
|
951
|
+
}
|
|
901
952
|
}
|
|
902
953
|
debug(`[${this._sessionId}]: state=VOD_PLAYING (${sessionState.vodMediaSeqVideo}_${sessionState.vodMediaSeqAudio}, ${currentVod.getLiveMediaSequencesCount()})`);
|
|
903
954
|
return;
|
|
@@ -1014,6 +1065,77 @@ class Session {
|
|
|
1014
1065
|
}
|
|
1015
1066
|
}
|
|
1016
1067
|
break;
|
|
1068
|
+
case SessionState.VOD_RELOAD_INIT:
|
|
1069
|
+
try {
|
|
1070
|
+
debug(`[${this._sessionId}]: state=VOD_RELOAD_INIT`);
|
|
1071
|
+
if (isLeader) {
|
|
1072
|
+
|
|
1073
|
+
const startTS = Date.now();
|
|
1074
|
+
|
|
1075
|
+
// 1) To tell Follower that, Leader is working on it!
|
|
1076
|
+
sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
|
|
1077
|
+
|
|
1078
|
+
// 2) Set new 'offset' sequences, to carry on the continuity from session-live
|
|
1079
|
+
const mSeq = this.switchDataForSession.mediaSeq;
|
|
1080
|
+
const dSeq = this.switchDataForSession.discSeq;
|
|
1081
|
+
const mSeqOffset = this.switchDataForSession.mediaSeqOffset;
|
|
1082
|
+
const reloadBehind = this.switchDataForSession.reloadBehind;
|
|
1083
|
+
const segments = this.switchDataForSession.transitionSegments;
|
|
1084
|
+
|
|
1085
|
+
if ([mSeq,dSeq,mSeqOffset,reloadBehind,segments].includes(null)) {
|
|
1086
|
+
debug(`[${this._sessionId}]: LEADER: Cannot Reload VOD, missing switch-back data`);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
await this._sessionState.set("mediaSeq", mSeq);
|
|
1091
|
+
await this._playheadState.set("mediaSeq", mSeq);
|
|
1092
|
+
await this._sessionState.set("discSeq", dSeq);
|
|
1093
|
+
debug(`[${this._sessionId}]: Setting current media and discontinuity count -> [${mSeq}]:[${dSeq}]`);
|
|
1094
|
+
|
|
1095
|
+
// 3) Set new media segments/currentVod, to carry on the continuity from session-live
|
|
1096
|
+
debug(`[${this._sessionId}]: LEADER: making changes to current VOD. I will also update currentVod in store.`);
|
|
1097
|
+
const playheadState = await this._playheadState.getValues(["vodMediaSeqVideo"]);
|
|
1098
|
+
let currentVod = await this._sessionState.getCurrentVod();
|
|
1099
|
+
let currentMseq = playheadState.vodMediaSeqVideo + mSeqOffset;
|
|
1100
|
+
if (currentMseq > currentVod.getLiveMediaSequencesCount() - 1) {
|
|
1101
|
+
currentMseq = currentVod.getLiveMediaSequencesCount() - 1;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// TODO: Support reloading with audioSegments as well
|
|
1105
|
+
|
|
1106
|
+
await currentVod.reload(currentMseq, segments, null, reloadBehind);
|
|
1107
|
+
await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
|
|
1108
|
+
await this._sessionState.set("vodReloaded", 1);
|
|
1109
|
+
await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
1110
|
+
await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
1111
|
+
await this._playheadState.set("vodMediaSeqVideo", 0);
|
|
1112
|
+
await this._playheadState.set("vodMediaSeqAudio", 0);
|
|
1113
|
+
await this._playheadState.set("playheadRef", Date.now());
|
|
1114
|
+
debug(`[${this._sessionId}]: LEADER: Set new Reloaded VOD and vodMediaSeq counts in store.`);
|
|
1115
|
+
// 4) emit cloudwatch event object
|
|
1116
|
+
cloudWatchLog(!this.cloudWatchLogging, "engine-session", { event: "switchback", channel: this._sessionId, reqTimeMs: Date.now() - startTS });
|
|
1117
|
+
|
|
1118
|
+
return;
|
|
1119
|
+
} else {
|
|
1120
|
+
debug(`[${this._sessionId}]: not a leader so will go directly to state VOD_RELOAD_INITIATING`);
|
|
1121
|
+
sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
|
|
1122
|
+
}
|
|
1123
|
+
} catch (err) {
|
|
1124
|
+
debug("Failed to init reload vod");
|
|
1125
|
+
throw err;
|
|
1126
|
+
}
|
|
1127
|
+
break;
|
|
1128
|
+
case SessionState.VOD_RELOAD_INITIATING:
|
|
1129
|
+
debug(`[${this._sessionId}]: state=VOD_RELOAD_INITIATING (${sessionState.vodMediaSeqVideo}_${sessionState.vodMediaSeqAudio}, ${currentVod.getLiveMediaSequencesCount()})`);
|
|
1130
|
+
if (!isLeader) {
|
|
1131
|
+
debug(`[${this._sessionId}]: not the leader so just waiting for the VOD to be reloaded`);
|
|
1132
|
+
if (sessionState.vodMediaSeqVideo === 0 || this.waitingForNextVod) {
|
|
1133
|
+
debug(`[${this._sessionId}]: First mediasequence in VOD and I am not the leader so invalidate current VOD cache and fetch the new one from the leader`);
|
|
1134
|
+
await this._sessionState.clearCurrentVodCache();
|
|
1135
|
+
}
|
|
1136
|
+
this.waitingForNextVod = true;
|
|
1137
|
+
}
|
|
1138
|
+
return;
|
|
1017
1139
|
default:
|
|
1018
1140
|
throw new Error("Invalid state: " + sessionState.state);
|
|
1019
1141
|
}
|