eyevinn-channel-engine 3.0.7 → 3.1.0

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.
@@ -2,7 +2,7 @@ name: Publish
2
2
 
3
3
  on:
4
4
  release:
5
- types: [published]
5
+ types: [created]
6
6
 
7
7
  jobs:
8
8
  build:
package/engine/session.js CHANGED
@@ -276,7 +276,8 @@ class Session {
276
276
  debug(`[${this._sessionId}]: FOLLOWER: WARNING! Attempts=0 - Risk of using wrong currentVod`);
277
277
  }
278
278
  if (!isLeader || vodReloaded) {
279
- debug(`[${this._sessionId}]: FOLLOWER: leader is alive, and has presumably updated currentVod`);
279
+ debug(`[${this._sessionId}]: FOLLOWER: leader is alive, and has presumably updated currentVod. Clearing the cache now`);
280
+ await this._sessionState.clearCurrentVodCache();
280
281
  return;
281
282
  }
282
283
  }
@@ -394,9 +395,20 @@ class Session {
394
395
  if (!this._sessionState) {
395
396
  throw new Error('Session not ready');
396
397
  }
397
- const sessionState = await this._sessionState.getValues(["discSeq"]);
398
+ const sessionState = await this._sessionState.getValues(["discSeq", "vodMediaSeqVideo"]);
398
399
  const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
399
- if (playheadState.vodMediaSeqVideo === 0) {
400
+
401
+ /*
402
+ Since sessionState is always newer. If it is a smaller value, then this signals that
403
+ a new vod has been loaded but playheadState has yet to get the new mseq value in 'incrementAsync()'.
404
+ Without this, we risk returning an m3u8 with an old mseq on a new vod to the client.
405
+ */
406
+ if (playheadState.vodMediaSeqVideo > sessionState.vodMediaSeqVideo) {
407
+ playheadState.vodMediaSeqVideo = sessionState.vodMediaSeqVideo;
408
+ }
409
+
410
+ if (playheadState.vodMediaSeqVideo === 0 || this.waitingForNextVod) {
411
+ this.waitingForNextVod = false;
400
412
  const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
401
413
  if (!isLeader) {
402
414
  debug(`[${this._sessionId}]: Not a leader and first media sequence in a VOD is requested. Invalidate cache to ensure having the correct VOD.`);
@@ -406,6 +418,7 @@ class Session {
406
418
  const currentVod = await this._sessionState.getCurrentVod();
407
419
  if (currentVod) {
408
420
  try {
421
+ debug(`[${this._sessionId}]: [${playheadState.vodMediaSeqVideo}]_[${currentVod.getLiveMediaSequencesCount()}]`);
409
422
  const m3u8 = currentVod.getLiveMediaSequences(playheadState.mediaSeq, bw, playheadState.vodMediaSeqVideo, sessionState.discSeq, this.targetDurationPadding, this.forceTargetDuration);
410
423
  debug(`[${this._sessionId}]: [${playheadState.mediaSeq + playheadState.vodMediaSeqVideo}][${sessionState.discSeq}][+${this.targetDurationPadding || 0}] Current media manifest for ${bw} requested`);
411
424
  return m3u8;
@@ -726,10 +739,11 @@ class Session {
726
739
  length = currentVod.getLiveMediaSequencesCount();
727
740
  lastDiscontinuity = currentVod.getLastDiscontinuity();
728
741
  }
742
+ await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
743
+ await this._sessionState.setCurrentVod(slateVod);
744
+
729
745
  await this._sessionState.set("vodMediaSeqVideo", 0);
730
746
  await this._sessionState.set("vodMediaSeqAudio", 0);
731
- await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
732
- await this._sessionState.setCurrentVod(slateVod);
733
747
  await this._sessionState.set("mediaSeq", sessionState.mediaSeq + length);
734
748
  await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
735
749
  await this._sessionState.set("slateCount", sessionState.slateCount + 1);
@@ -813,10 +827,6 @@ class Session {
813
827
  debug(`[${this._sessionId}]: ${currentVod.getDeltaTimes()}`);
814
828
  debug(`[${this._sessionId}]: ${currentVod.getPlayheadPositions()}`);
815
829
  //debug(newVod);
816
- sessionState.mediaSeq = await this._sessionState.set("mediaSeq", 0);
817
- sessionState.discSeq = await this._sessionState.set("discSeq", 0);
818
- sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
819
- sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
820
830
  await this._playheadState.set("playheadRef", Date.now());
821
831
  this.produceEvent({
822
832
  type: 'NOW_PLAYING',
@@ -827,6 +837,10 @@ class Session {
827
837
  });
828
838
  sessionState.state = await this._sessionState.set("state", SessionState.VOD_PLAYING);
829
839
  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);
830
844
  await this._sessionState.remove("nextVod");
831
845
  return;
832
846
  } else {
@@ -15,6 +15,7 @@ const daterangeAttribute = (key, attr) => {
15
15
  return key.toUpperCase() + "=" + `"${attr}"`;
16
16
  }
17
17
  };
18
+ const TARGET_PLAYLIST_DURATION_SEC = 60;
18
19
  const RESET_DELAY = 5000;
19
20
  const FAIL_TIMEOUT = 4000;
20
21
  const PlayheadState = Object.freeze({
@@ -30,7 +31,9 @@ class SessionLive {
30
31
  this.sessionLiveStateStore = sessionLiveStore.sessionLiveStateStore;
31
32
  this.instanceId = sessionLiveStore.instanceId;
32
33
  this.mediaSeqCount = 0;
34
+ this.prevMediaSeqCount = 0;
33
35
  this.discSeqCount = 0;
36
+ this.prevDiscSeqCount = 0;
34
37
  this.targetDuration = 0;
35
38
  this.masterManifestUri = null;
36
39
  this.vodSegments = {};
@@ -106,6 +109,7 @@ class SessionLive {
106
109
  }
107
110
 
108
111
  this.mediaSeqCount = 0;
112
+ this.prevMediaSeqCount = 0;
109
113
  this.discSeqCount = 0;
110
114
  this.targetDuration = 0;
111
115
  this.masterManifestUri = null;
@@ -303,7 +307,7 @@ class SessionLive {
303
307
  const leadersMediaSeqCount = liveCounts.mediaSeqCount;
304
308
  const leadersDiscSeqCount = liveCounts.discSeqCount;
305
309
  if (leadersMediaSeqCount !== null) {
306
- this.mediaSeqCount = leadersMediaSeqCount - 1;
310
+ this.mediaSeqCount = leadersMediaSeqCount;
307
311
  debug(`[${this.sessionId}]: Setting mediaSeqCount to: [${this.mediaSeqCount}]`);
308
312
  }
309
313
  if (leadersDiscSeqCount !== null) {
@@ -342,7 +346,7 @@ class SessionLive {
342
346
  let bw = Object.keys(this.mediaManifestURIs)[i];
343
347
 
344
348
  const liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
345
- const vodTargetBandwidth = this._findNearestBw(bw, Object.keys(this.vodSegments));
349
+ const vodTargetBandwidth = this._getNearestBandwidth(bw, Object.keys(this.vodSegments));
346
350
 
347
351
  // Remove segments and disc-tag if they are on top
348
352
  if (this.vodSegments[vodTargetBandwidth].length > 0 && this.vodSegments[vodTargetBandwidth][0].discontinuity) {
@@ -481,68 +485,28 @@ class SessionLive {
481
485
  // FOLLOWER only function
482
486
  _updateLiveSegQueue() {
483
487
  if (Object.keys(this.liveSegsForFollowers).length === 0) {
484
- debug(`[${this.sessionId}]: Error No Segments found at all.`);
488
+ debug(`[${this.sessionId}]: FOLLOWER: Error No Segments found at all.`);
485
489
  }
486
- const vodBws = Object.keys(this.vodSegments);
487
490
  const liveBws = Object.keys(this.liveSegsForFollowers);
488
491
  const size = this.liveSegsForFollowers[liveBws[0]].length;
489
492
 
490
- let newSegCount = 0;
491
- this.liveSegsForFollowers[liveBws[0]].map((seg) => {
492
- if (seg.uri) {
493
- newSegCount++;
494
- }
495
- });
496
-
497
- debug(`[${this.sessionId}]: FOLLOWER: liveSegsForFollowers Bws=${Object.keys(this.liveSegsForFollowers)}_newSegCount=${newSegCount} `);
498
- // Remove transitional segs & add live source segs collected from store
499
- for (let k = 0; k < newSegCount; k++) {
500
- let incrementDiscSeqCount = false;
501
- // Shift the top vod segment on all variants
502
- for (let i = 0; i < vodBws.length; i++) {
503
- let seg = this.vodSegments[vodBws[i]].shift();
504
- if (seg && seg.discontinuity) {
505
- incrementDiscSeqCount = true;
506
- this.vodSegments[vodBws[i]].shift();
507
- }
508
- }
509
- if (incrementDiscSeqCount) {
510
- this.discSeqCount++;
511
- }
512
- }
513
- // Push to bottom, new live source segment on all variants
493
+ // Push the New Live Segments to All Variants
514
494
  for (let segIdx = 0; segIdx < size; segIdx++) {
515
- let incrementDiscSeqCount = false;
516
- liveBws.forEach((bw) => {
517
- let liveSegFromLeader = this.liveSegsForFollowers[bw][segIdx];
518
- if (!this.liveSegQueue[bw]) {
519
- this.liveSegQueue[bw] = [];
495
+ for (let i = 0; i < liveBws.length; i++) {
496
+ const liveBw = liveBws[i];
497
+ const liveSegFromLeader = this.liveSegsForFollowers[liveBw][segIdx];
498
+ if (!this.liveSegQueue[liveBw]) {
499
+ this.liveSegQueue[liveBw] = [];
520
500
  }
521
- this.liveSegQueue[bw].push(liveSegFromLeader);
522
-
523
- let segCount = 0;
524
- this.liveSegQueue[bw].map((seg) => {
525
- if (seg.uri) {
526
- segCount++;
527
- }
528
- });
529
- if (segCount > this.targetNumSeg) {
530
- let seg = this.liveSegQueue[bw].shift();
531
- if (seg && seg.discontinuity) {
532
- incrementDiscSeqCount = true;
533
- seg = this.liveSegQueue[bw].shift();
534
- }
535
- }
536
- debug(`[${this.sessionId}]: Pushed segment (${liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag"}) to 'liveSegQueue'`);
537
- });
538
- if (incrementDiscSeqCount) {
539
- this.discSeqCount++;
501
+ this.liveSegQueue[liveBw].push(liveSegFromLeader);
502
+ debug(`[${this.sessionId}]: FOLLOWER: Pushed segment (${liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag"}) to 'liveSegQueue' (${liveBw})`);
540
503
  }
541
504
  }
542
- let prevVal = this.mediaSeqCount;
543
- this.mediaSeqCount += newSegCount;
544
- debug(`[${this.sessionId}]: Incrementing Mseq Count from [${prevVal}] -> [${this.mediaSeqCount}]`);
545
- debug(`[${this.sessionId}]: Finished updating all Follower's Counts and Segment Queues!`);
505
+ // Remove older segments and update counts
506
+ const newTotalDuration = this._incrementAndShift("FOLLOWER");
507
+ if (newTotalDuration) {
508
+ debug(`[${this.sessionId}]: FOLLOWER: New Adjusted Playlist Duration=${newTotalDuration}s`);
509
+ }
546
510
  }
547
511
 
548
512
  /**
@@ -718,8 +682,9 @@ class SessionLive {
718
682
 
719
683
  // Respawners never do this, only starter followers.
720
684
  // Edge Case: FOLLOWER transitioned from session with different segments from LEADER
721
- if (leadersFirstSeqCounts.mediaSeqCount - 1 !== this.mediaSeqCount) {
722
- this.mediaSeqCount = leadersFirstSeqCounts.mediaSeqCount - 1;
685
+ const leaderStartedOnCount = leadersFirstSeqCounts.mediaSeqCount;
686
+ if (leaderStartedOnCount !== this.mediaSeqCount) {
687
+ this.mediaSeqCount = leaderStartedOnCount;
723
688
  const transitSegs = await this.sessionLiveState.get("transitSegs");
724
689
  if (!this._isEmpty(transitSegs)) {
725
690
  this.vodSegments = transitSegs;
@@ -735,7 +700,7 @@ class SessionLive {
735
700
  }
736
701
 
737
702
  // Prepare to load segments...
738
- debug(`[${this.instanceId}]: Newest mseq from LIVE=${allMediaSeqCounts[0]} First mseq in store=${leadersFirstSeqCounts.liveSourceMseqCount}`);
703
+ debug(`[${this.instanceId}][${this.sessionId}]: Newest mseq from LIVE=${allMediaSeqCounts[0]} First mseq in store=${leadersFirstSeqCounts.liveSourceMseqCount}`);
739
704
  if (allMediaSeqCounts[0] === leadersFirstSeqCounts.liveSourceMseqCount) {
740
705
  this.pushAmount = 1; // Follower from start
741
706
  } else {
@@ -805,34 +770,34 @@ class SessionLive {
805
770
  let pushPromises = [];
806
771
  for (let i = 0; i < Object.keys(this.mediaManifestURIs).length; i++) {
807
772
  let bw = Object.keys(this.mediaManifestURIs)[i];
773
+ // will add new segments to live seg queue
808
774
  pushPromises.push(this._parseMediaManifest(this.liveSourceM3Us[bw].M3U, this.mediaManifestURIs[bw], bw));
809
775
  debug(`[${this.sessionId}]: Pushed pushPromise for bw=${bw}`);
810
776
  }
811
777
  // Segment Pushing
812
778
  debug(`[${this.sessionId}]: Executing Promises II: Segment Pushing`);
813
- let results = await allSettled(pushPromises); //await Promise.all(pushPromises);
814
- const removedDiscSeqList = results.map((item) => {
815
- if (item.status === "rejected") {
816
- return -1;
817
- }
818
- return item.value.removedDiscSeqs;
819
- });
820
- if (removedDiscSeqList.every((val, i, arr) => val === arr[0])) {
821
- // if all variants removed equal amounts of disc-tags
822
- this.discSeqCount += removedDiscSeqList[0];
779
+ await allSettled(pushPromises);
780
+
781
+ // UPDATE COUNTS, & Shift Segments in vodSegments and liveSegQueue if needed.
782
+ const newTotalDuration = this._incrementAndShift("LEADER");
783
+ if (newTotalDuration) {
784
+ debug(`[${this.sessionId}]: New Adjusted Playlist Duration=${newTotalDuration}s`);
823
785
  }
824
- // UPDATE COUNTS, & Shift Segments in vodSegments
825
- await this._incrementAndShift();
826
786
  }
827
787
 
828
788
  // -----------------------------------------------------
829
789
  // Leader writes to store so that Followers can read.
830
790
  // -----------------------------------------------------
831
791
  if (isLeader) {
832
- debug(`[${this.sessionId}]: LEADER: Adding data to store!`);
833
792
  if (this.allowedToSet) {
834
- await this.sessionLiveState.set("liveSegsForFollowers", this.liveSegsForFollowers);
835
- await this.sessionLiveState.set("lastRequestedMediaSeqRaw", this.lastRequestedMediaSeqRaw);
793
+ const liveBws = Object.keys(this.liveSegsForFollowers);
794
+ const segListSize = this.liveSegsForFollowers[liveBws[0]].length;
795
+ // Do not replace old data with empty data
796
+ if (segListSize > 0) {
797
+ debug(`[${this.sessionId}]: LEADER: Adding data to store!`);
798
+ await this.sessionLiveState.set("liveSegsForFollowers", this.liveSegsForFollowers);
799
+ await this.sessionLiveState.set("lastRequestedMediaSeqRaw", this.lastRequestedMediaSeqRaw);
800
+ }
836
801
  }
837
802
 
838
803
  // [LASTLY]: LEADER does this for respawned-FOLLOWERS' sake.
@@ -841,8 +806,8 @@ class SessionLive {
841
806
  await timer(1000); // maybe remove
842
807
  const firstCounts = {
843
808
  liveSourceMseqCount: this.lastRequestedMediaSeqRaw,
844
- mediaSeqCount: this.mediaSeqCount,
845
- discSeqCount: this.discSeqCount,
809
+ mediaSeqCount: this.prevMediaSeqCount,
810
+ discSeqCount: this.prevDiscSeqCount,
846
811
  };
847
812
  debug(`[${this.sessionId}]: LEADER: I am adding 'firstCounts'=${JSON.stringify(firstCounts)} to Store for future followers`);
848
813
  await this.sessionLiveState.set("firstCounts", firstCounts);
@@ -858,39 +823,148 @@ class SessionLive {
858
823
  return;
859
824
  }
860
825
 
861
- async _incrementAndShift() {
862
- const vodBandwidths = Object.keys(this.vodSegments);
863
- for (let j = 0; j < this.pushAmount; j++) {
826
+ _shiftSegments(opt) {
827
+ let _totalDur = 0;
828
+ let _segments = {};
829
+ let _name = "";
830
+ let _removedSegments = 0;
831
+ let _removedDiscontinuities = 0;
832
+
833
+ if (opt && opt.totalDur) {
834
+ _totalDur = opt.totalDur;
835
+ }
836
+ if (opt && opt.segments) {
837
+ _segments = JSON.parse(JSON.stringify(opt.segments)); // clone it
838
+ }
839
+ if (opt && opt.name) {
840
+ _name = opt.name || "NONE";
841
+ }
842
+ if (opt && opt.removedSegments) {
843
+ _removedSegments = opt.removedSegments;
844
+ }
845
+ if (opt && opt.removedDiscontinuities) {
846
+ _removedDiscontinuities = opt.removedDiscontinuities;
847
+ }
848
+ const bws = Object.keys(_segments);
849
+
850
+ /* When Total Duration is past the Limit, start Shifting V2L|LIVE segments if found */
851
+ while (_totalDur > TARGET_PLAYLIST_DURATION_SEC) {
852
+ // Skip loop if there are no more segments to remove...
853
+ if (_segments[bws[0]].length === 0) {
854
+ return { totalDuration: _totalDur, removedSegments: _removedSegments, removedDiscontinuities: _removedDiscontinuities, shiftedSegments: _segments };
855
+ }
856
+ debug(`[${this.sessionId}]: ${_name}: (${_totalDur})s/(${TARGET_PLAYLIST_DURATION_SEC})s - Playlist Duration is Over the Target. Shift needed!`);
857
+ let timeToRemove = 0;
864
858
  let incrementDiscSeqCount = false;
865
- // Shift the top vod segment
866
- for (let i = 0; i < vodBandwidths.length; i++) {
867
- let seg = this.vodSegments[vodBandwidths[i]].shift();
868
- if (seg) {
869
- if (seg.discontinuity) {
870
- this.vodSegments[vodBandwidths[i]].shift();
871
- incrementDiscSeqCount = true;
859
+
860
+ // Shift Segments for each variant...
861
+ for (let i = 0; i < bws.length; i++) {
862
+ let seg = _segments[bws[i]].shift();
863
+ if (i === 0) {
864
+ debug(`[${this.sessionId}]: ${_name}: (${bws[i]}) Ejected from playlist->: ${JSON.stringify(seg, null, 2)}`);
865
+ }
866
+ if (seg && seg.discontinuity) {
867
+ incrementDiscSeqCount = true;
868
+ if (_segments[bws[i]].length > 0) {
869
+ seg = _segments[bws[i]].shift();
870
+ if (i === 0) {
871
+ debug(`[${this.sessionId}]: ${_name}: (${bws[i]}) Ejected from playlist->: ${JSON.stringify(seg, null, 2)}`);
872
+ }
872
873
  }
873
874
  }
875
+ if (seg && seg.duration) {
876
+ timeToRemove = seg.duration;
877
+ }
878
+ }
879
+ if (timeToRemove) {
880
+ _totalDur -= timeToRemove;
881
+ // Increment number of removed segments...
882
+ _removedSegments++;
874
883
  }
875
884
  if (incrementDiscSeqCount) {
876
- this.discSeqCount++;
885
+ // Update Session Live Discontinuity Sequence Count
886
+ _removedDiscontinuities++;
877
887
  }
878
- this.mediaSeqCount++;
879
888
  }
889
+ return { totalDuration: _totalDur, removedSegments: _removedSegments, removedDiscontinuities: _removedDiscontinuities, shiftedSegments: _segments };
890
+ }
880
891
 
881
- // TODO: LASTLY: SHRINK IF NEEDED. (should only happen once if ever.)
882
- // let extraSegments = (this.liveSegQueue[liveTargetBandwidth].length + this.vodSegments[vodBandwidths[0]].length) - this.targetNumSeg;
883
- // if(extraSegments > 0) {
884
- // // POP top vod seg, 'extraSegments' times for all variants
885
- // for (let k = 0; k < extraSegments; k++) {
886
- // // Shift the top vod segment
887
- // for (let i = 0; i < vodBandwidths.length; i++) {
888
- // this.vodSegments[vodBandwidths[i]].shift();
889
- // }
890
- // this.mediaSeqCount++;
891
- // }
892
- // debug(`[${this.sessionId}]: Success! We shrunk manifest segment amount by ${extraSegments}_units`);
893
- // }
892
+ /**
893
+ * Shifts V2L or LIVE items if total segment duration (V2L+LIVE) are over the target duration.
894
+ * It will also update and increment SessionLive's MediaSeqCount and DiscSeqCount based
895
+ * on what was shifted.
896
+ * @param {string} instanceName Name of instance "LEADER" | "FOLLOWER"
897
+ * @returns {number} The new total duration in seconds
898
+ */
899
+ _incrementAndShift(instanceName) {
900
+ if (!instanceName) {
901
+ instanceName = "UNKNOWN";
902
+ }
903
+ const vodBws = Object.keys(this.vodSegments);
904
+ const liveBws = Object.keys(this.liveSegQueue);
905
+ let vodTotalDur = 0;
906
+ let liveTotalDur = 0;
907
+ let totalDur = 0;
908
+ let removedSegments = 0;
909
+ let removedDiscontinuities = 0;
910
+
911
+ // Calculate Playlist Total Duration
912
+ this.vodSegments[vodBws[0]].forEach((seg) => {
913
+ if (seg.duration) {
914
+ vodTotalDur += seg.duration;
915
+ }
916
+ });
917
+ this.liveSegQueue[liveBws[0]].forEach((seg) => {
918
+ if (seg.duration) {
919
+ liveTotalDur += seg.duration;
920
+ }
921
+ });
922
+ totalDur = vodTotalDur + liveTotalDur;
923
+ debug(`[${this.sessionId}]: ${instanceName}: L2L dur->: ${liveTotalDur}s | V2L dur->: ${vodTotalDur}s | Total dur->: ${totalDur}s`);
924
+
925
+ /** --- SHIFT then INCREMENT --- **/
926
+
927
+ // Shift V2L Segments
928
+ const outputV2L = this._shiftSegments({
929
+ name: instanceName,
930
+ totalDur: totalDur,
931
+ segments: this.vodSegments,
932
+ removedSegments: removedSegments,
933
+ removedDiscontinuities: removedDiscontinuities,
934
+ });
935
+ // Update V2L Segments
936
+ this.vodSegments = outputV2L.shiftedSegments;
937
+ // Update values
938
+ totalDur = outputV2L.totalDuration;
939
+ removedSegments = outputV2L.removedSegments;
940
+ removedDiscontinuities = outputV2L.removedDiscontinuities;
941
+ // Shift LIVE Segments
942
+ const outputLIVE = this._shiftSegments({
943
+ name: instanceName,
944
+ totalDur: totalDur,
945
+ segments: this.liveSegQueue,
946
+ removedSegments: removedSegments,
947
+ removedDiscontinuities: removedDiscontinuities,
948
+ });
949
+ // Update LIVE Segments
950
+ this.liveSegQueue = outputLIVE.shiftedSegments;
951
+ // Update values
952
+ totalDur = outputLIVE.totalDuration;
953
+ removedSegments = outputLIVE.removedSegments;
954
+ removedDiscontinuities = outputLIVE.removedDiscontinuities;
955
+
956
+ // Update Session Live Discontinuity Sequence Count...
957
+ this.prevDiscSeqCount = this.discSeqCount;
958
+ this.discSeqCount += removedDiscontinuities;
959
+ // Update Session Live Media Sequence Count...
960
+ this.prevMediaSeqCount = this.mediaSeqCount;
961
+ this.mediaSeqCount += removedSegments;
962
+ if (this.discSeqCount !== this.prevDiscSeqCount) {
963
+ debug(`[${this.sessionId}]: ${instanceName}: Incrementing Dseq Count from {${this.prevDiscSeqCount}} -> {${this.discSeqCount}}`);
964
+ }
965
+ debug(`[${this.sessionId}]: ${instanceName}: Incrementing Mseq Count from [${this.prevMediaSeqCount}] -> [${this.mediaSeqCount}]`);
966
+ debug(`[${this.sessionId}]: ${instanceName}: Finished updating all Counts and Segment Queues!`);
967
+ return totalDur;
894
968
  }
895
969
 
896
970
  async _loadMediaManifest(bw) {
@@ -957,28 +1031,17 @@ class SessionLive {
957
1031
  //debug(`[${this.sessionId}]: Current RAW Mseq: [${m3u.get("mediaSequence")}]`);
958
1032
  //debug(`[${this.sessionId}]: Previous RAW Mseq: [${this.lastRequestedMediaSeqRaw}]`);
959
1033
 
960
- // TODO: UPDATE target for number of segments in a manifest, if appropriate.
961
- // if (m3u.items.PlaylistItem.length < this.targetNumSeg) {
962
- // debug(`[${this.sessionId}]: WE PLAN TO LOWER SEGMENT AMOUNT IN MANIFEST FROM (${this.targetNumSeg}) TO (${m3u.items.PlaylistItem.length})`);
963
- // this.targetNumSeg = m3u.items.PlaylistItem.length;
964
- // }
965
-
966
1034
  if (this.pushAmount >= 0) {
967
1035
  this.lastRequestedMediaSeqRaw = m3u.get("mediaSequence");
968
1036
  }
969
1037
  this.targetDuration = m3u.get("targetDuration");
970
- let amountRemovedDiscSeqs = 0;
971
1038
  let startIdx = m3u.items.PlaylistItem.length - this.pushAmount;
972
1039
  startIdx = startIdx < 0 ? 0 : startIdx;
973
1040
  if (mediaManifestUri) {
974
1041
  // push segments
975
- amountRemovedDiscSeqs = this._addLiveSegmentsToQueue(startIdx, m3u.items.PlaylistItem, baseUrl, liveTargetBandwidth);
1042
+ this._addLiveSegmentsToQueue(startIdx, m3u.items.PlaylistItem, baseUrl, liveTargetBandwidth);
976
1043
  }
977
- let resolveObj = {
978
- removedDiscSeqs: amountRemovedDiscSeqs,
979
- bw: liveTargetBandwidth,
980
- };
981
- resolve(resolveObj);
1044
+ resolve();
982
1045
  } catch (exc) {
983
1046
  console.error("ERROR: " + exc);
984
1047
  reject(exc);
@@ -986,8 +1049,15 @@ class SessionLive {
986
1049
  });
987
1050
  }
988
1051
 
1052
+ /**
1053
+ * Collects 'new' PlaylistItems and converts them into custom SegmentItems,
1054
+ * then Pushes them to the LiveSegQueue for all variants.
1055
+ * @param {number} startIdx
1056
+ * @param {m3u8.Item.PlaylistItem} playlistItems
1057
+ * @param {string} baseUrl
1058
+ * @param {string} liveTargetBandwidth
1059
+ */
989
1060
  _addLiveSegmentsToQueue(startIdx, playlistItems, baseUrl, liveTargetBandwidth) {
990
- let amountRemovedDiscSeqs = 0;
991
1061
  for (let i = startIdx; i < playlistItems.length; i++) {
992
1062
  debug(`[${this.sessionId}]: Adding Live Segment(s) to Queue (for bw=${liveTargetBandwidth})`);
993
1063
  let seg = {};
@@ -1056,30 +1126,11 @@ class SessionLive {
1056
1126
  if (daterangeData) {
1057
1127
  seg["daterange"] = daterangeData;
1058
1128
  }
1059
-
1129
+ // Push new Live Segments!
1060
1130
  this.liveSegQueue[liveTargetBandwidth].push(seg);
1061
- this.liveSegsForFollowers[liveTargetBandwidth].push(seg);
1062
- let segCount = 0;
1063
- this.liveSegQueue[liveTargetBandwidth].map((seg) => {
1064
- if (seg.uri) {
1065
- segCount++;
1066
- }
1067
- });
1068
- debug(`[${this.sessionId}]: size of queue=${segCount}_targetNumseg=${this.targetNumSeg}`);
1069
- if (segCount > this.targetNumSeg) {
1070
- debug(`[${this.sessionId}]: liveTargetBandwidth=${liveTargetBandwidth}_seg=${JSON.stringify(seg, null, 2)}`);
1071
- seg = this.liveSegQueue[liveTargetBandwidth].shift();
1072
- if (seg) {
1073
- if (seg.discontinuity) {
1074
- this.liveSegQueue[liveTargetBandwidth].shift();
1075
- amountRemovedDiscSeqs++;
1076
- }
1077
- }
1078
- debug(`[${this.sessionId}]: After Shifting -> size of queue=${segCount}_targetNumseg=${this.targetNumSeg}`);
1079
- }
1131
+ this.liveSegsForFollowers[liveTargetBandwidth].push(seg);
1080
1132
  }
1081
1133
  }
1082
- return amountRemovedDiscSeqs;
1083
1134
  }
1084
1135
 
1085
1136
  /*
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "3.0.7",
3
+ "version": "3.1.0",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -202,36 +202,42 @@ segment_0_8.ts
202
202
 
203
203
  const mockLiveSegments = {
204
204
  180000: [
205
- { duration: 7, uri: "http://mock.mock.com/180000/seg09.ts" },
206
- { duration: 7, uri: "http://mock.mock.com/180000/seg10.ts" },
207
- { duration: 7, uri: "http://mock.mock.com/180000/seg11.ts" },
208
- { duration: 7, uri: "http://mock.mock.com/180000/seg12.ts" },
209
- { duration: 7, uri: "http://mock.mock.com/180000/seg13.ts" },
210
- { duration: 7, uri: "http://mock.mock.com/180000/seg14.ts" },
211
- { duration: 7, uri: "http://mock.mock.com/180000/seg15.ts" },
212
- { duration: 7, uri: "http://mock.mock.com/180000/seg16.ts" },
205
+ { duration: 6, uri: "http://mock.mock.com/180000/seg09.ts" },
206
+ { duration: 6, uri: "http://mock.mock.com/180000/seg10.ts" },
207
+ { duration: 6, uri: "http://mock.mock.com/180000/seg11.ts" },
208
+ { duration: 6, uri: "http://mock.mock.com/180000/seg12.ts" },
209
+ { duration: 6, uri: "http://mock.mock.com/180000/seg13.ts" },
210
+ { duration: 6, uri: "http://mock.mock.com/180000/seg14.ts" },
211
+ { duration: 6, uri: "http://mock.mock.com/180000/seg15.ts" },
212
+ { duration: 6, uri: "http://mock.mock.com/180000/seg16.ts" },
213
+ { duration: 6, uri: "http://mock.mock.com/180000/seg17.ts" },
214
+ { duration: 6, uri: "http://mock.mock.com/180000/seg18.ts" },
213
215
  { discontinuity: true },
214
216
  ],
215
217
  1258000: [
216
- { duration: 7, uri: "http://mock.mock.com/180000/seg09.ts" },
217
- { duration: 7, uri: "http://mock.mock.com/180000/seg10.ts" },
218
- { duration: 7, uri: "http://mock.mock.com/180000/seg11.ts" },
219
- { duration: 7, uri: "http://mock.mock.com/180000/seg12.ts" },
220
- { duration: 7, uri: "http://mock.mock.com/180000/seg13.ts" },
221
- { duration: 7, uri: "http://mock.mock.com/180000/seg14.ts" },
222
- { duration: 7, uri: "http://mock.mock.com/180000/seg15.ts" },
223
- { duration: 7, uri: "http://mock.mock.com/180000/seg16.ts" },
218
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg09.ts" },
219
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg10.ts" },
220
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg11.ts" },
221
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg12.ts" },
222
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg13.ts" },
223
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg14.ts" },
224
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg15.ts" },
225
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg16.ts" },
226
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg17.ts" },
227
+ { duration: 6, uri: "http://mock.mock.com/1258000/seg18.ts" },
224
228
  { discontinuity: true },
225
229
  ],
226
230
  2488000: [
227
- { duration: 7, uri: "http://mock.mock.com/180000/seg09.ts" },
228
- { duration: 7, uri: "http://mock.mock.com/180000/seg10.ts" },
229
- { duration: 7, uri: "http://mock.mock.com/180000/seg11.ts" },
230
- { duration: 7, uri: "http://mock.mock.com/180000/seg12.ts" },
231
- { duration: 7, uri: "http://mock.mock.com/180000/seg13.ts" },
232
- { duration: 7, uri: "http://mock.mock.com/180000/seg14.ts" },
233
- { duration: 7, uri: "http://mock.mock.com/180000/seg15.ts" },
234
- { duration: 7, uri: "http://mock.mock.com/180000/seg16.ts" },
231
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg09.ts" },
232
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg10.ts" },
233
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg11.ts" },
234
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg12.ts" },
235
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg13.ts" },
236
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg14.ts" },
237
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg15.ts" },
238
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg16.ts" },
239
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg17.ts" },
240
+ { duration: 6, uri: "http://mock.mock.com/2488000/seg18.ts" },
235
241
  { discontinuity: true },
236
242
  ],
237
243
  };