eyevinn-channel-engine 3.0.5 → 3.0.6
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_live.js +52 -34
- package/package.json +1 -1
- package/server-livemix.js +1 -1
package/engine/session_live.js
CHANGED
|
@@ -136,7 +136,7 @@ class SessionLive {
|
|
|
136
136
|
if (!this.masterManifestUri) {
|
|
137
137
|
await timer(3000);
|
|
138
138
|
continue;
|
|
139
|
-
}
|
|
139
|
+
}
|
|
140
140
|
if (this.playheadState === PlayheadState.STOPPED) {
|
|
141
141
|
debug(`[${this.sessionId}]: Playhead has Stopped, clearing local session and store.`);
|
|
142
142
|
this.waitForPlayhead = false;
|
|
@@ -208,7 +208,7 @@ class SessionLive {
|
|
|
208
208
|
while (!this.masterManifestUri && attempts > 0) {
|
|
209
209
|
attempts--;
|
|
210
210
|
try {
|
|
211
|
-
debug(`[${this.instanceId}][${this.sessionId}]: Going to fetch Live Master Manifest!`)
|
|
211
|
+
debug(`[${this.instanceId}][${this.sessionId}]: Going to fetch Live Master Manifest!`);
|
|
212
212
|
// Load & Parse all Media Manifest URIs from Master
|
|
213
213
|
await this._loadMasterManifest(masterManifestUri);
|
|
214
214
|
this.masterManifestUri = masterManifestUri;
|
|
@@ -390,7 +390,7 @@ class SessionLive {
|
|
|
390
390
|
// Generate manifest to give to client
|
|
391
391
|
async getCurrentMediaManifestAsync(bw) {
|
|
392
392
|
if (!this.sessionLiveState) {
|
|
393
|
-
throw new Error(
|
|
393
|
+
throw new Error("SessionLive not ready");
|
|
394
394
|
}
|
|
395
395
|
if (bw === null) {
|
|
396
396
|
debug(`[${this.sessionId}]: No bandwidth provided`);
|
|
@@ -488,7 +488,11 @@ class SessionLive {
|
|
|
488
488
|
const size = this.liveSegsForFollowers[liveBws[0]].length;
|
|
489
489
|
|
|
490
490
|
let newSegCount = 0;
|
|
491
|
-
this.liveSegsForFollowers[liveBws[0]].map((seg) => {
|
|
491
|
+
this.liveSegsForFollowers[liveBws[0]].map((seg) => {
|
|
492
|
+
if (seg.uri) {
|
|
493
|
+
newSegCount++;
|
|
494
|
+
}
|
|
495
|
+
});
|
|
492
496
|
|
|
493
497
|
debug(`[${this.sessionId}]: FOLLOWER: liveSegsForFollowers Bws=${Object.keys(this.liveSegsForFollowers)}_newSegCount=${newSegCount} `);
|
|
494
498
|
// Remove transitional segs & add live source segs collected from store
|
|
@@ -509,7 +513,7 @@ class SessionLive {
|
|
|
509
513
|
// Push to bottom, new live source segment on all variants
|
|
510
514
|
for (let segIdx = 0; segIdx < size; segIdx++) {
|
|
511
515
|
let incrementDiscSeqCount = false;
|
|
512
|
-
liveBws.forEach((bw)=>{
|
|
516
|
+
liveBws.forEach((bw) => {
|
|
513
517
|
let liveSegFromLeader = this.liveSegsForFollowers[bw][segIdx];
|
|
514
518
|
if (!this.liveSegQueue[bw]) {
|
|
515
519
|
this.liveSegQueue[bw] = [];
|
|
@@ -517,7 +521,11 @@ class SessionLive {
|
|
|
517
521
|
this.liveSegQueue[bw].push(liveSegFromLeader);
|
|
518
522
|
|
|
519
523
|
let segCount = 0;
|
|
520
|
-
this.liveSegQueue[bw].map((seg) => {
|
|
524
|
+
this.liveSegQueue[bw].map((seg) => {
|
|
525
|
+
if (seg.uri) {
|
|
526
|
+
segCount++;
|
|
527
|
+
}
|
|
528
|
+
});
|
|
521
529
|
if (segCount > this.targetNumSeg) {
|
|
522
530
|
let seg = this.liveSegQueue[bw].shift();
|
|
523
531
|
if (seg && seg.discontinuity) {
|
|
@@ -651,7 +659,7 @@ class SessionLive {
|
|
|
651
659
|
}
|
|
652
660
|
|
|
653
661
|
// Handle if any promise got rejected
|
|
654
|
-
if (manifestList.some(result => result.status === "rejected")) {
|
|
662
|
+
if (manifestList.some((result) => result.status === "rejected")) {
|
|
655
663
|
debug(`[${this.sessionId}]: ALERT! Promises I: Failed, Rejection Found! Trying again...`);
|
|
656
664
|
continue;
|
|
657
665
|
}
|
|
@@ -663,16 +671,21 @@ class SessionLive {
|
|
|
663
671
|
return item.value.mediaSeq;
|
|
664
672
|
});
|
|
665
673
|
|
|
666
|
-
|
|
667
674
|
// Handle if mediaSeqCounts are NOT synced up!
|
|
668
675
|
if (!allMediaSeqCounts.every((val, i, arr) => val === arr[0])) {
|
|
669
|
-
|
|
670
676
|
debug(`[${this.sessionId}]: Live Mseq counts=[${allMediaSeqCounts}]`);
|
|
671
677
|
// Decrement fetch counter
|
|
672
678
|
FETCH_ATTEMPTS--;
|
|
679
|
+
// Calculate retry delay time. Default=1500
|
|
680
|
+
let retryDelayMs = 1500;
|
|
681
|
+
if (Object.keys(this.liveSegQueue).length > 0) {
|
|
682
|
+
const firstBw = Object.keys(this.liveSegQueue)[0];
|
|
683
|
+
const lastIdx = this.liveSegQueue[firstBw].length - 1;
|
|
684
|
+
retryDelayMs = this.liveSegQueue[firstBw][lastIdx].duration * 1000 * 0.25;
|
|
685
|
+
}
|
|
673
686
|
// Wait a little before trying again
|
|
674
|
-
debug(`[${this.sessionId}]: ALERT! Live Source Data NOT in sync! Will try again after
|
|
675
|
-
await timer(
|
|
687
|
+
debug(`[${this.sessionId}]: ALERT! Live Source Data NOT in sync! Will try again after ${retryDelayMs}ms`);
|
|
688
|
+
await timer(retryDelayMs);
|
|
676
689
|
this.timerCompensation = false;
|
|
677
690
|
continue;
|
|
678
691
|
}
|
|
@@ -681,7 +694,7 @@ class SessionLive {
|
|
|
681
694
|
let leadersFirstSeqCounts = await this.sessionLiveState.get("firstCounts");
|
|
682
695
|
let tries = 20;
|
|
683
696
|
|
|
684
|
-
while (!leadersFirstSeqCounts.liveSourceMseqCount && tries > 0 ||
|
|
697
|
+
while ((!leadersFirstSeqCounts.liveSourceMseqCount && tries > 0) || leadersFirstSeqCounts.liveSourceMseqCount === 0) {
|
|
685
698
|
debug(`[${this.sessionId}]: NEW FOLLOWER: Waiting for LEADER to add 'firstCounts' in store! Will look again after 1000ms (tries left=${tries})`);
|
|
686
699
|
await timer(1000);
|
|
687
700
|
leadersFirstSeqCounts = await this.sessionLiveState.get("firstCounts");
|
|
@@ -727,7 +740,7 @@ class SessionLive {
|
|
|
727
740
|
this.pushAmount = 1; // Follower from start
|
|
728
741
|
} else {
|
|
729
742
|
// RESPAWNED NODES
|
|
730
|
-
this.pushAmount =
|
|
743
|
+
this.pushAmount = allMediaSeqCounts[0] - leadersFirstSeqCounts.liveSourceMseqCount + 1;
|
|
731
744
|
|
|
732
745
|
const transitSegs = await this.sessionLiveState.get("transitSegs");
|
|
733
746
|
//debug(`[${this.sessionId}]: NEW FOLLOWER: I tried to get 'transitSegs'. This is what I found ${JSON.stringify(transitSegs)}`);
|
|
@@ -760,7 +773,7 @@ class SessionLive {
|
|
|
760
773
|
}
|
|
761
774
|
|
|
762
775
|
if (FETCH_ATTEMPTS === 0) {
|
|
763
|
-
debug(`[${this.sessionId}]: Fetching from Live-Source did not work! Returning to Playhead Loop...`)
|
|
776
|
+
debug(`[${this.sessionId}]: Fetching from Live-Source did not work! Returning to Playhead Loop...`);
|
|
764
777
|
return;
|
|
765
778
|
}
|
|
766
779
|
|
|
@@ -781,8 +794,7 @@ class SessionLive {
|
|
|
781
794
|
this.firstTime = false;
|
|
782
795
|
debug(`[${this.sessionId}]: Got all needed segments from live-source (read from store).\nWe are now able to build Live Manifest: [${this.mediaSeqCount}]`);
|
|
783
796
|
return;
|
|
784
|
-
}
|
|
785
|
-
else if (leadersCurrentMseqRaw < this.lastRequestedMediaSeqRaw) {
|
|
797
|
+
} else if (leadersCurrentMseqRaw < this.lastRequestedMediaSeqRaw) {
|
|
786
798
|
// WE ARE A RESPAWN-NODE, and we are ahead of leader.
|
|
787
799
|
this.blockGenerateManifest = true;
|
|
788
800
|
}
|
|
@@ -798,7 +810,7 @@ class SessionLive {
|
|
|
798
810
|
}
|
|
799
811
|
// Segment Pushing
|
|
800
812
|
debug(`[${this.sessionId}]: Executing Promises II: Segment Pushing`);
|
|
801
|
-
let results = await allSettled(pushPromises)
|
|
813
|
+
let results = await allSettled(pushPromises); //await Promise.all(pushPromises);
|
|
802
814
|
const removedDiscSeqList = results.map((item) => {
|
|
803
815
|
if (item.status === "rejected") {
|
|
804
816
|
return -1;
|
|
@@ -951,7 +963,9 @@ class SessionLive {
|
|
|
951
963
|
// this.targetNumSeg = m3u.items.PlaylistItem.length;
|
|
952
964
|
// }
|
|
953
965
|
|
|
954
|
-
this.
|
|
966
|
+
if (this.pushAmount >= 0) {
|
|
967
|
+
this.lastRequestedMediaSeqRaw = m3u.get("mediaSequence");
|
|
968
|
+
}
|
|
955
969
|
this.targetDuration = m3u.get("targetDuration");
|
|
956
970
|
let amountRemovedDiscSeqs = 0;
|
|
957
971
|
let startIdx = m3u.items.PlaylistItem.length - this.pushAmount;
|
|
@@ -962,8 +976,8 @@ class SessionLive {
|
|
|
962
976
|
}
|
|
963
977
|
let resolveObj = {
|
|
964
978
|
removedDiscSeqs: amountRemovedDiscSeqs,
|
|
965
|
-
bw: liveTargetBandwidth
|
|
966
|
-
}
|
|
979
|
+
bw: liveTargetBandwidth,
|
|
980
|
+
};
|
|
967
981
|
resolve(resolveObj);
|
|
968
982
|
} catch (exc) {
|
|
969
983
|
console.error("ERROR: " + exc);
|
|
@@ -998,7 +1012,7 @@ class SessionLive {
|
|
|
998
1012
|
}
|
|
999
1013
|
cueData["out"] = true;
|
|
1000
1014
|
cueData["duration"] = attributes["cueout"];
|
|
1001
|
-
|
|
1015
|
+
}
|
|
1002
1016
|
if ("cuecont" in attributes) {
|
|
1003
1017
|
if (!cueData) {
|
|
1004
1018
|
cueData = {};
|
|
@@ -1046,10 +1060,14 @@ class SessionLive {
|
|
|
1046
1060
|
this.liveSegQueue[liveTargetBandwidth].push(seg);
|
|
1047
1061
|
this.liveSegsForFollowers[liveTargetBandwidth].push(seg);
|
|
1048
1062
|
let segCount = 0;
|
|
1049
|
-
this.liveSegQueue[liveTargetBandwidth].map((seg) => {
|
|
1063
|
+
this.liveSegQueue[liveTargetBandwidth].map((seg) => {
|
|
1064
|
+
if (seg.uri) {
|
|
1065
|
+
segCount++;
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1050
1068
|
debug(`[${this.sessionId}]: size of queue=${segCount}_targetNumseg=${this.targetNumSeg}`);
|
|
1051
1069
|
if (segCount > this.targetNumSeg) {
|
|
1052
|
-
debug(`[${this.sessionId}]: liveTargetBandwidth=${liveTargetBandwidth}_seg=${JSON.stringify(seg,null,2)}`);
|
|
1070
|
+
debug(`[${this.sessionId}]: liveTargetBandwidth=${liveTargetBandwidth}_seg=${JSON.stringify(seg, null, 2)}`);
|
|
1053
1071
|
seg = this.liveSegQueue[liveTargetBandwidth].shift();
|
|
1054
1072
|
if (seg) {
|
|
1055
1073
|
if (seg.discontinuity) {
|
|
@@ -1079,8 +1097,7 @@ class SessionLive {
|
|
|
1079
1097
|
}
|
|
1080
1098
|
const liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
|
|
1081
1099
|
const vodTargetBandwidth = this._findNearestBw(bw, Object.keys(this.vodSegments));
|
|
1082
|
-
debug(`[${this.sessionId}]: Client requesting manifest for bw=(${bw}). Nearest LiveBw=(${liveTargetBandwidth})`)
|
|
1083
|
-
|
|
1100
|
+
debug(`[${this.sessionId}]: Client requesting manifest for bw=(${bw}). Nearest LiveBw=(${liveTargetBandwidth})`);
|
|
1084
1101
|
|
|
1085
1102
|
if (this.blockGenerateManifest) {
|
|
1086
1103
|
debug(`[${this.sessionId}]: FOLLOWER: Cannot Generate Manifest! Waiting to sync-up with Leader...`);
|
|
@@ -1154,7 +1171,7 @@ class SessionLive {
|
|
|
1154
1171
|
m3u8 += "#EXT-X-DISCONTINUITY\n";
|
|
1155
1172
|
}
|
|
1156
1173
|
if (seg.cue) {
|
|
1157
|
-
if(seg.cue.out) {
|
|
1174
|
+
if (seg.cue.out) {
|
|
1158
1175
|
if (seg.cue.scteData) {
|
|
1159
1176
|
m3u8 += "#EXT-OATCLS-SCTE35:" + seg.cue.scteData + "\n";
|
|
1160
1177
|
}
|
|
@@ -1166,8 +1183,7 @@ class SessionLive {
|
|
|
1166
1183
|
if (seg.cue.cont) {
|
|
1167
1184
|
if (seg.cue.scteData) {
|
|
1168
1185
|
m3u8 += "#EXT-X-CUE-OUT-CONT:ElapsedTime=" + seg.cue.cont + ",Duration=" + seg.cue.duration + ",SCTE35=" + seg.cue.scteData + "\n";
|
|
1169
|
-
}
|
|
1170
|
-
else {
|
|
1186
|
+
} else {
|
|
1171
1187
|
m3u8 += "#EXT-X-CUE-OUT-CONT:" + seg.cue.cont + "/" + seg.cue.duration + "\n";
|
|
1172
1188
|
}
|
|
1173
1189
|
}
|
|
@@ -1176,14 +1192,16 @@ class SessionLive {
|
|
|
1176
1192
|
m3u8 += `#EXT-X-PROGRAM-DATE-TIME:${seg.datetime}\n`;
|
|
1177
1193
|
}
|
|
1178
1194
|
if (seg.daterange) {
|
|
1179
|
-
const dateRangeAttributes = Object.keys(seg.daterange)
|
|
1180
|
-
|
|
1181
|
-
|
|
1195
|
+
const dateRangeAttributes = Object.keys(seg.daterange)
|
|
1196
|
+
.map((key) => daterangeAttribute(key, seg.daterange[key]))
|
|
1197
|
+
.join(",");
|
|
1198
|
+
if (!seg.datetime && seg.daterange["start-date"]) {
|
|
1199
|
+
m3u8 += "#EXT-X-PROGRAM-DATE-TIME:" + seg.daterange["start-date"] + "\n";
|
|
1182
1200
|
}
|
|
1183
1201
|
m3u8 += "#EXT-X-DATERANGE:" + dateRangeAttributes + "\n";
|
|
1184
1202
|
}
|
|
1185
1203
|
// Mimick logic used in hls-vodtolive
|
|
1186
|
-
if (seg.cue && seg.cue.in){
|
|
1204
|
+
if (seg.cue && seg.cue.in) {
|
|
1187
1205
|
m3u8 += "#EXT-X-CUE-IN" + "\n";
|
|
1188
1206
|
}
|
|
1189
1207
|
if (seg.uri) {
|
|
@@ -1234,13 +1252,13 @@ class SessionLive {
|
|
|
1234
1252
|
const profiles = this.sessionLiveProfile;
|
|
1235
1253
|
const toKeep = new Set();
|
|
1236
1254
|
let newItem = {};
|
|
1237
|
-
profiles.forEach(profile => {
|
|
1255
|
+
profiles.forEach((profile) => {
|
|
1238
1256
|
let bwToKeep = this._findNearestBw(profile.bw, Object.keys(this.mediaManifestURIs));
|
|
1239
1257
|
toKeep.add(bwToKeep);
|
|
1240
1258
|
});
|
|
1241
1259
|
toKeep.forEach((bw) => {
|
|
1242
1260
|
newItem[bw] = this.mediaManifestURIs[bw];
|
|
1243
|
-
})
|
|
1261
|
+
});
|
|
1244
1262
|
this.mediaManifestURIs = newItem;
|
|
1245
1263
|
}
|
|
1246
1264
|
|
package/package.json
CHANGED
package/server-livemix.js
CHANGED
|
@@ -155,7 +155,7 @@ class StreamSwitchManager {
|
|
|
155
155
|
type: StreamType.LIVE,
|
|
156
156
|
start_time: startOffset,
|
|
157
157
|
end_time: endTime,
|
|
158
|
-
uri: "https://
|
|
158
|
+
uri: "https://d2fz24s2fts31b.cloudfront.net/out/v1/2e5b17b7dd0548c7b844eb1b311b6434/manifest.m3u8",
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
161
|
eventId: this.generateID(),
|