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.
Files changed (2) hide show
  1. package/engine/session.js +189 -174
  2. 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
- { event: 'applyTimePositionOffset', channel: this._sessionId, offsetMs: this.timePositionOffset });
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
- if (this.alwaysNewSegments) {
208
- // Apply Playhead diff compensation, only after external diff compensation has concluded.
209
- if (this.diffCompensation <= 0) {
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
- const waitTimeMs = 500;
394
- debug(`[${this._sessionId}]: state=${state} - Waiting ${waitTimeMs}ms_${tries} until Leader has finished loading next vod.`);
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
- playheadState.mediaSeq === this.prevMediaSeqOffset.video)) {
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
- { event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
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
- sessionState.vodMediaSeqVideo === null ||
638
- sessionState.vodMediaSeqAudio === null ||
639
- sessionState.state === null ||
640
- sessionState.mediaSeq === null ||
641
- sessionState.discSeq === null) {
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
- { event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
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
- { event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
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
- { event: 'timePositionOffsetUpdated', channel: this._sessionId, offsetMs: this.timePositionOffset });
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
- case SessionState.VOD_RELOAD_INIT:
1245
- try {
1246
- debug(`[${this._sessionId}]: state=VOD_RELOAD_INIT`);
1247
- if (isLeader) {
1248
- const startTS = Date.now();
1249
- // 1) To tell Follower that, Leader is working on it!
1250
- sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
1251
- // 2) Set new 'offset' sequences, to carry on the continuity from session-live
1252
- let mSeq = this.switchDataForSession.mediaSeq;
1253
- let currentVod = await this._sessionState.getCurrentVod();
1254
- if (currentVod.sequenceAlwaysContainNewSegments) {
1255
- // (!) will need to compensate if using this setting on HLSVod Object.
1256
- Object.keys(this.switchDataForSession.transitionSegments).forEach(bw => {
1257
- let shiftedSeg = this.switchDataForSession.transitionSegments[bw].shift();
1258
- if (shiftedSeg && shiftedSeg.discontinuity) {
1259
- shiftedSeg = this.switchDataForSession.transitionSegments[bw].shift();
1260
- }
1261
- });
1262
- }
1263
- const dSeq = this.switchDataForSession.discSeq;
1264
- const mSeqOffset = this.switchDataForSession.mediaSeqOffset;
1265
- const reloadBehind = this.switchDataForSession.reloadBehind;
1266
- const segments = this.switchDataForSession.transitionSegments;
1267
- if ([mSeq,dSeq,mSeqOffset,reloadBehind,segments].includes(null)) {
1268
- debug(`[${this._sessionId}]: LEADER: Cannot Reload VOD, missing switch-back data`);
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
- } catch (err) {
1307
- debug("Failed to init reload vod");
1308
- throw err;
1309
- }
1310
- break;
1311
- case SessionState.VOD_RELOAD_INITIATING:
1312
- debug(`[${this._sessionId}]: state=VOD_RELOAD_INITIATING (${sessionState.vodMediaSeqVideo}_${sessionState.vodMediaSeqAudio}, ${currentVod.getLiveMediaSequencesCount()})`);
1313
- if (!isLeader) {
1314
- debug(`[${this._sessionId}]: not the leader so just waiting for the VOD to be reloaded`);
1315
- if (sessionState.vodMediaSeqVideo === 0 || this.waitingForNextVod) {
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
- this.waitingForNextVod = true;
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
- // Allow Leader|Follower to clear vodCache...
1322
- this.isAllowedToClearVodCache = true;
1323
- return;
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
- .then(() => {
1369
- const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
1370
- hlsVod = new HLSVod(this.slateUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
1371
- const timestamp = Date.now();
1372
- hlsVod.addMetadata('id', `slate-${timestamp}`);
1373
- hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
1374
- hlsVod.addMetadata('planned-duration', ((reps || this.slateRepetitions) * this.slateDuration) / 1000);
1375
- const slateMediaManifestLoader = (bw) => {
1376
- let mediaManifestStream = new Readable();
1377
- mediaManifestStream.push(slateVod.getMediaManifest(bw));
1378
- mediaManifestStream.push(null);
1379
- return mediaManifestStream;
1380
- };
1381
- if (afterVod) {
1382
- return hlsVod.loadAfter(afterVod, null, slateMediaManifestLoader);
1383
- } else {
1384
- return hlsVod.load(null, slateMediaManifestLoader);
1385
- }
1386
- })
1387
- .then(() => {
1388
- resolve(hlsVod);
1389
- })
1390
- .catch(err => {
1391
- debug(err);
1392
- reject(err);
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
- .then(() => {
1414
- const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
1415
- hlsVod = new HLSVod(nexVodUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
1416
- const timestamp = Date.now();
1417
- hlsVod.addMetadata('id', `slate-${timestamp}`);
1418
- hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
1419
- hlsVod.addMetadata('planned-duration', requestedDuration);
1420
- const slateMediaManifestLoader = (bw) => {
1421
- let mediaManifestStream = new Readable();
1422
- mediaManifestStream.push(slateVod.getMediaManifest(bw));
1423
- mediaManifestStream.push(null);
1424
- return mediaManifestStream;
1425
- };
1426
- if (afterVod) {
1427
- return hlsVod.loadAfter(afterVod, null, slateMediaManifestLoader);
1428
- } else {
1429
- return hlsVod.load(null, slateMediaManifestLoader);
1430
- }
1431
- })
1432
- .then(() => {
1433
- resolve(hlsVod);
1434
- })
1435
- .catch(err => {
1436
- debug(err);
1437
- reject(err);
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
- .then(nextVod => {
1471
- //debug(nextVod);
1472
- this.currentMetadata = {
1473
- id: nextVod.id,
1474
- title: nextVod.title || '',
1475
- };
1476
- resolve(nextVod);
1477
- })
1478
- .catch(reject);
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "3.3.10",
3
+ "version": "3.3.11",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {