eyevinn-channel-engine 3.0.3 → 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/README.md +3 -1
- package/engine/server.js +12 -6
- package/engine/session_live.js +65 -43
- package/engine/stream_switcher.js +15 -0
- package/package.json +1 -1
- package/server-livemix.js +1 -1
package/README.md
CHANGED
|
@@ -141,8 +141,9 @@ class MyStreamSwitchManager {
|
|
|
141
141
|
getSchedule(channelId) -> [ { eventId, assetId, title, type, start_time, end_time, uri, duration } ]
|
|
142
142
|
}
|
|
143
143
|
```
|
|
144
|
+
When using **Live Mixing** in **High-Availablilty** mode, it is important to know that the class's `getSchedule(channelId)` function will be called by each channel-engine instance. Meaning that it is crucial that the implementation of this function will return the same response for each and every channel-engine instance, as they are expecting to be recieving identical lists in order to be synced up for live mixing.
|
|
144
145
|
|
|
145
|
-
The class's `getSchedule(channelId)` function should return a list of
|
|
146
|
+
The class's `getSchedule(channelId)` function should return a promise containing the list of events as an object in the following format below:
|
|
146
147
|
|
|
147
148
|
```
|
|
148
149
|
{
|
|
@@ -208,6 +209,7 @@ Below are examples of a Live stream event and a VOD event respectively:
|
|
|
208
209
|
(*Important Note: When it comes to VOD events. Desired `duration` has priority over `end_time`. Meaning that you will only resume VOD2Live content after the asset in the VOD event has finished. This may change in the future.*)
|
|
209
210
|
|
|
210
211
|
|
|
212
|
+
|
|
211
213
|
Then create an instance of the class and reference it as the `streamSwitchManager` in your engineOptions, just like you'd do with the channel manager.
|
|
212
214
|
|
|
213
215
|
```
|
package/engine/server.js
CHANGED
|
@@ -167,12 +167,18 @@ class ChannelEngine {
|
|
|
167
167
|
|
|
168
168
|
const StreamSwitchLoop = async (timeIntervalMs) => {
|
|
169
169
|
while(true) {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
170
|
+
try {
|
|
171
|
+
const ts_1 = Date.now();
|
|
172
|
+
await this.updateStreamSwitchAsync()
|
|
173
|
+
const ts_2 = Date.now();
|
|
174
|
+
let interval = (timeIntervalMs - (ts_2 - ts_1)) < 0 ? 50 : (timeIntervalMs - (ts_2 - ts_1));
|
|
175
|
+
await timer(interval)
|
|
176
|
+
debug(`StreamSwitchLoop waited for all channels. Next tick in: ${interval}ms`)
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error(err)
|
|
179
|
+
debug(`StreamSwitchLoop iteration failed. Trying Again in 1000ms!`);
|
|
180
|
+
await timer(1000);
|
|
181
|
+
}
|
|
176
182
|
}
|
|
177
183
|
}
|
|
178
184
|
StreamSwitchLoop(this.streamSwitchTimeIntervalMs);
|
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 = {};
|
|
@@ -1018,16 +1032,17 @@ class SessionLive {
|
|
|
1018
1032
|
cueData["assetData"] = attributes["assetData"];
|
|
1019
1033
|
}
|
|
1020
1034
|
if ("daterange" in attributes) {
|
|
1021
|
-
if(!daterangeData) {
|
|
1022
|
-
daterangeData = {}
|
|
1035
|
+
if (!daterangeData) {
|
|
1036
|
+
daterangeData = {};
|
|
1023
1037
|
}
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1038
|
+
let allDaterangeAttributes = Object.keys(attributes["daterange"]);
|
|
1039
|
+
allDaterangeAttributes.forEach((attr) => {
|
|
1040
|
+
if (attr.match(/DURATION$/)) {
|
|
1041
|
+
daterangeData[attr.toLowerCase()] = parseFloat(attributes["daterange"][attr]);
|
|
1042
|
+
} else {
|
|
1043
|
+
daterangeData[attr.toLowerCase()] = attributes["daterange"][attr];
|
|
1044
|
+
}
|
|
1045
|
+
});
|
|
1031
1046
|
}
|
|
1032
1047
|
if (playlistItem.properties.uri) {
|
|
1033
1048
|
if (playlistItem.properties.uri.match("^http")) {
|
|
@@ -1045,10 +1060,14 @@ class SessionLive {
|
|
|
1045
1060
|
this.liveSegQueue[liveTargetBandwidth].push(seg);
|
|
1046
1061
|
this.liveSegsForFollowers[liveTargetBandwidth].push(seg);
|
|
1047
1062
|
let segCount = 0;
|
|
1048
|
-
this.liveSegQueue[liveTargetBandwidth].map((seg) => {
|
|
1063
|
+
this.liveSegQueue[liveTargetBandwidth].map((seg) => {
|
|
1064
|
+
if (seg.uri) {
|
|
1065
|
+
segCount++;
|
|
1066
|
+
}
|
|
1067
|
+
});
|
|
1049
1068
|
debug(`[${this.sessionId}]: size of queue=${segCount}_targetNumseg=${this.targetNumSeg}`);
|
|
1050
1069
|
if (segCount > this.targetNumSeg) {
|
|
1051
|
-
debug(`[${this.sessionId}]: liveTargetBandwidth=${liveTargetBandwidth}_seg=${JSON.stringify(seg,null,2)}`);
|
|
1070
|
+
debug(`[${this.sessionId}]: liveTargetBandwidth=${liveTargetBandwidth}_seg=${JSON.stringify(seg, null, 2)}`);
|
|
1052
1071
|
seg = this.liveSegQueue[liveTargetBandwidth].shift();
|
|
1053
1072
|
if (seg) {
|
|
1054
1073
|
if (seg.discontinuity) {
|
|
@@ -1078,8 +1097,7 @@ class SessionLive {
|
|
|
1078
1097
|
}
|
|
1079
1098
|
const liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
|
|
1080
1099
|
const vodTargetBandwidth = this._findNearestBw(bw, Object.keys(this.vodSegments));
|
|
1081
|
-
debug(`[${this.sessionId}]: Client requesting manifest for bw=(${bw}). Nearest LiveBw=(${liveTargetBandwidth})`)
|
|
1082
|
-
|
|
1100
|
+
debug(`[${this.sessionId}]: Client requesting manifest for bw=(${bw}). Nearest LiveBw=(${liveTargetBandwidth})`);
|
|
1083
1101
|
|
|
1084
1102
|
if (this.blockGenerateManifest) {
|
|
1085
1103
|
debug(`[${this.sessionId}]: FOLLOWER: Cannot Generate Manifest! Waiting to sync-up with Leader...`);
|
|
@@ -1153,7 +1171,7 @@ class SessionLive {
|
|
|
1153
1171
|
m3u8 += "#EXT-X-DISCONTINUITY\n";
|
|
1154
1172
|
}
|
|
1155
1173
|
if (seg.cue) {
|
|
1156
|
-
if(seg.cue.out) {
|
|
1174
|
+
if (seg.cue.out) {
|
|
1157
1175
|
if (seg.cue.scteData) {
|
|
1158
1176
|
m3u8 += "#EXT-OATCLS-SCTE35:" + seg.cue.scteData + "\n";
|
|
1159
1177
|
}
|
|
@@ -1165,21 +1183,25 @@ class SessionLive {
|
|
|
1165
1183
|
if (seg.cue.cont) {
|
|
1166
1184
|
if (seg.cue.scteData) {
|
|
1167
1185
|
m3u8 += "#EXT-X-CUE-OUT-CONT:ElapsedTime=" + seg.cue.cont + ",Duration=" + seg.cue.duration + ",SCTE35=" + seg.cue.scteData + "\n";
|
|
1168
|
-
}
|
|
1169
|
-
else {
|
|
1186
|
+
} else {
|
|
1170
1187
|
m3u8 += "#EXT-X-CUE-OUT-CONT:" + seg.cue.cont + "/" + seg.cue.duration + "\n";
|
|
1171
1188
|
}
|
|
1172
1189
|
}
|
|
1173
1190
|
}
|
|
1191
|
+
if (seg.datetime) {
|
|
1192
|
+
m3u8 += `#EXT-X-PROGRAM-DATE-TIME:${seg.datetime}\n`;
|
|
1193
|
+
}
|
|
1174
1194
|
if (seg.daterange) {
|
|
1175
|
-
const dateRangeAttributes = Object.keys(seg.daterange)
|
|
1176
|
-
|
|
1177
|
-
|
|
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";
|
|
1178
1200
|
}
|
|
1179
1201
|
m3u8 += "#EXT-X-DATERANGE:" + dateRangeAttributes + "\n";
|
|
1180
1202
|
}
|
|
1181
1203
|
// Mimick logic used in hls-vodtolive
|
|
1182
|
-
if (seg.cue && seg.cue.in){
|
|
1204
|
+
if (seg.cue && seg.cue.in) {
|
|
1183
1205
|
m3u8 += "#EXT-X-CUE-IN" + "\n";
|
|
1184
1206
|
}
|
|
1185
1207
|
if (seg.uri) {
|
|
@@ -1230,13 +1252,13 @@ class SessionLive {
|
|
|
1230
1252
|
const profiles = this.sessionLiveProfile;
|
|
1231
1253
|
const toKeep = new Set();
|
|
1232
1254
|
let newItem = {};
|
|
1233
|
-
profiles.forEach(profile => {
|
|
1255
|
+
profiles.forEach((profile) => {
|
|
1234
1256
|
let bwToKeep = this._findNearestBw(profile.bw, Object.keys(this.mediaManifestURIs));
|
|
1235
1257
|
toKeep.add(bwToKeep);
|
|
1236
1258
|
});
|
|
1237
1259
|
toKeep.forEach((bw) => {
|
|
1238
1260
|
newItem[bw] = this.mediaManifestURIs[bw];
|
|
1239
|
-
})
|
|
1261
|
+
});
|
|
1240
1262
|
this.mediaManifestURIs = newItem;
|
|
1241
1263
|
}
|
|
1242
1264
|
|
|
@@ -278,6 +278,9 @@ class StreamSwitcher {
|
|
|
278
278
|
debug(`[${this.sessionId}]: [ Switched from V2L->LIVE ]`);
|
|
279
279
|
return true;
|
|
280
280
|
} catch (err) {
|
|
281
|
+
this.streamTypeLive = false;
|
|
282
|
+
this.working = false;
|
|
283
|
+
this.eventId = null;
|
|
281
284
|
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->LIVE ]`);
|
|
282
285
|
throw new Error(err);
|
|
283
286
|
}
|
|
@@ -309,6 +312,9 @@ class StreamSwitcher {
|
|
|
309
312
|
debug(`[${this.sessionId}]: [ Switched from V2L->VOD ]`);
|
|
310
313
|
return false;
|
|
311
314
|
} catch (err) {
|
|
315
|
+
this.streamTypeLive = false;
|
|
316
|
+
this.working = false;
|
|
317
|
+
this.eventId = null;
|
|
312
318
|
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->VOD ]`);
|
|
313
319
|
throw new Error(err);
|
|
314
320
|
}
|
|
@@ -349,6 +355,9 @@ class StreamSwitcher {
|
|
|
349
355
|
debug(`[${this.sessionId}]: [ Switched from LIVE->V2L ]`);
|
|
350
356
|
return false;
|
|
351
357
|
} catch (err) {
|
|
358
|
+
this.streamTypeLive = false;
|
|
359
|
+
this.working = false;
|
|
360
|
+
this.eventId = null;
|
|
352
361
|
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->V2L ]`);
|
|
353
362
|
throw new Error(err);
|
|
354
363
|
}
|
|
@@ -389,6 +398,9 @@ class StreamSwitcher {
|
|
|
389
398
|
debug(`[${this.sessionId}]: Switched from LIVE->VOD`);
|
|
390
399
|
return false;
|
|
391
400
|
} catch (err) {
|
|
401
|
+
this.streamTypeLive = false;
|
|
402
|
+
this.working = false;
|
|
403
|
+
this.eventId = null;
|
|
392
404
|
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->VOD ]`);
|
|
393
405
|
throw new Error(err);
|
|
394
406
|
}
|
|
@@ -425,6 +437,9 @@ class StreamSwitcher {
|
|
|
425
437
|
debug(`[${this.sessionId}]: Switched from LIVE->LIVE`);
|
|
426
438
|
return true;
|
|
427
439
|
} catch (err) {
|
|
440
|
+
this.streamTypeLive = false;
|
|
441
|
+
this.working = false;
|
|
442
|
+
this.eventId = null;
|
|
428
443
|
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->LIVE ]`);
|
|
429
444
|
throw new Error(err);
|
|
430
445
|
}
|
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(),
|