eyevinn-channel-engine 3.0.0 → 3.0.4
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/.github/workflows/nodejs.yml +1 -1
- package/README.md +11 -5
- package/engine/server.js +24 -10
- package/engine/session.js +21 -7
- package/engine/session_live.js +117 -131
- package/engine/session_live_state.js +1 -1
- package/engine/stream_switcher.js +234 -121
- package/package.json +5 -4
- package/server-livemix.js +195 -0
- package/server.js +36 -128
- package/spec/engine/init_switching_spec.js +4 -2
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const debug = require("debug")("engine-
|
|
1
|
+
const debug = require("debug")("engine-stream-switcher");
|
|
2
2
|
const crypto = require("crypto");
|
|
3
3
|
const fetch = require("node-fetch");
|
|
4
4
|
const { AbortController } = require("abort-controller");
|
|
@@ -30,6 +30,7 @@ class StreamSwitcher {
|
|
|
30
30
|
this.timeDiff = null;
|
|
31
31
|
this.abortTimeStamp = null;
|
|
32
32
|
|
|
33
|
+
|
|
33
34
|
if (config) {
|
|
34
35
|
if (config.sessionId) {
|
|
35
36
|
this.sessionId = config.sessionId;
|
|
@@ -50,6 +51,26 @@ class StreamSwitcher {
|
|
|
50
51
|
return this.eventId;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
async abortLiveFeed(session, sessionLive, message) {
|
|
55
|
+
if (this.streamTypeLive) {
|
|
56
|
+
let status = null;
|
|
57
|
+
debug(`[${this.sessionId}]: Abort Live Stream! Reason: ${message}`);
|
|
58
|
+
try {
|
|
59
|
+
this.abortTimeStamp = Date.now();
|
|
60
|
+
status = await this._initSwitching(
|
|
61
|
+
SwitcherState.LIVE_TO_V2L,
|
|
62
|
+
session,
|
|
63
|
+
sessionLive,
|
|
64
|
+
null
|
|
65
|
+
);
|
|
66
|
+
return status;
|
|
67
|
+
} catch (err) {
|
|
68
|
+
debug(`Failed to force a switch off live feed: ${err}`);
|
|
69
|
+
throw new Error(err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
53
74
|
/**
|
|
54
75
|
*
|
|
55
76
|
* @param {Session} session The VOD2Live Session object.
|
|
@@ -57,6 +78,7 @@ class StreamSwitcher {
|
|
|
57
78
|
* @returns A bool, true if streamSwitchManager contains current Live event to be played else false.
|
|
58
79
|
*/
|
|
59
80
|
async streamSwitcher(session, sessionLive) {
|
|
81
|
+
let status = null;
|
|
60
82
|
if (!this.streamSwitchManager) {
|
|
61
83
|
debug(`[${this.sessionId}]: No streamSwitchManager available`);
|
|
62
84
|
return false;
|
|
@@ -65,11 +87,28 @@ class StreamSwitcher {
|
|
|
65
87
|
debug(`[${this.sessionId}]: streamSwitcher is currently busy`);
|
|
66
88
|
return null;
|
|
67
89
|
}
|
|
68
|
-
|
|
90
|
+
// Handle Complete Storage Reset
|
|
91
|
+
let sessionState = await session._sessionState.get("state");
|
|
92
|
+
if (sessionState === 1 || !sessionState) {
|
|
93
|
+
this.working = true;
|
|
94
|
+
sessionLive.waitForPlayhead = false;
|
|
95
|
+
sessionLive.allowedToSet = false; // only have effect on leader
|
|
96
|
+
await sessionLive.resetSession();
|
|
97
|
+
await sessionLive.resetLiveStoreAsync(0);
|
|
98
|
+
this.working = false;
|
|
99
|
+
this.abortTimeStamp = Date.now() + 20 * 1000; // 30 second V2L->LIVE timeout
|
|
100
|
+
if (this.streamTypeLive) {
|
|
101
|
+
debug(`[${this.sessionId}]: [ Ending LIVE Abruptly, Going to -> V2L ]`);
|
|
102
|
+
this.streamTypeLive = false;
|
|
103
|
+
}
|
|
104
|
+
debug(`[${this.sessionId}]: StreamSwitcher reacting to Full Store Reset`);
|
|
105
|
+
return false; // Go to V2L feed
|
|
106
|
+
}
|
|
107
|
+
|
|
69
108
|
// Filter out schedule objects from the past
|
|
70
|
-
const tsNow = Date.now();
|
|
109
|
+
const tsNow = Date.now();
|
|
71
110
|
const strmSchedule = await this.streamSwitchManager.getSchedule(this.sessionId);
|
|
72
|
-
const schedule = strmSchedule.filter((obj) => obj.end_time > tsNow);
|
|
111
|
+
const schedule = strmSchedule.filter((obj) => obj.end_time > tsNow);
|
|
73
112
|
if (schedule.length === 0 && this.streamTypeLive) {
|
|
74
113
|
status = await this._initSwitching(
|
|
75
114
|
SwitcherState.LIVE_TO_V2L,
|
|
@@ -102,47 +141,54 @@ class StreamSwitcher {
|
|
|
102
141
|
let tries = 0;
|
|
103
142
|
let validURI = false;
|
|
104
143
|
while (!validURI && tries < MAX_FAILS) {
|
|
105
|
-
debug(
|
|
144
|
+
debug(
|
|
145
|
+
`[${
|
|
146
|
+
this.sessionId
|
|
147
|
+
}]: Switcher is validating Master URI... (tries left=${
|
|
148
|
+
MAX_FAILS - tries
|
|
149
|
+
})`
|
|
150
|
+
);
|
|
106
151
|
validURI = await this._validURI(scheduleObj.uri);
|
|
107
152
|
tries++;
|
|
108
153
|
}
|
|
109
154
|
if (!validURI) {
|
|
110
155
|
debug(`[${this.sessionId}]: Unreachable URI: [${scheduleObj.uri}]`);
|
|
111
156
|
if (this.streamTypeLive) {
|
|
112
|
-
|
|
113
|
-
this.abortTimeStamp = Date.now();
|
|
114
|
-
status = await this._initSwitching(
|
|
115
|
-
SwitcherState.LIVE_TO_V2L,
|
|
157
|
+
await this.abortLiveFeed(
|
|
116
158
|
session,
|
|
117
159
|
sessionLive,
|
|
118
|
-
|
|
160
|
+
"Switching back to VOD2Live due to unreachable URI"
|
|
119
161
|
);
|
|
120
|
-
return status;
|
|
121
162
|
}
|
|
122
163
|
return false;
|
|
123
164
|
}
|
|
124
165
|
debug(`[${this.sessionId}]: ....Master URI -> VALID`);
|
|
125
166
|
|
|
126
|
-
|
|
127
|
-
if (this.abortTimeStamp && (tsNow - this.abortTimeStamp) <= 10000) {
|
|
167
|
+
if (this.abortTimeStamp && tsNow - this.abortTimeStamp <= 10000) {
|
|
128
168
|
// If we have a valid URI and no more than 10 seconds have passed since switching from Live->V2L.
|
|
129
169
|
// Stay on V2L to give live sessionLive some time to prepare before switching back to live.
|
|
130
|
-
debug(
|
|
170
|
+
debug(
|
|
171
|
+
`[${this.sessionId}]: Waiting [${
|
|
172
|
+
10000 - (tsNow - this.abortTimeStamp)
|
|
173
|
+
}ms] before switching back to Live due to unreachable URI`
|
|
174
|
+
);
|
|
131
175
|
return false;
|
|
132
|
-
}
|
|
176
|
+
}
|
|
133
177
|
this.abortTimeStamp = null;
|
|
134
178
|
|
|
135
179
|
if (this.streamTypeLive) {
|
|
136
|
-
if (
|
|
180
|
+
if (
|
|
181
|
+
tsNow >= scheduleObj.start_time &&
|
|
182
|
+
this.eventId !== scheduleObj.eventId
|
|
183
|
+
) {
|
|
137
184
|
if (scheduleObj.type === StreamType.LIVE) {
|
|
138
|
-
status =
|
|
185
|
+
status = await this._initSwitching(
|
|
139
186
|
SwitcherState.LIVE_TO_LIVE,
|
|
140
187
|
session,
|
|
141
188
|
sessionLive,
|
|
142
189
|
scheduleObj
|
|
143
190
|
);
|
|
144
191
|
return status;
|
|
145
|
-
|
|
146
192
|
}
|
|
147
193
|
status = await this._initSwitching(
|
|
148
194
|
SwitcherState.LIVE_TO_VOD,
|
|
@@ -153,7 +199,11 @@ class StreamSwitcher {
|
|
|
153
199
|
return status;
|
|
154
200
|
}
|
|
155
201
|
}
|
|
156
|
-
if (
|
|
202
|
+
if (
|
|
203
|
+
tsNow >= scheduleObj.start_time &&
|
|
204
|
+
tsNow < scheduleObj.end_time &&
|
|
205
|
+
scheduleObj.end_time - tsNow > 10000
|
|
206
|
+
) {
|
|
157
207
|
if (scheduleObj.type === StreamType.LIVE) {
|
|
158
208
|
if (!this.streamTypeLive) {
|
|
159
209
|
status = await this._initSwitching(
|
|
@@ -168,7 +218,9 @@ class StreamSwitcher {
|
|
|
168
218
|
}
|
|
169
219
|
if (!this.streamTypeLive) {
|
|
170
220
|
if (!scheduleObj.duration) {
|
|
171
|
-
debug(
|
|
221
|
+
debug(
|
|
222
|
+
`[${this.sessionId}]: Cannot switch VOD no duration specified for schedule item: [${scheduleObj.assetId}]`
|
|
223
|
+
);
|
|
172
224
|
return false;
|
|
173
225
|
}
|
|
174
226
|
if (this.eventId !== scheduleObj.eventId) {
|
|
@@ -198,133 +250,190 @@ class StreamSwitcher {
|
|
|
198
250
|
|
|
199
251
|
switch (state) {
|
|
200
252
|
case SwitcherState.V2L_TO_LIVE:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.eventId = null;
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
253
|
+
try {
|
|
254
|
+
debug(`[${this.sessionId}]: [ INIT Switching from V2L->LIVE ]`);
|
|
255
|
+
this.eventId = scheduleObj.eventId;
|
|
256
|
+
currVodCounts = await session.getCurrentMediaAndDiscSequenceCount();
|
|
257
|
+
currVodSegments = await session.getCurrentMediaSequenceSegments();
|
|
258
|
+
|
|
259
|
+
// In risk that the SL-playhead might have updated some data after
|
|
260
|
+
// we reset last time... we should Reset SessionLive before sending new data.
|
|
261
|
+
await sessionLive.resetLiveStoreAsync(0);
|
|
262
|
+
await sessionLive.setCurrentMediaAndDiscSequenceCount(
|
|
263
|
+
currVodCounts.mediaSeq,
|
|
264
|
+
currVodCounts.discSeq
|
|
265
|
+
);
|
|
266
|
+
await sessionLive.setCurrentMediaSequenceSegments(currVodSegments);
|
|
267
|
+
liveUri = await sessionLive.setLiveUri(scheduleObj.uri);
|
|
219
268
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
this.eventId = scheduleObj.eventId;
|
|
227
|
-
currVodCounts = await session.getCurrentMediaAndDiscSequenceCount();
|
|
228
|
-
eventSegments = await session.getTruncatedVodSegments(scheduleObj.uri, scheduleObj.duration / 1000);
|
|
269
|
+
if (!liveUri) {
|
|
270
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->LIVE ]`);
|
|
271
|
+
this.working = false;
|
|
272
|
+
this.eventId = null;
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
229
275
|
|
|
230
|
-
if (!eventSegments) {
|
|
231
|
-
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->VOD ]`);
|
|
232
276
|
this.working = false;
|
|
233
|
-
this.
|
|
234
|
-
|
|
277
|
+
this.streamTypeLive = true;
|
|
278
|
+
debug(`[${this.sessionId}]: [ Switched from V2L->LIVE ]`);
|
|
279
|
+
return true;
|
|
280
|
+
} catch (err) {
|
|
281
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->LIVE ]`);
|
|
282
|
+
throw new Error(err);
|
|
235
283
|
}
|
|
236
284
|
|
|
237
|
-
|
|
238
|
-
|
|
285
|
+
case SwitcherState.V2L_TO_VOD:
|
|
286
|
+
try {
|
|
287
|
+
debug(`[${this.sessionId}]: [ INIT Switching from V2L->VOD ]`);
|
|
288
|
+
this.eventId = scheduleObj.eventId;
|
|
289
|
+
currVodCounts = await session.getCurrentMediaAndDiscSequenceCount();
|
|
290
|
+
eventSegments = await session.getTruncatedVodSegments(
|
|
291
|
+
scheduleObj.uri,
|
|
292
|
+
scheduleObj.duration / 1000
|
|
293
|
+
);
|
|
239
294
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
liveSegments = await sessionLive.getCurrentMediaSequenceSegments();
|
|
247
|
-
liveCounts = await sessionLive.getCurrentMediaAndDiscSequenceCount();
|
|
295
|
+
if (!eventSegments) {
|
|
296
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->VOD ]`);
|
|
297
|
+
this.working = false;
|
|
298
|
+
this.eventId = null;
|
|
299
|
+
return false;
|
|
300
|
+
}
|
|
248
301
|
|
|
249
|
-
|
|
250
|
-
|
|
302
|
+
await session.setCurrentMediaAndDiscSequenceCount(
|
|
303
|
+
currVodCounts.mediaSeq,
|
|
304
|
+
currVodCounts.discSeq
|
|
305
|
+
);
|
|
306
|
+
await session.setCurrentMediaSequenceSegments(eventSegments, 0, true);
|
|
251
307
|
|
|
252
|
-
|
|
253
|
-
debug(`[${this.sessionId}]:
|
|
308
|
+
this.working = false;
|
|
309
|
+
debug(`[${this.sessionId}]: [ Switched from V2L->VOD ]`);
|
|
310
|
+
return false;
|
|
311
|
+
} catch (err) {
|
|
312
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from V2L->VOD ]`);
|
|
313
|
+
throw new Error(err);
|
|
254
314
|
}
|
|
315
|
+
case SwitcherState.LIVE_TO_V2L:
|
|
316
|
+
try {
|
|
317
|
+
debug(`[${this.sessionId}]: [ INIT Switching from LIVE->V2L ]`);
|
|
318
|
+
this.eventId = null;
|
|
319
|
+
liveSegments = await sessionLive.getCurrentMediaSequenceSegments();
|
|
320
|
+
liveCounts = await sessionLive.getCurrentMediaAndDiscSequenceCount();
|
|
321
|
+
|
|
322
|
+
await sessionLive.resetSession();
|
|
323
|
+
sessionLive.resetLiveStoreAsync(RESET_DELAY); // In parallel
|
|
324
|
+
|
|
325
|
+
if (scheduleObj && !scheduleObj.duration) {
|
|
326
|
+
debug(
|
|
327
|
+
`[${this.sessionId}]: Cannot switch VOD. No duration specified for schedule item: [${scheduleObj.assetId}]`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (this._isEmpty(liveSegments.currMseqSegs)) {
|
|
332
|
+
this.working = false;
|
|
333
|
+
this.streamTypeLive = false;
|
|
334
|
+
debug(`[${this.sessionId}]: [ Switched from LIVE->V2L ]`);
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
await session.setCurrentMediaSequenceSegments(
|
|
339
|
+
liveSegments.currMseqSegs,
|
|
340
|
+
liveSegments.segCount
|
|
341
|
+
);
|
|
342
|
+
await session.setCurrentMediaAndDiscSequenceCount(
|
|
343
|
+
liveCounts.mediaSeq,
|
|
344
|
+
liveCounts.discSeq
|
|
345
|
+
);
|
|
255
346
|
|
|
256
|
-
if (this._isEmpty(liveSegments.currMseqSegs)) {
|
|
257
347
|
this.working = false;
|
|
258
348
|
this.streamTypeLive = false;
|
|
259
349
|
debug(`[${this.sessionId}]: [ Switched from LIVE->V2L ]`);
|
|
260
|
-
return false
|
|
350
|
+
return false;
|
|
351
|
+
} catch (err) {
|
|
352
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->V2L ]`);
|
|
353
|
+
throw new Error(err);
|
|
261
354
|
}
|
|
262
|
-
|
|
263
|
-
await session.setCurrentMediaSequenceSegments(liveSegments.currMseqSegs, liveSegments.segCount);
|
|
264
|
-
await session.setCurrentMediaAndDiscSequenceCount(liveCounts.mediaSeq, liveCounts.discSeq);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
this.working = false;
|
|
268
|
-
this.streamTypeLive = false;
|
|
269
|
-
debug(`[${this.sessionId}]: [ Switched from LIVE->V2L ]`);
|
|
270
|
-
return false;
|
|
271
355
|
case SwitcherState.LIVE_TO_VOD:
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
356
|
+
try {
|
|
357
|
+
debug(`[${this.sessionId}]: INIT Switching from LIVE->VOD`);
|
|
358
|
+
// TODO: Not yet fully tested/supported
|
|
359
|
+
this.eventId = scheduleObj.eventId;
|
|
360
|
+
liveSegments = await sessionLive.getCurrentMediaSequenceSegments();
|
|
361
|
+
liveCounts = await sessionLive.getCurrentMediaAndDiscSequenceCount();
|
|
362
|
+
await sessionLive.resetSession();
|
|
363
|
+
sessionLive.resetLiveStoreAsync(RESET_DELAY); // In parallel
|
|
364
|
+
|
|
365
|
+
eventSegments = await session.getTruncatedVodSegments(
|
|
366
|
+
scheduleObj.uri,
|
|
367
|
+
scheduleObj.duration / 1000
|
|
368
|
+
);
|
|
369
|
+
if (!eventSegments) {
|
|
370
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->VOD ]`);
|
|
371
|
+
this.streamTypeLive = false;
|
|
372
|
+
this.working = false;
|
|
373
|
+
this.eventId = null;
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
await session.setCurrentMediaAndDiscSequenceCount(
|
|
378
|
+
liveCounts.mediaSeq - 1,
|
|
379
|
+
liveCounts.discSeq - 1
|
|
380
|
+
);
|
|
381
|
+
await session.setCurrentMediaSequenceSegments(
|
|
382
|
+
liveSegments.currMseqSegs,
|
|
383
|
+
liveSegments.segCount
|
|
384
|
+
);
|
|
385
|
+
await session.setCurrentMediaSequenceSegments(eventSegments, 0, true);
|
|
386
|
+
|
|
284
387
|
this.working = false;
|
|
285
|
-
this.
|
|
388
|
+
this.streamTypeLive = false;
|
|
389
|
+
debug(`[${this.sessionId}]: Switched from LIVE->VOD`);
|
|
286
390
|
return false;
|
|
391
|
+
} catch (err) {
|
|
392
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->VOD ]`);
|
|
393
|
+
throw new Error(err);
|
|
287
394
|
}
|
|
288
|
-
|
|
289
|
-
await session.setCurrentMediaAndDiscSequenceCount(liveCounts.mediaSeq - 1, liveCounts.discSeq - 1);
|
|
290
|
-
await session.setCurrentMediaSequenceSegments(liveSegments.currMseqSegs, liveSegments.segCount);
|
|
291
|
-
await session.setCurrentMediaSequenceSegments(eventSegments, 0, true);
|
|
292
|
-
|
|
293
|
-
this.working = false;
|
|
294
|
-
this.streamTypeLive = false;
|
|
295
|
-
debug(`[${this.sessionId}]: Switched from LIVE->VOD`);
|
|
296
|
-
return false;
|
|
297
395
|
case SwitcherState.LIVE_TO_LIVE:
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
396
|
+
try {
|
|
397
|
+
debug(`[${this.sessionId}]: INIT Switching from LIVE->LIVE`);
|
|
398
|
+
// TODO: Not yet fully tested/supported
|
|
399
|
+
this.eventId = scheduleObj.eventId;
|
|
400
|
+
eventSegments = await sessionLive.getCurrentMediaSequenceSegments();
|
|
401
|
+
currLiveCounts =
|
|
402
|
+
await sessionLive.getCurrentMediaAndDiscSequenceCount();
|
|
403
|
+
|
|
404
|
+
await sessionLive.resetSession();
|
|
405
|
+
await sessionLive.resetLiveStoreAsync(0);
|
|
406
|
+
|
|
407
|
+
await sessionLive.setCurrentMediaAndDiscSequenceCount(
|
|
408
|
+
currLiveCounts.mediaSeq,
|
|
409
|
+
currLiveCounts.discSeq
|
|
410
|
+
);
|
|
411
|
+
await sessionLive.setCurrentMediaSequenceSegments(
|
|
412
|
+
eventSegments.currMseqSegs
|
|
413
|
+
);
|
|
414
|
+
liveUri = await sessionLive.setLiveUri(scheduleObj.uri);
|
|
306
415
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
416
|
+
if (!liveUri) {
|
|
417
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->LIVE ]`);
|
|
418
|
+
this.streamTypeLive = false;
|
|
419
|
+
this.working = false;
|
|
420
|
+
this.eventId = null;
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
310
423
|
|
|
311
|
-
if (!liveUri) {
|
|
312
|
-
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->LIVE ]`);
|
|
313
|
-
this.streamTypeLive = false;
|
|
314
424
|
this.working = false;
|
|
315
|
-
this.
|
|
316
|
-
return
|
|
425
|
+
debug(`[${this.sessionId}]: Switched from LIVE->LIVE`);
|
|
426
|
+
return true;
|
|
427
|
+
} catch (err) {
|
|
428
|
+
debug(`[${this.sessionId}]: [ ERROR Switching from LIVE->LIVE ]`);
|
|
429
|
+
throw new Error(err);
|
|
317
430
|
}
|
|
318
|
-
|
|
319
|
-
this.working = false;
|
|
320
|
-
debug(`[${this.sessionId}]: Switched from LIVE->LIVE`);
|
|
321
|
-
return true;
|
|
322
431
|
default:
|
|
323
432
|
debug(`[${this.sessionId}]: SwitcherState [${state}] not implemented`);
|
|
324
433
|
this.streamTypeLive = false;
|
|
325
434
|
this.working = false;
|
|
326
435
|
this.eventId = null;
|
|
327
|
-
return false
|
|
436
|
+
return false;
|
|
328
437
|
}
|
|
329
438
|
}
|
|
330
439
|
|
|
@@ -352,10 +461,14 @@ class StreamSwitcher {
|
|
|
352
461
|
if (online.status >= 200 && online.status < 300) {
|
|
353
462
|
return true;
|
|
354
463
|
}
|
|
355
|
-
debug(
|
|
464
|
+
debug(
|
|
465
|
+
`[${this.sessionId}]: Failed to validate URI: ${uri}\nERROR! Returned Status Code: ${online.status}`
|
|
466
|
+
);
|
|
356
467
|
return false;
|
|
357
468
|
} catch (err) {
|
|
358
|
-
debug(
|
|
469
|
+
debug(
|
|
470
|
+
`[${this.sessionId}]: Failed to validate URI: ${uri}\nERROR! ${err}`
|
|
471
|
+
);
|
|
359
472
|
return false;
|
|
360
473
|
} finally {
|
|
361
474
|
clearTimeout(timeout);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"test": "$(npm bin)/jasmine",
|
|
9
9
|
"postversion": "git push && git push --tags",
|
|
10
10
|
"start": "node server.js",
|
|
11
|
-
"start-demux": "node server-demux.js"
|
|
11
|
+
"start-demux": "node server-demux.js",
|
|
12
|
+
"start-livemix": "node server-livemix.js"
|
|
12
13
|
},
|
|
13
14
|
"author": "Eyevinn Technology AB <work@eyevinn.se>",
|
|
14
15
|
"contributors": [
|
|
@@ -21,8 +22,8 @@
|
|
|
21
22
|
"dependencies": {
|
|
22
23
|
"@eyevinn/hls-repeat": "^0.1.1",
|
|
23
24
|
"@eyevinn/hls-truncate": "^0.1.0",
|
|
24
|
-
"@eyevinn/hls-vodtolive": "^1.
|
|
25
|
-
"@eyevinn/m3u8": "^0.4.
|
|
25
|
+
"@eyevinn/hls-vodtolive": "^1.17.1",
|
|
26
|
+
"@eyevinn/m3u8": "^0.4.1",
|
|
26
27
|
"abort-controller": "^3.0.0",
|
|
27
28
|
"debug": "^3.2.7",
|
|
28
29
|
"memcache-client": "^0.10.1",
|