eyevinn-channel-engine 3.1.4 → 3.1.7

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 CHANGED
@@ -44,6 +44,8 @@ class Session {
44
44
  this.maxTickInterval = DEFAULT_MAX_TICK_INTERVAL;
45
45
  this.diffCompensation = null;
46
46
  this.waitingForNextVod = false;
47
+ this.leaderIsSettingNextVod = false;
48
+ this.isSwitchingBackFromLive = false;
47
49
 
48
50
  if (config) {
49
51
  if (config.sessionId) {
@@ -258,6 +260,8 @@ class Session {
258
260
  throw new Error('Session not ready');
259
261
  }
260
262
 
263
+ this.isSwitchingBackFromLive = true;
264
+
261
265
  let isLeader = await this._sessionStateStore.isLeader(this._instanceId);
262
266
  if (!isLeader) {
263
267
  debug(`[${this._sessionId}]: FOLLOWER: Invalidate cache to ensure having the correct VOD!`);
@@ -278,6 +282,7 @@ class Session {
278
282
  if (!isLeader || vodReloaded) {
279
283
  debug(`[${this._sessionId}]: FOLLOWER: leader is alive, and has presumably updated currentVod. Clearing the cache now`);
280
284
  await this._sessionState.clearCurrentVodCache();
285
+ this.isSwitchingBackFromLive = false;
281
286
  return;
282
287
  }
283
288
  }
@@ -313,6 +318,7 @@ class Session {
313
318
  await this._playheadState.set("vodMediaSeqAudio", 0);
314
319
  await this._playheadState.set("playheadRef", Date.now());
315
320
  }
321
+ this.isSwitchingBackFromLive = false;
316
322
  }
317
323
 
318
324
  async getCurrentMediaSequenceSegments() {
@@ -350,6 +356,9 @@ class Session {
350
356
  if (!this._sessionState) {
351
357
  throw new Error('Session not ready');
352
358
  }
359
+
360
+ this.isSwitchingBackFromLive = true;
361
+
353
362
  const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
354
363
  if (isLeader) {
355
364
  await this._sessionState.set("mediaSeq", _mediaSeq);
@@ -357,6 +366,7 @@ class Session {
357
366
  await this._sessionState.set("discSeq", _discSeq);
358
367
  debug(`[${this._sessionId}]: Setting current media and discontinuity count -> [${_mediaSeq}]:[${_discSeq}]`);
359
368
  }
369
+ this.isSwitchingBackFromLive = false;
360
370
  }
361
371
 
362
372
  async getCurrentMediaAndDiscSequenceCount() {
@@ -395,6 +405,15 @@ class Session {
395
405
  if (!this._sessionState) {
396
406
  throw new Error('Session not ready');
397
407
  }
408
+ // Be sure that the leader is not in the middle of setting new vod data in store.
409
+ // Followers will never run this part...
410
+ let tries = 4;
411
+ while(tries > 0 && this.leaderIsSettingNextVod) {
412
+ debug(`[${this._sessionId}]: Leader is setting the next vod. Waiting 500ms_${tries}`);
413
+ await timer(500);
414
+ tries--;
415
+ }
416
+
398
417
  const sessionState = await this._sessionState.getValues(["vodMediaSeqVideo", "discSeq"]);
399
418
  const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
400
419
 
@@ -463,6 +482,14 @@ class Session {
463
482
  async incrementAsync() {
464
483
  await this._tickAsync();
465
484
  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
+
466
493
  let sessionState = await this._sessionState.getValues(
467
494
  ["state", "mediaSeq", "discSeq", "vodMediaSeqVideo", "vodMediaSeqAudio"]);
468
495
  let playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo", "vodMediaSeqAudio"]);
@@ -498,7 +525,7 @@ class Session {
498
525
  sessionState.vodMediaSeqVideo = await this._sessionState.increment("vodMediaSeqVideo");
499
526
  sessionState.vodMediaSeqAudio = await this._sessionState.increment("vodMediaSeqAudio");
500
527
  }
501
-
528
+
502
529
  if (sessionState.vodMediaSeqVideo >= currentVod.getLiveMediaSequencesCount() - 1) {
503
530
  sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", currentVod.getLiveMediaSequencesCount() - 1);
504
531
  sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", currentVod.getLiveMediaSequencesCount() - 1);
@@ -739,11 +766,10 @@ class Session {
739
766
  length = currentVod.getLiveMediaSequencesCount();
740
767
  lastDiscontinuity = currentVod.getLastDiscontinuity();
741
768
  }
742
- await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
743
- await this._sessionState.setCurrentVod(slateVod);
744
-
745
769
  await this._sessionState.set("vodMediaSeqVideo", 0);
746
770
  await this._sessionState.set("vodMediaSeqAudio", 0);
771
+ await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
772
+ await this._sessionState.setCurrentVod(slateVod);
747
773
  await this._sessionState.set("mediaSeq", sessionState.mediaSeq + length);
748
774
  await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
749
775
  await this._sessionState.set("slateCount", sessionState.slateCount + 1);
@@ -827,6 +853,10 @@ class Session {
827
853
  debug(`[${this._sessionId}]: ${currentVod.getDeltaTimes()}`);
828
854
  debug(`[${this._sessionId}]: ${currentVod.getPlayheadPositions()}`);
829
855
  //debug(newVod);
856
+ sessionState.mediaSeq = await this._sessionState.set("mediaSeq", 0);
857
+ sessionState.discSeq = await this._sessionState.set("discSeq", 0);
858
+ sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
859
+ sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
830
860
  await this._playheadState.set("playheadRef", Date.now());
831
861
  this.produceEvent({
832
862
  type: 'NOW_PLAYING',
@@ -837,10 +867,6 @@ class Session {
837
867
  });
838
868
  sessionState.state = await this._sessionState.set("state", SessionState.VOD_PLAYING);
839
869
  sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
840
- sessionState.mediaSeq = await this._sessionState.set("mediaSeq", 0);
841
- sessionState.discSeq = await this._sessionState.set("discSeq", 0);
842
- sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
843
- sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
844
870
  await this._sessionState.remove("nextVod");
845
871
  return;
846
872
  } else {
@@ -944,16 +970,19 @@ class Session {
944
970
  await ChaosMonkey.loadVod(loadPromise);
945
971
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
946
972
  { event: 'loadVod', channel: this._sessionId, loadTimeMs: Date.now() - loadStart });
973
+ this.leaderIsSettingNextVod = true;
947
974
  debug(`[${this._sessionId}]: next VOD loaded (${newVod.getDeltaTimes()})`);
948
975
  debug(`[${this._sessionId}]: ${newVod.getPlayheadPositions()}`);
949
976
  currentVod = newVod;
950
977
  debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}`);
951
- await this._sessionState.remove("nextVod");
952
- sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
953
978
  sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
954
979
  sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
955
980
  sessionState.mediaSeq = await this._sessionState.set("mediaSeq", sessionState.mediaSeq + length);
956
981
  sessionState.discSeq = await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
982
+ debug(`[${this._sessionId}]: new sequence data set in store [${sessionState.mediaSeq}][${sessionState.discSeq}]`);
983
+ await this._sessionState.remove("nextVod");
984
+ sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
985
+ this.leaderIsSettingNextVod = false;
957
986
  await this._playheadState.set("playheadRef", Date.now());
958
987
  this.produceEvent({
959
988
  type: 'NOW_PLAYING',
@@ -499,8 +499,14 @@ class SessionLive {
499
499
  if (!this.liveSegQueue[liveBw]) {
500
500
  this.liveSegQueue[liveBw] = [];
501
501
  }
502
- this.liveSegQueue[liveBw].push(liveSegFromLeader);
503
- debug(`[${this.sessionId}]: FOLLOWER: Pushed segment (${liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag"}) to 'liveSegQueue' (${liveBw})`);
502
+ // Do not push duplicates
503
+ const liveSegURIs = this.liveSegQueue[liveBw].filter(seg => seg.uri).map(seg => seg.uri);
504
+ if (liveSegFromLeader.uri && liveSegURIs.includes(liveSegFromLeader.uri)) {
505
+ debug(`[${this.sessionId}]: FOLLOWER: Found duplicate live segment. Skip push!`);
506
+ } else {
507
+ this.liveSegQueue[liveBw].push(liveSegFromLeader);
508
+ debug(`[${this.sessionId}]: FOLLOWER: Pushed segment (${liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag"}) to 'liveSegQueue' (${liveBw})`);
509
+ }
504
510
  }
505
511
  }
506
512
  // Remove older segments and update counts
@@ -520,13 +526,13 @@ class SessionLive {
520
526
  */
521
527
  async _loadAllMediaManifests() {
522
528
  debug(`[${this.sessionId}]: Attempting to load all media manifest URIs in=${Object.keys(this.mediaManifestURIs)}`);
523
-
529
+ let currentMseqRaw = null;
524
530
  // -------------------------------------
525
531
  // If I am a Follower-node then my job
526
532
  // ends here, where I only read from store.
527
533
  // -------------------------------------
528
534
  let isLeader = await this.sessionLiveStateStore.isLeader(this.instanceId);
529
- if (!isLeader && this.lastRequestedMediaSeqRaw) {
535
+ if (!isLeader && this.lastRequestedMediaSeqRaw !== null) {
530
536
  debug(`[${this.sessionId}]: FOLLOWER: Reading data from store!`);
531
537
 
532
538
  let leadersMediaSeqRaw = await this.sessionLiveState.get("lastRequestedMediaSeqRaw");
@@ -659,6 +665,8 @@ class SessionLive {
659
665
  continue;
660
666
  }
661
667
 
668
+ currentMseqRaw = allMediaSeqCounts[0];
669
+
662
670
  if (!isLeader) {
663
671
  let leadersFirstSeqCounts = await this.sessionLiveState.get("firstCounts");
664
672
  let tries = 20;
@@ -687,9 +695,13 @@ class SessionLive {
687
695
 
688
696
  // Respawners never do this, only starter followers.
689
697
  // Edge Case: FOLLOWER transitioned from session with different segments from LEADER
690
- const leaderStartedOnCount = leadersFirstSeqCounts.mediaSeqCount;
691
- if (leaderStartedOnCount !== this.mediaSeqCount) {
692
- this.mediaSeqCount = leaderStartedOnCount;
698
+ if (leadersFirstSeqCounts.discSeqCount !== this.discSeqCount) {
699
+ this.discSeqCount = leadersFirstSeqCounts.discSeqCount;
700
+ }
701
+ if (leadersFirstSeqCounts.mediaSeqCount !== this.mediaSeqCount) {
702
+ this.mediaSeqCount = leadersFirstSeqCounts.mediaSeqCount;
703
+ debug(`[${this.sessionId}]: FOLLOWER transistioned with wrong V2L segments, updating counts to [${
704
+ this.mediaSeqCount}][${this.discSeqCount}], and reading 'transitSegs' from store`);
693
705
  const transitSegs = await this.sessionLiveState.get("transitSegs");
694
706
  if (!this._isEmpty(transitSegs)) {
695
707
  this.vodSegments = transitSegs;
@@ -697,12 +709,12 @@ class SessionLive {
697
709
  }
698
710
 
699
711
  // Prepare to load segments...
700
- debug(`[${this.instanceId}][${this.sessionId}]: Newest mseq from LIVE=${allMediaSeqCounts[0]} First mseq in store=${leadersFirstSeqCounts.liveSourceMseqCount}`);
701
- if (allMediaSeqCounts[0] === leadersFirstSeqCounts.liveSourceMseqCount) {
712
+ debug(`[${this.instanceId}][${this.sessionId}]: Newest mseq from LIVE=${currentMseqRaw} First mseq in store=${leadersFirstSeqCounts.liveSourceMseqCount}`);
713
+ if (currentMseqRaw === leadersFirstSeqCounts.liveSourceMseqCount) {
702
714
  this.pushAmount = 1; // Follower from start
703
715
  } else {
704
716
  // RESPAWNED NODES
705
- this.pushAmount = allMediaSeqCounts[0] - leadersFirstSeqCounts.liveSourceMseqCount + 1;
717
+ this.pushAmount = currentMseqRaw - leadersFirstSeqCounts.liveSourceMseqCount + 1;
706
718
 
707
719
  const transitSegs = await this.sessionLiveState.get("transitSegs");
708
720
  //debug(`[${this.sessionId}]: NEW FOLLOWER: I tried to get 'transitSegs'. This is what I found ${JSON.stringify(transitSegs)}`);
@@ -716,8 +728,8 @@ class SessionLive {
716
728
  if (this.firstTime) {
717
729
  this.pushAmount = 1; // Leader from start
718
730
  } else {
719
- this.pushAmount = allMediaSeqCounts[0] - this.lastRequestedMediaSeqRaw;
720
- debug(`[${this.sessionId}]: ...calculating pushAmount=${allMediaSeqCounts[0]}-${this.lastRequestedMediaSeqRaw}=${this.pushAmount}`);
731
+ this.pushAmount = currentMseqRaw - this.lastRequestedMediaSeqRaw;
732
+ debug(`[${this.sessionId}]: ...calculating pushAmount=${currentMseqRaw}-${this.lastRequestedMediaSeqRaw}=${this.pushAmount}`);
721
733
  }
722
734
  debug(`[${this.sessionId}]: ...pushAmount=${this.pushAmount}`);
723
735
  break;
@@ -737,9 +749,9 @@ class SessionLive {
737
749
  const leadersCurrentMseqRaw = await this.sessionLiveState.get("lastRequestedMediaSeqRaw");
738
750
  const counts = await this.sessionLiveState.get("firstCounts");
739
751
  const leadersFirstMseqRaw = counts.liveSourceMseqCount;
740
- if (leadersCurrentMseqRaw && leadersCurrentMseqRaw !== this.lastRequestedMediaSeqRaw) {
752
+ if (leadersCurrentMseqRaw !== null && leadersCurrentMseqRaw !== currentMseqRaw) {
741
753
  // if leader never had any segs from prev mseq
742
- if (leadersFirstMseqRaw && leadersFirstMseqRaw === leadersCurrentMseqRaw) {
754
+ if (leadersFirstMseqRaw !== null && leadersFirstMseqRaw === leadersCurrentMseqRaw) {
743
755
  // Follower updates it's manifest ingedients (segment holders & counts)
744
756
  this.lastRequestedMediaSeqRaw = leadersCurrentMseqRaw;
745
757
  this.liveSegsForFollowers = await this.sessionLiveState.get("liveSegsForFollowers");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "3.1.4",
3
+ "version": "3.1.7",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {