eyevinn-channel-engine 3.3.10 → 3.3.11
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 +189 -174
- package/package.json +1 -1
package/engine/session.js
CHANGED
|
@@ -43,6 +43,7 @@ class Session {
|
|
|
43
43
|
this.cloudWatchLogging = false;
|
|
44
44
|
this.playheadDiffThreshold = DEFAULT_PLAYHEAD_DIFF_THRESHOLD;
|
|
45
45
|
this.maxTickInterval = DEFAULT_MAX_TICK_INTERVAL;
|
|
46
|
+
this.maxTickIntervalIsDefault = true;
|
|
46
47
|
this.diffCompensationRate = DEFAULT_DIFF_COMPENSATION_RATE;
|
|
47
48
|
this.diffCompensation = null;
|
|
48
49
|
this.timePositionOffset = 0;
|
|
@@ -110,6 +111,7 @@ class Session {
|
|
|
110
111
|
}
|
|
111
112
|
if (config.maxTickInterval) {
|
|
112
113
|
this.maxTickInterval = config.maxTickInterval;
|
|
114
|
+
this.maxTickIntervalIsDefault = false;
|
|
113
115
|
}
|
|
114
116
|
if (config.disabledPlayhead) {
|
|
115
117
|
this.disabledPlayhead = true;
|
|
@@ -150,6 +152,7 @@ class Session {
|
|
|
150
152
|
|
|
151
153
|
let playheadState = await this._playheadState.getValues(["state"]);
|
|
152
154
|
let state = await this._playheadState.setState(PlayheadState.RUNNING);
|
|
155
|
+
let numberOfLargeTicks = 0;
|
|
153
156
|
while (state !== PlayheadState.CRASHED) {
|
|
154
157
|
try {
|
|
155
158
|
const tsIncrementBegin = Date.now();
|
|
@@ -165,7 +168,7 @@ class Session {
|
|
|
165
168
|
state = await this._playheadState.getState();
|
|
166
169
|
|
|
167
170
|
const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
168
|
-
if (isLeader &&
|
|
171
|
+
if (isLeader &&
|
|
169
172
|
[
|
|
170
173
|
SessionState.VOD_NEXT_INIT,
|
|
171
174
|
SessionState.VOD_NEXT_INITIATING,
|
|
@@ -198,23 +201,23 @@ class Session {
|
|
|
198
201
|
if (this.timePositionOffset && this.diffCompensation <= 0 && this.alwaysNewSegments) {
|
|
199
202
|
timePosition -= this.timePositionOffset;
|
|
200
203
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
|
|
201
|
-
|
|
204
|
+
{ event: 'applyTimePositionOffset', channel: this._sessionId, offsetMs: this.timePositionOffset });
|
|
202
205
|
}
|
|
203
206
|
const diff = position - timePosition;
|
|
204
207
|
debug(`[${this._sessionId}]: ${timePosition}:${position}:${diff > 0 ? '+' : ''}${diff}ms`);
|
|
205
208
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
|
|
206
209
|
{ event: 'playheadDiff', channel: this._sessionId, diffMs: diff });
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const timeToAdd = this._getPlayheadDiffCompesationValue(diff, this.playheadDiffThreshold);
|
|
211
|
-
tickInterval += timeToAdd;
|
|
212
|
-
}
|
|
213
|
-
} else {
|
|
214
|
-
// Apply Playhead diff compensation, always.
|
|
210
|
+
if (this.alwaysNewSegments) {
|
|
211
|
+
// Apply Playhead diff compensation, only after external diff compensation has concluded.
|
|
212
|
+
if (this.diffCompensation <= 0) {
|
|
215
213
|
const timeToAdd = this._getPlayheadDiffCompesationValue(diff, this.playheadDiffThreshold);
|
|
216
214
|
tickInterval += timeToAdd;
|
|
217
215
|
}
|
|
216
|
+
} else {
|
|
217
|
+
// Apply Playhead diff compensation, always.
|
|
218
|
+
const timeToAdd = this._getPlayheadDiffCompesationValue(diff, this.playheadDiffThreshold);
|
|
219
|
+
tickInterval += timeToAdd;
|
|
220
|
+
}
|
|
218
221
|
// Apply external diff compensation if available.
|
|
219
222
|
if (this.diffCompensation && this.diffCompensation > 0) {
|
|
220
223
|
const DIFF_COMPENSATION = (reqTickInterval * this.diffCompensationRate).toFixed(2) * 1000;
|
|
@@ -227,6 +230,18 @@ class Session {
|
|
|
227
230
|
if (tickInterval <= 0.5) {
|
|
228
231
|
tickInterval = 0.5;
|
|
229
232
|
} else if (tickInterval > (this.maxTickInterval / 1000)) {
|
|
233
|
+
const changeMaxTick = Math.ceil(Math.abs(tickInterval * 1000 - (this.maxTickInterval))) + 1000;
|
|
234
|
+
if (this.maxTickIntervalIsDefault) {
|
|
235
|
+
if (numberOfLargeTicks > 2) {
|
|
236
|
+
this.maxTickInterval += changeMaxTick;
|
|
237
|
+
numberOfLargeTicks = 0;
|
|
238
|
+
} else {
|
|
239
|
+
numberOfLargeTicks++;
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
console.warn(`[${this._sessionId}]: Playhead tick interval went over Max tick interval by ${changeMaxTick}ms.
|
|
243
|
+
If the value keeps increasing, consider increasing the 'maxTickInterval' in engineOptions`);
|
|
244
|
+
}
|
|
230
245
|
tickInterval = this.maxTickInterval / 1000;
|
|
231
246
|
}
|
|
232
247
|
debug(`[${this._sessionId}]: (${(new Date()).toISOString()}) ${timeSpentInIncrement}sec in increment. Next tick in ${tickInterval} seconds`)
|
|
@@ -332,7 +347,7 @@ class Session {
|
|
|
332
347
|
break;
|
|
333
348
|
}
|
|
334
349
|
}
|
|
335
|
-
|
|
350
|
+
|
|
336
351
|
let isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
337
352
|
if (!isLeader) {
|
|
338
353
|
debug(`[${this._sessionId}]: FOLLOWER: Invalidate cache to ensure having the correct VOD!`);
|
|
@@ -372,7 +387,7 @@ class Session {
|
|
|
372
387
|
}
|
|
373
388
|
if (attempts === 0) {
|
|
374
389
|
debug(`[${this._sessionId}]: LEADER: WARNING! Vod was never Reloaded!`);
|
|
375
|
-
return;
|
|
390
|
+
return;
|
|
376
391
|
}
|
|
377
392
|
}
|
|
378
393
|
}
|
|
@@ -390,8 +405,8 @@ class Session {
|
|
|
390
405
|
let state = await this.getSessionState();
|
|
391
406
|
let tries = 12;
|
|
392
407
|
while (state !== SessionState.VOD_PLAYING && tries > 0) {
|
|
393
|
-
|
|
394
|
-
|
|
408
|
+
const waitTimeMs = 500;
|
|
409
|
+
debug(`[${this._sessionId}]: state=${state} - Waiting ${waitTimeMs}ms_${tries} until Leader has finished loading next vod.`);
|
|
395
410
|
await timer(waitTimeMs);
|
|
396
411
|
tries--;
|
|
397
412
|
state = await this.getSessionState();
|
|
@@ -453,13 +468,13 @@ class Session {
|
|
|
453
468
|
let state = await this.getSessionState();
|
|
454
469
|
let tries = 12;
|
|
455
470
|
while (state !== SessionState.VOD_PLAYING && tries > 0) {
|
|
456
|
-
const waitTimeMs = 500;
|
|
471
|
+
const waitTimeMs = 500;
|
|
457
472
|
debug(`[${this._sessionId}]: state=${state} - Waiting ${waitTimeMs}ms_${tries} until Leader has finished loading next vod.`);
|
|
458
473
|
await timer(waitTimeMs);
|
|
459
474
|
tries--;
|
|
460
475
|
state = await this.getSessionState();
|
|
461
476
|
}
|
|
462
|
-
|
|
477
|
+
|
|
463
478
|
const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
|
|
464
479
|
const discSeqOffset = await this._sessionState.get("discSeq");
|
|
465
480
|
// Clear Vod Cache here when Switching to Live just to be safe...
|
|
@@ -481,7 +496,7 @@ class Session {
|
|
|
481
496
|
mediaSequenceValue = playheadState.vodMediaSeqVideo;
|
|
482
497
|
}
|
|
483
498
|
const discSeqCount = discSeqOffset + currentVod.discontinuities[playheadState.vodMediaSeqVideo];
|
|
484
|
-
|
|
499
|
+
|
|
485
500
|
debug(`[${this._sessionId}]: MediaSeq: (${playheadState.mediaSeq}+${mediaSequenceValue}=${(playheadState.mediaSeq + mediaSequenceValue)}) and DiscSeq: (${discSeqCount}) requested `);
|
|
486
501
|
return {
|
|
487
502
|
'mediaSeq': (playheadState.mediaSeq + mediaSequenceValue),
|
|
@@ -523,7 +538,7 @@ class Session {
|
|
|
523
538
|
|
|
524
539
|
if (playheadState.vodMediaSeqVideo > sessionState.vodMediaSeqVideo ||
|
|
525
540
|
(playheadState.vodMediaSeqVideo < sessionState.vodMediaSeqVideo &&
|
|
526
|
-
|
|
541
|
+
playheadState.mediaSeq === this.prevMediaSeqOffset.video)) {
|
|
527
542
|
const state = await this._sessionState.get("state");
|
|
528
543
|
if ([SessionState.VOD_RELOAD_INIT, SessionState.VOD_RELOAD_INITIATING].includes(state)) {
|
|
529
544
|
debug(`[${this._sessionId}]: Recently reloaded Vod. PlayheadState not up-to-date (${playheadState.vodMediaSeqVideo}_${sessionState.vodMediaSeqVideo}). Waiting 500ms before reading from store again`);
|
|
@@ -542,7 +557,7 @@ class Session {
|
|
|
542
557
|
}
|
|
543
558
|
|
|
544
559
|
// Force reading up from store, but only once if the condition is right
|
|
545
|
-
if (playheadState.vodMediaSeqVideo < 2 || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
|
|
560
|
+
if (playheadState.vodMediaSeqVideo < 2 || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
|
|
546
561
|
debug(`[${this._sessionId}]: current[${playheadState.vodMediaSeqVideo}]_prev[${this.prevVodMediaSeq.video}]`);
|
|
547
562
|
debug(`[${this._sessionId}]: current-offset[${playheadState.mediaSeq}]_prev-offset[${this.prevMediaSeqOffset.video}]`);
|
|
548
563
|
// If true, then we have not updated the prev-values and not cleared the cache yet.
|
|
@@ -561,8 +576,8 @@ class Session {
|
|
|
561
576
|
if (this.diffCompensation) {
|
|
562
577
|
this.timePositionOffset = this.diffCompensation;
|
|
563
578
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
|
|
564
|
-
|
|
565
|
-
} else{
|
|
579
|
+
{ event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
|
|
580
|
+
} else {
|
|
566
581
|
this.timePositionOffset = 0;
|
|
567
582
|
}
|
|
568
583
|
}
|
|
@@ -626,7 +641,7 @@ class Session {
|
|
|
626
641
|
}
|
|
627
642
|
|
|
628
643
|
async incrementAsync() {
|
|
629
|
-
await this._tickAsync();
|
|
644
|
+
await this._tickAsync();
|
|
630
645
|
const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
631
646
|
|
|
632
647
|
let sessionState = await this._sessionState.getValues(
|
|
@@ -634,11 +649,11 @@ class Session {
|
|
|
634
649
|
let playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo", "vodMediaSeqAudio"]);
|
|
635
650
|
let currentVod = await this._sessionState.getCurrentVod();
|
|
636
651
|
if (!currentVod ||
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
652
|
+
sessionState.vodMediaSeqVideo === null ||
|
|
653
|
+
sessionState.vodMediaSeqAudio === null ||
|
|
654
|
+
sessionState.state === null ||
|
|
655
|
+
sessionState.mediaSeq === null ||
|
|
656
|
+
sessionState.discSeq === null) {
|
|
642
657
|
debug(`[${this._sessionId}]: Session is not ready yet`);
|
|
643
658
|
debug(sessionState);
|
|
644
659
|
await this._sessionState.clearCurrentVodCache();
|
|
@@ -667,7 +682,7 @@ class Session {
|
|
|
667
682
|
sessionState.vodMediaSeqVideo = await this._sessionState.increment("vodMediaSeqVideo");
|
|
668
683
|
sessionState.vodMediaSeqAudio = await this._sessionState.increment("vodMediaSeqAudio");
|
|
669
684
|
}
|
|
670
|
-
|
|
685
|
+
|
|
671
686
|
if (sessionState.vodMediaSeqVideo >= currentVod.getLiveMediaSequencesCount() - 1) {
|
|
672
687
|
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", currentVod.getLiveMediaSequencesCount() - 1);
|
|
673
688
|
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", currentVod.getLiveMediaSequencesCount() - 1);
|
|
@@ -691,7 +706,7 @@ class Session {
|
|
|
691
706
|
debug(`[${this._sessionId}]: INCREMENT (mseq=${playheadState.mediaSeq + playheadState.vodMediaSeqVideo}) vodMediaSeq=(${playheadState.vodMediaSeqVideo}_${playheadState.vodMediaSeqAudio} of ${currentVod.getLiveMediaSequencesCount()})`);
|
|
692
707
|
|
|
693
708
|
// As a FOLLOWER, we might need to read up from shared store...
|
|
694
|
-
if (playheadState.vodMediaSeqVideo < 2 || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
|
|
709
|
+
if (playheadState.vodMediaSeqVideo < 2 || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
|
|
695
710
|
debug(`[${this._sessionId}]: current[${playheadState.vodMediaSeqVideo}]_prev[${this.prevVodMediaSeq.video}]`);
|
|
696
711
|
debug(`[${this._sessionId}]: current-offset[${playheadState.mediaSeq}]_prev-offset[${this.prevMediaSeqOffset.video}]`);
|
|
697
712
|
if (playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
|
|
@@ -708,8 +723,8 @@ class Session {
|
|
|
708
723
|
if (this.diffCompensation) {
|
|
709
724
|
this.timePositionOffset = this.diffCompensation;
|
|
710
725
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
|
|
711
|
-
|
|
712
|
-
} else{
|
|
726
|
+
{ event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
|
|
727
|
+
} else {
|
|
713
728
|
this.timePositionOffset = 0;
|
|
714
729
|
}
|
|
715
730
|
}
|
|
@@ -918,7 +933,7 @@ class Session {
|
|
|
918
933
|
allAudioGroupsAndTheirLanguages[groupId] =
|
|
919
934
|
currentVod.getAudioLangsForAudioGroup(groupId);
|
|
920
935
|
});
|
|
921
|
-
|
|
936
|
+
|
|
922
937
|
return allAudioGroupsAndTheirLanguages;
|
|
923
938
|
}
|
|
924
939
|
|
|
@@ -953,7 +968,7 @@ class Session {
|
|
|
953
968
|
await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
954
969
|
await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
955
970
|
await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
|
|
956
|
-
await this._sessionState.setCurrentVod(slateVod);
|
|
971
|
+
await this._sessionState.setCurrentVod(slateVod);
|
|
957
972
|
await this._sessionState.set("mediaSeq", sessionState.mediaSeq + endValue);
|
|
958
973
|
await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
|
|
959
974
|
await this._sessionState.set("slateCount", sessionState.slateCount + 1);
|
|
@@ -1161,8 +1176,8 @@ class Session {
|
|
|
1161
1176
|
if (this.diffCompensation) {
|
|
1162
1177
|
this.timePositionOffset = this.diffCompensation;
|
|
1163
1178
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
|
|
1164
|
-
|
|
1165
|
-
} else{
|
|
1179
|
+
{ event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
|
|
1180
|
+
} else {
|
|
1166
1181
|
this.timePositionOffset = 0;
|
|
1167
1182
|
}
|
|
1168
1183
|
}
|
|
@@ -1217,8 +1232,8 @@ class Session {
|
|
|
1217
1232
|
if (this.diffCompensation) {
|
|
1218
1233
|
this.timePositionOffset = this.diffCompensation;
|
|
1219
1234
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
|
|
1220
|
-
|
|
1221
|
-
} else{
|
|
1235
|
+
{ event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
|
|
1236
|
+
} else {
|
|
1222
1237
|
this.timePositionOffset = 0;
|
|
1223
1238
|
}
|
|
1224
1239
|
}
|
|
@@ -1241,86 +1256,86 @@ class Session {
|
|
|
1241
1256
|
this.isAllowedToClearVodCache = true;
|
|
1242
1257
|
}
|
|
1243
1258
|
break;
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
return;
|
|
1270
|
-
}
|
|
1271
|
-
await this._sessionState.set("mediaSeq", mSeq);
|
|
1272
|
-
await this._playheadState.set("mediaSeq", mSeq);
|
|
1273
|
-
await this._sessionState.set("discSeq", dSeq);
|
|
1274
|
-
debug(`[${this._sessionId}]: Setting current media and discontinuity count -> [${mSeq}]:[${dSeq}]`);
|
|
1275
|
-
// 3) Set new media segments/currentVod, to carry on the continuity from session-live
|
|
1276
|
-
debug(`[${this._sessionId}]: LEADER: making changes to current VOD. I will also update currentVod in store.`);
|
|
1277
|
-
const playheadState = await this._playheadState.getValues(["vodMediaSeqVideo"]);
|
|
1278
|
-
let nextMseq = playheadState.vodMediaSeqVideo + 1;
|
|
1279
|
-
if (nextMseq > currentVod.getLiveMediaSequencesCount() - 1) {
|
|
1280
|
-
nextMseq = currentVod.getLiveMediaSequencesCount() - 1;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
// ---------------------------------------------------.
|
|
1284
|
-
// TODO: Support reloading with audioSegments as well |
|
|
1285
|
-
// ---------------------------------------------------'
|
|
1286
|
-
|
|
1287
|
-
await currentVod.reload(nextMseq, segments, null, reloadBehind);
|
|
1288
|
-
await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
|
|
1289
|
-
await this._sessionState.set("vodReloaded", 1);
|
|
1290
|
-
await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
1291
|
-
await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
1292
|
-
await this._playheadState.set("vodMediaSeqVideo", 0);
|
|
1293
|
-
await this._playheadState.set("vodMediaSeqAudio", 0);
|
|
1294
|
-
await this._playheadState.set("playheadRef", Date.now());
|
|
1295
|
-
// 4) Log to debug and cloudwatch
|
|
1296
|
-
debug(`[${this._sessionId}]: LEADER: Set new Reloaded VOD and vodMediaSeq counts in store.`);
|
|
1297
|
-
debug(`[${this._sessionId}]: next VOD Reloaded (${currentVod.getDeltaTimes()})`);
|
|
1298
|
-
debug(`[${this._sessionId}]: ${currentVod.getPlayheadPositions()}`);
|
|
1299
|
-
debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}`);
|
|
1300
|
-
cloudWatchLog(!this.cloudWatchLogging, "engine-session", { event: "switchback", channel: this._sessionId, reqTimeMs: Date.now() - startTS });
|
|
1259
|
+
case SessionState.VOD_RELOAD_INIT:
|
|
1260
|
+
try {
|
|
1261
|
+
debug(`[${this._sessionId}]: state=VOD_RELOAD_INIT`);
|
|
1262
|
+
if (isLeader) {
|
|
1263
|
+
const startTS = Date.now();
|
|
1264
|
+
// 1) To tell Follower that, Leader is working on it!
|
|
1265
|
+
sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
|
|
1266
|
+
// 2) Set new 'offset' sequences, to carry on the continuity from session-live
|
|
1267
|
+
let mSeq = this.switchDataForSession.mediaSeq;
|
|
1268
|
+
let currentVod = await this._sessionState.getCurrentVod();
|
|
1269
|
+
if (currentVod.sequenceAlwaysContainNewSegments) {
|
|
1270
|
+
// (!) will need to compensate if using this setting on HLSVod Object.
|
|
1271
|
+
Object.keys(this.switchDataForSession.transitionSegments).forEach(bw => {
|
|
1272
|
+
let shiftedSeg = this.switchDataForSession.transitionSegments[bw].shift();
|
|
1273
|
+
if (shiftedSeg && shiftedSeg.discontinuity) {
|
|
1274
|
+
shiftedSeg = this.switchDataForSession.transitionSegments[bw].shift();
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
}
|
|
1278
|
+
const dSeq = this.switchDataForSession.discSeq;
|
|
1279
|
+
const mSeqOffset = this.switchDataForSession.mediaSeqOffset;
|
|
1280
|
+
const reloadBehind = this.switchDataForSession.reloadBehind;
|
|
1281
|
+
const segments = this.switchDataForSession.transitionSegments;
|
|
1282
|
+
if ([mSeq, dSeq, mSeqOffset, reloadBehind, segments].includes(null)) {
|
|
1283
|
+
debug(`[${this._sessionId}]: LEADER: Cannot Reload VOD, missing switch-back data`);
|
|
1301
1284
|
return;
|
|
1302
|
-
} else {
|
|
1303
|
-
debug(`[${this._sessionId}]: not a leader so will go directly to state VOD_RELOAD_INITIATING`);
|
|
1304
|
-
sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
|
|
1305
1285
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
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`);
|
|
1317
|
-
await this._sessionState.clearCurrentVodCache();
|
|
1286
|
+
await this._sessionState.set("mediaSeq", mSeq);
|
|
1287
|
+
await this._playheadState.set("mediaSeq", mSeq);
|
|
1288
|
+
await this._sessionState.set("discSeq", dSeq);
|
|
1289
|
+
debug(`[${this._sessionId}]: Setting current media and discontinuity count -> [${mSeq}]:[${dSeq}]`);
|
|
1290
|
+
// 3) Set new media segments/currentVod, to carry on the continuity from session-live
|
|
1291
|
+
debug(`[${this._sessionId}]: LEADER: making changes to current VOD. I will also update currentVod in store.`);
|
|
1292
|
+
const playheadState = await this._playheadState.getValues(["vodMediaSeqVideo"]);
|
|
1293
|
+
let nextMseq = playheadState.vodMediaSeqVideo + 1;
|
|
1294
|
+
if (nextMseq > currentVod.getLiveMediaSequencesCount() - 1) {
|
|
1295
|
+
nextMseq = currentVod.getLiveMediaSequencesCount() - 1;
|
|
1318
1296
|
}
|
|
1319
|
-
|
|
1297
|
+
|
|
1298
|
+
// ---------------------------------------------------.
|
|
1299
|
+
// TODO: Support reloading with audioSegments as well |
|
|
1300
|
+
// ---------------------------------------------------'
|
|
1301
|
+
|
|
1302
|
+
await currentVod.reload(nextMseq, segments, null, reloadBehind);
|
|
1303
|
+
await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
|
|
1304
|
+
await this._sessionState.set("vodReloaded", 1);
|
|
1305
|
+
await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
1306
|
+
await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
1307
|
+
await this._playheadState.set("vodMediaSeqVideo", 0);
|
|
1308
|
+
await this._playheadState.set("vodMediaSeqAudio", 0);
|
|
1309
|
+
await this._playheadState.set("playheadRef", Date.now());
|
|
1310
|
+
// 4) Log to debug and cloudwatch
|
|
1311
|
+
debug(`[${this._sessionId}]: LEADER: Set new Reloaded VOD and vodMediaSeq counts in store.`);
|
|
1312
|
+
debug(`[${this._sessionId}]: next VOD Reloaded (${currentVod.getDeltaTimes()})`);
|
|
1313
|
+
debug(`[${this._sessionId}]: ${currentVod.getPlayheadPositions()}`);
|
|
1314
|
+
debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}`);
|
|
1315
|
+
cloudWatchLog(!this.cloudWatchLogging, "engine-session", { event: "switchback", channel: this._sessionId, reqTimeMs: Date.now() - startTS });
|
|
1316
|
+
return;
|
|
1317
|
+
} else {
|
|
1318
|
+
debug(`[${this._sessionId}]: not a leader so will go directly to state VOD_RELOAD_INITIATING`);
|
|
1319
|
+
sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
|
|
1320
1320
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1321
|
+
} catch (err) {
|
|
1322
|
+
debug("Failed to init reload vod");
|
|
1323
|
+
throw err;
|
|
1324
|
+
}
|
|
1325
|
+
break;
|
|
1326
|
+
case SessionState.VOD_RELOAD_INITIATING:
|
|
1327
|
+
debug(`[${this._sessionId}]: state=VOD_RELOAD_INITIATING (${sessionState.vodMediaSeqVideo}_${sessionState.vodMediaSeqAudio}, ${currentVod.getLiveMediaSequencesCount()})`);
|
|
1328
|
+
if (!isLeader) {
|
|
1329
|
+
debug(`[${this._sessionId}]: not the leader so just waiting for the VOD to be reloaded`);
|
|
1330
|
+
if (sessionState.vodMediaSeqVideo === 0 || this.waitingForNextVod) {
|
|
1331
|
+
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`);
|
|
1332
|
+
await this._sessionState.clearCurrentVodCache();
|
|
1333
|
+
}
|
|
1334
|
+
this.waitingForNextVod = true;
|
|
1335
|
+
}
|
|
1336
|
+
// Allow Leader|Follower to clear vodCache...
|
|
1337
|
+
this.isAllowedToClearVodCache = true;
|
|
1338
|
+
return;
|
|
1324
1339
|
default:
|
|
1325
1340
|
throw new Error("Invalid state: " + sessionState.state);
|
|
1326
1341
|
}
|
|
@@ -1365,32 +1380,32 @@ class Session {
|
|
|
1365
1380
|
let hlsVod;
|
|
1366
1381
|
|
|
1367
1382
|
slateVod.load()
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1383
|
+
.then(() => {
|
|
1384
|
+
const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
|
|
1385
|
+
hlsVod = new HLSVod(this.slateUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
|
|
1386
|
+
const timestamp = Date.now();
|
|
1387
|
+
hlsVod.addMetadata('id', `slate-${timestamp}`);
|
|
1388
|
+
hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
|
|
1389
|
+
hlsVod.addMetadata('planned-duration', ((reps || this.slateRepetitions) * this.slateDuration) / 1000);
|
|
1390
|
+
const slateMediaManifestLoader = (bw) => {
|
|
1391
|
+
let mediaManifestStream = new Readable();
|
|
1392
|
+
mediaManifestStream.push(slateVod.getMediaManifest(bw));
|
|
1393
|
+
mediaManifestStream.push(null);
|
|
1394
|
+
return mediaManifestStream;
|
|
1395
|
+
};
|
|
1396
|
+
if (afterVod) {
|
|
1397
|
+
return hlsVod.loadAfter(afterVod, null, slateMediaManifestLoader);
|
|
1398
|
+
} else {
|
|
1399
|
+
return hlsVod.load(null, slateMediaManifestLoader);
|
|
1400
|
+
}
|
|
1401
|
+
})
|
|
1402
|
+
.then(() => {
|
|
1403
|
+
resolve(hlsVod);
|
|
1404
|
+
})
|
|
1405
|
+
.catch(err => {
|
|
1406
|
+
debug(err);
|
|
1407
|
+
reject(err);
|
|
1408
|
+
});
|
|
1394
1409
|
} catch (err) {
|
|
1395
1410
|
reject(err);
|
|
1396
1411
|
}
|
|
@@ -1410,32 +1425,32 @@ class Session {
|
|
|
1410
1425
|
let hlsVod;
|
|
1411
1426
|
|
|
1412
1427
|
slateVod.load()
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1428
|
+
.then(() => {
|
|
1429
|
+
const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
|
|
1430
|
+
hlsVod = new HLSVod(nexVodUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
|
|
1431
|
+
const timestamp = Date.now();
|
|
1432
|
+
hlsVod.addMetadata('id', `slate-${timestamp}`);
|
|
1433
|
+
hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
|
|
1434
|
+
hlsVod.addMetadata('planned-duration', requestedDuration);
|
|
1435
|
+
const slateMediaManifestLoader = (bw) => {
|
|
1436
|
+
let mediaManifestStream = new Readable();
|
|
1437
|
+
mediaManifestStream.push(slateVod.getMediaManifest(bw));
|
|
1438
|
+
mediaManifestStream.push(null);
|
|
1439
|
+
return mediaManifestStream;
|
|
1440
|
+
};
|
|
1441
|
+
if (afterVod) {
|
|
1442
|
+
return hlsVod.loadAfter(afterVod, null, slateMediaManifestLoader);
|
|
1443
|
+
} else {
|
|
1444
|
+
return hlsVod.load(null, slateMediaManifestLoader);
|
|
1445
|
+
}
|
|
1446
|
+
})
|
|
1447
|
+
.then(() => {
|
|
1448
|
+
resolve(hlsVod);
|
|
1449
|
+
})
|
|
1450
|
+
.catch(err => {
|
|
1451
|
+
debug(err);
|
|
1452
|
+
reject(err);
|
|
1453
|
+
});
|
|
1439
1454
|
} catch (err) {
|
|
1440
1455
|
reject(err);
|
|
1441
1456
|
}
|
|
@@ -1467,15 +1482,15 @@ class Session {
|
|
|
1467
1482
|
_getNextVodById(id) {
|
|
1468
1483
|
return new Promise((resolve, reject) => {
|
|
1469
1484
|
this._assetManager.getNextVodById(this._sessionId, id)
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1485
|
+
.then(nextVod => {
|
|
1486
|
+
//debug(nextVod);
|
|
1487
|
+
this.currentMetadata = {
|
|
1488
|
+
id: nextVod.id,
|
|
1489
|
+
title: nextVod.title || '',
|
|
1490
|
+
};
|
|
1491
|
+
resolve(nextVod);
|
|
1492
|
+
})
|
|
1493
|
+
.catch(reject);
|
|
1479
1494
|
});
|
|
1480
1495
|
}
|
|
1481
1496
|
|