eyevinn-channel-engine 4.0.8 → 4.0.10

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.
@@ -19,7 +19,7 @@ declare class SharedPlayheadState {
19
19
  getState(): Promise<4>;
20
20
  getLastM3u8(): Promise<any>;
21
21
  getValues(keys: any): Promise<any>;
22
- set(key: any, value: any): Promise<any>;
22
+ set(key: any, value: any, isLeader: any): Promise<any>;
23
23
  setState(newState: any): Promise<4>;
24
24
  setLastM3u8(m3u8: any): Promise<any>;
25
25
  }
@@ -97,12 +97,16 @@ var SharedPlayheadState = /** @class */ (function () {
97
97
  });
98
98
  });
99
99
  };
100
- SharedPlayheadState.prototype.set = function (key, value) {
100
+ SharedPlayheadState.prototype.set = function (key, value, isLeader) {
101
101
  return __awaiter(this, void 0, void 0, function () {
102
102
  return __generator(this, function (_a) {
103
103
  switch (_a.label) {
104
- case 0: return [4 /*yield*/, this.store.set(this.sessionId, key, value)];
104
+ case 0:
105
+ if (!isLeader) return [3 /*break*/, 2];
106
+ return [4 /*yield*/, this.store.set(this.sessionId, key, value)];
105
107
  case 1: return [2 /*return*/, _a.sent()];
108
+ case 2: return [4 /*yield*/, this.store.get(this.sessionId, key)];
109
+ case 3: return [2 /*return*/, _a.sent()];
106
110
  }
107
111
  });
108
112
  });
@@ -2,6 +2,7 @@ export = RedisStateStore;
2
2
  declare class RedisStateStore {
3
3
  constructor(keyPrefix: any, opts: any);
4
4
  keyPrefix: any;
5
+ volatileKeyTTL: any;
5
6
  client: any;
6
7
  initAsync(id: any, initData: any): Promise<{}>;
7
8
  resetAsync(id: any, initData: any): Promise<void>;
@@ -36,7 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
36
  };
37
37
  var redis = require("redis");
38
38
  var debug = require("debug")("redis-state-store");
39
- var VOLATILE_KEY_TTL = 5; // Timeout so it should not expire within one normal increment iteration (in seconds)
39
+ var DEFAULT_VOLATILE_KEY_TTL = 5; // Timeout so it should not expire within one normal increment iteration (in seconds)
40
40
  var RedisStateStore = /** @class */ (function () {
41
41
  function RedisStateStore(keyPrefix, opts) {
42
42
  this.keyPrefix = keyPrefix;
@@ -45,6 +45,11 @@ var RedisStateStore = /** @class */ (function () {
45
45
  this.keyPrefix = prependPrefix + this.keyPrefix;
46
46
  debug("Prepending keyprefix with ".concat(prependPrefix, " => ").concat(this.keyPrefix));
47
47
  }
48
+ this.volatileKeyTTL = DEFAULT_VOLATILE_KEY_TTL;
49
+ if (opts.volatileKeyTTL) {
50
+ debug("Overriding default, volatileKeyTTL=".concat(opts.volatileKeyTTL, "s"));
51
+ this.volatileKeyTTL = opts.volatileKeyTTL;
52
+ }
48
53
  this.client = redis.createClient(opts.redisUrl);
49
54
  }
50
55
  RedisStateStore.prototype.initAsync = function (id, initData) {
@@ -126,15 +131,17 @@ var RedisStateStore = /** @class */ (function () {
126
131
  };
127
132
  RedisStateStore.prototype.getAsync = function (id, key) {
128
133
  return __awaiter(this, void 0, void 0, function () {
129
- var storeKey, getAsync, data;
134
+ var startMs, storeKey, getAsync, data;
130
135
  var _this = this;
131
136
  return __generator(this, function (_a) {
132
137
  switch (_a.label) {
133
138
  case 0:
139
+ startMs = Date.now();
134
140
  storeKey = "" + this.keyPrefix + id + key;
135
141
  getAsync = new Promise(function (resolve, reject) {
136
142
  _this.client.get(storeKey, function (err, reply) {
137
- debug("REDIS get ".concat(storeKey, ":").concat(reply ? reply.length + " chars" : "null"));
143
+ var ioTimeMs = Date.now() - startMs;
144
+ debug("REDIS get ".concat(storeKey, ":").concat(reply ? reply.length + " chars" : "null", " (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ? 'REDISSLOW!' : ''));
138
145
  if (!err) {
139
146
  resolve(JSON.parse(reply));
140
147
  }
@@ -153,15 +160,17 @@ var RedisStateStore = /** @class */ (function () {
153
160
  };
154
161
  RedisStateStore.prototype.setAsync = function (id, key, value) {
155
162
  return __awaiter(this, void 0, void 0, function () {
156
- var storeKey, setAsync;
163
+ var startMs, storeKey, setAsync;
157
164
  var _this = this;
158
165
  return __generator(this, function (_a) {
159
166
  switch (_a.label) {
160
167
  case 0:
168
+ startMs = Date.now();
161
169
  storeKey = "" + this.keyPrefix + id + key;
162
170
  setAsync = new Promise(function (resolve, reject) {
163
171
  _this.client.set(storeKey, JSON.stringify(value), function (err, res) {
164
- debug("REDIS set ".concat(storeKey, ": ").concat(res));
172
+ var ioTimeMs = Date.now() - startMs;
173
+ debug("REDIS set ".concat(storeKey, ": ").concat(res, " (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ? 'REDISSLOW!' : ''));
165
174
  if (!err) {
166
175
  resolve(value);
167
176
  }
@@ -187,9 +196,9 @@ var RedisStateStore = /** @class */ (function () {
187
196
  data = _a.sent();
188
197
  storeKey = "" + this.keyPrefix + id + key;
189
198
  expireAsync = new Promise(function (resolve, reject) {
190
- _this.client.expire(storeKey, VOLATILE_KEY_TTL, function (err, res) {
199
+ _this.client.expire(storeKey, _this.volatileKeyTTL, function (err, res) {
191
200
  if (!err) {
192
- debug("REDIS expire ".concat(storeKey, " ").concat(VOLATILE_KEY_TTL, "s: ").concat(res === 1 ? "OK" : "KEY DOES NOT EXIST"));
201
+ debug("REDIS expire ".concat(storeKey, " ").concat(_this.volatileKeyTTL, "s: ").concat(res === 1 ? "OK" : "KEY DOES NOT EXIST"));
193
202
  resolve();
194
203
  }
195
204
  else {
@@ -207,14 +216,17 @@ var RedisStateStore = /** @class */ (function () {
207
216
  };
208
217
  RedisStateStore.prototype.removeAsync = function (id, key) {
209
218
  return __awaiter(this, void 0, void 0, function () {
210
- var storeKey, delAsync;
219
+ var startMs, storeKey, delAsync;
211
220
  var _this = this;
212
221
  return __generator(this, function (_a) {
213
222
  switch (_a.label) {
214
223
  case 0:
224
+ startMs = Date.now();
215
225
  storeKey = "" + this.keyPrefix + id + key;
216
226
  delAsync = new Promise(function (resolve, reject) {
217
227
  _this.client.del(storeKey, function (err, res) {
228
+ var ioTimeMs = Date.now() - startMs;
229
+ debug("REDIS remove ".concat(storeKey, ": (").concat(ioTimeMs, "ms) ").concat(ioTimeMs > 1000 ? 'REDISSLOW!' : ''));
218
230
  if (!err) {
219
231
  resolve();
220
232
  }
@@ -22,6 +22,7 @@ export interface ChannelEngineOpts {
22
22
  adCopyMgrUri?: string;
23
23
  adXchangeUri?: string;
24
24
  noSessionDataTags?: boolean;
25
+ volatileKeyTTL?: number;
25
26
  }
26
27
  export interface VodRequest {
27
28
  sessionId: string;
@@ -101,11 +101,13 @@ var ChannelEngine = /** @class */ (function () {
101
101
  redisUrl: options.redisUrl,
102
102
  memcachedUrl: options.memcachedUrl,
103
103
  cacheTTL: options.sharedStoreCacheTTL,
104
+ volatileKeyTTL: options.volatileKeyTTL,
104
105
  }),
105
106
  playheadStateStore: new PlayheadStateStore({
106
107
  redisUrl: options.redisUrl,
107
108
  memcachedUrl: options.memcachedUrl,
108
109
  cacheTTL: options.sharedStoreCacheTTL,
110
+ volatileKeyTTL: options.volatileKeyTTL,
109
111
  }),
110
112
  instanceId: this.instanceId,
111
113
  };
@@ -114,6 +116,7 @@ var ChannelEngine = /** @class */ (function () {
114
116
  redisUrl: options.redisUrl,
115
117
  memcachedUrl: options.memcachedUrl,
116
118
  cacheTTL: options.sharedStoreCacheTTL,
119
+ volatileKeyTTL: options.volatileKeyTTL,
117
120
  }),
118
121
  instanceId: this.instanceId,
119
122
  };
@@ -326,7 +329,9 @@ var ChannelEngine = /** @class */ (function () {
326
329
  }
327
330
  });
328
331
  }); };
329
- StreamSwitchLoop(this.streamSwitchTimeIntervalMs);
332
+ if (this.streamSwitchManager) {
333
+ StreamSwitchLoop(this.streamSwitchTimeIntervalMs);
334
+ }
330
335
  }
331
336
  ChannelEngine.prototype.updateStreamSwitchAsync = function () {
332
337
  return __awaiter(this, void 0, void 0, function () {
@@ -884,34 +889,39 @@ var ChannelEngine = /** @class */ (function () {
884
889
  debug(req.params);
885
890
  session = sessions[req.params[1]];
886
891
  sessionLive = sessionsLive[req.params[1]];
887
- if (!(session && sessionLive)) return [3 /*break*/, 11];
892
+ if (!(session && sessionLive)) return [3 /*break*/, 12];
888
893
  _a.label = 1;
889
894
  case 1:
890
- _a.trys.push([1, 9, , 10]);
891
- _a.label = 2;
895
+ _a.trys.push([1, 10, , 11]);
896
+ body = null;
897
+ if (!!this.streamSwitchManager) return [3 /*break*/, 3];
898
+ debug("[".concat(req.params[1], "]: Responding with VOD2Live manifest"));
899
+ return [4 /*yield*/, session.getCurrentMediaManifestAsync(req.params[0], req.headers["x-playback-session-id"])];
892
900
  case 2:
893
- if (!(switcherStatus[req.params[1]] === null || switcherStatus[req.params[1]] === undefined)) return [3 /*break*/, 4];
901
+ body = _a.sent();
902
+ return [3 /*break*/, 9];
903
+ case 3:
904
+ if (!(switcherStatus[req.params[1]] === null || switcherStatus[req.params[1]] === undefined)) return [3 /*break*/, 5];
894
905
  debug("[".concat(req.params[1], "]: (").concat(switcherStatus[req.params[1]], ") Waiting for streamSwitcher to respond"));
895
906
  return [4 /*yield*/, timer(500)];
896
- case 3:
897
- _a.sent();
898
- return [3 /*break*/, 2];
899
907
  case 4:
900
- body = null;
908
+ _a.sent();
909
+ return [3 /*break*/, 3];
910
+ case 5:
901
911
  debug("switcherStatus[".concat(req.params[1], "]=[").concat(switcherStatus[req.params[1]], "]"));
902
- if (!switcherStatus[req.params[1]]) return [3 /*break*/, 6];
912
+ if (!switcherStatus[req.params[1]]) return [3 /*break*/, 7];
903
913
  debug("[".concat(req.params[1], "]: Responding with Live-stream manifest"));
904
914
  return [4 /*yield*/, sessionLive.getCurrentMediaManifestAsync(req.params[0])];
905
- case 5:
906
- body = _a.sent();
907
- return [3 /*break*/, 8];
908
915
  case 6:
916
+ body = _a.sent();
917
+ return [3 /*break*/, 9];
918
+ case 7:
909
919
  debug("[".concat(req.params[1], "]: Responding with VOD2Live manifest"));
910
920
  return [4 /*yield*/, session.getCurrentMediaManifestAsync(req.params[0], req.headers["x-playback-session-id"])];
911
- case 7:
912
- body = _a.sent();
913
- _a.label = 8;
914
921
  case 8:
922
+ body = _a.sent();
923
+ _a.label = 9;
924
+ case 9:
915
925
  //verbose(`[${session.sessionId}] body=`);
916
926
  //verbose(body);
917
927
  res.sendRaw(200, Buffer.from(body, 'utf8'), {
@@ -921,17 +931,17 @@ var ChannelEngine = /** @class */ (function () {
921
931
  "X-Instance-Id": this.instanceId + "<".concat(version, ">"),
922
932
  });
923
933
  next();
924
- return [3 /*break*/, 10];
925
- case 9:
934
+ return [3 /*break*/, 11];
935
+ case 10:
926
936
  err_6 = _a.sent();
927
937
  next(this._gracefulErrorHandler(err_6));
928
- return [3 /*break*/, 10];
929
- case 10: return [3 /*break*/, 12];
930
- case 11:
938
+ return [3 /*break*/, 11];
939
+ case 11: return [3 /*break*/, 13];
940
+ case 12:
931
941
  err = new errs.NotFoundError('Invalid session(s)');
932
942
  next(err);
933
- _a.label = 12;
934
- case 12: return [2 /*return*/];
943
+ _a.label = 13;
944
+ case 13: return [2 /*return*/];
935
945
  }
936
946
  });
937
947
  });
@@ -253,7 +253,7 @@ var Session = /** @class */ (function () {
253
253
  tickInterval = firstDuration < 2 ? 2 : firstDuration;
254
254
  debug("[".concat(this._sessionId, "]: I am the leader and updated tick interval to ").concat(tickInterval, " sec"));
255
255
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'tickIntervalUpdated', channel: this._sessionId, tickIntervalSec: tickInterval });
256
- this._playheadState.set("tickInterval", tickInterval);
256
+ this._playheadState.set("tickInterval", tickInterval, isLeader);
257
257
  return [3 /*break*/, 21];
258
258
  case 13:
259
259
  if (!(state == PlayheadState.STOPPED)) return [3 /*break*/, 14];
@@ -326,7 +326,7 @@ var Session = /** @class */ (function () {
326
326
  case 17:
327
327
  _a.sent();
328
328
  tsTickEnd = Date.now();
329
- return [4 /*yield*/, this._playheadState.set("tickMs", (tsTickEnd - tsIncrementBegin))];
329
+ return [4 /*yield*/, this._playheadState.set("tickMs", (tsTickEnd - tsIncrementBegin), isLeader)];
330
330
  case 18:
331
331
  _a.sent();
332
332
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'tickInterval', channel: this._sessionId, tickTimeMs: (tsTickEnd - tsIncrementBegin) });
@@ -335,7 +335,7 @@ var Session = /** @class */ (function () {
335
335
  case 19:
336
336
  lastDuration = _a.sent();
337
337
  nextTickInterval = lastDuration < 2 ? 2 : lastDuration;
338
- return [4 /*yield*/, this._playheadState.set("tickInterval", nextTickInterval)];
338
+ return [4 /*yield*/, this._playheadState.set("tickInterval", nextTickInterval, isLeader)];
339
339
  case 20:
340
340
  _a.sent();
341
341
  cloudWatchLog(!this.cloudWatchLogging, "engine-session", { event: "tickIntervalUpdated", channel: this._sessionId, tickIntervalSec: nextTickInterval });
@@ -375,12 +375,15 @@ var Session = /** @class */ (function () {
375
375
  };
376
376
  Session.prototype.stopPlayheadAsync = function () {
377
377
  return __awaiter(this, void 0, void 0, function () {
378
+ var isLeader;
378
379
  return __generator(this, function (_a) {
379
380
  switch (_a.label) {
380
- case 0:
381
- debug("[".concat(this._sessionId, "]: Stopping playhead consumer"));
382
- return [4 /*yield*/, this._playheadState.set("state", PlayheadState.STOPPED)];
381
+ case 0: return [4 /*yield*/, this._sessionStateStore.isLeader(this._instanceId)];
383
382
  case 1:
383
+ isLeader = _a.sent();
384
+ debug("[".concat(this._sessionId, "]: Stopping playhead consumer"));
385
+ return [4 /*yield*/, this._playheadState.set("state", PlayheadState.STOPPED, isLeader)];
386
+ case 2:
384
387
  _a.sent();
385
388
  return [2 /*return*/];
386
389
  }
@@ -1031,6 +1034,7 @@ var Session = /** @class */ (function () {
1031
1034
  index = 0;
1032
1035
  audioSeqLastIdx = currentVod_1.getLiveMediaSequencesCount("audio") - 1;
1033
1036
  thresh = 0.5;
1037
+ debug("[".concat(this._sessionId, "]: About to determine audio increment"));
1034
1038
  _o.label = 19;
1035
1039
  case 19: return [4 /*yield*/, this._getAudioPlayheadPosition(sessionState_1.vodMediaSeqAudio + index)];
1036
1040
  case 20:
@@ -1058,6 +1062,7 @@ var Session = /** @class */ (function () {
1058
1062
  audioIncrement = index;
1059
1063
  _o.label = 23;
1060
1064
  case 23:
1065
+ debug("[".concat(this._sessionId, "]: Will increment audio with ").concat(audioIncrement));
1061
1066
  _d = sessionState;
1062
1067
  return [4 /*yield*/, this._sessionState.increment("vodMediaSeqAudio", audioIncrement)];
1063
1068
  case 24:
@@ -1090,20 +1095,23 @@ var Session = /** @class */ (function () {
1090
1095
  this.isSwitchingBackToV2L = false;
1091
1096
  _o.label = 32;
1092
1097
  case 32:
1098
+ if (isLeader) {
1099
+ debug("[".concat(this._sessionId, "]: I am the leader, updating PlayheadState values"));
1100
+ }
1093
1101
  _j = playheadState;
1094
- return [4 /*yield*/, this._playheadState.set("mediaSeq", sessionState.mediaSeq)];
1102
+ return [4 /*yield*/, this._playheadState.set("mediaSeq", sessionState.mediaSeq, isLeader)];
1095
1103
  case 33:
1096
1104
  _j.mediaSeq = _o.sent();
1097
1105
  _k = playheadState;
1098
- return [4 /*yield*/, this._playheadState.set("mediaSeqAudio", sessionState.mediaSeqAudio)];
1106
+ return [4 /*yield*/, this._playheadState.set("mediaSeqAudio", sessionState.mediaSeqAudio, isLeader)];
1099
1107
  case 34:
1100
1108
  _k.mediaSeqAudio = _o.sent();
1101
1109
  _l = playheadState;
1102
- return [4 /*yield*/, this._playheadState.set("vodMediaSeqVideo", sessionState.vodMediaSeqVideo)];
1110
+ return [4 /*yield*/, this._playheadState.set("vodMediaSeqVideo", sessionState.vodMediaSeqVideo, isLeader)];
1103
1111
  case 35:
1104
1112
  _l.vodMediaSeqVideo = _o.sent();
1105
1113
  _m = playheadState;
1106
- return [4 /*yield*/, this._playheadState.set("vodMediaSeqAudio", sessionState.vodMediaSeqAudio)];
1114
+ return [4 /*yield*/, this._playheadState.set("vodMediaSeqAudio", sessionState.vodMediaSeqAudio, isLeader)];
1107
1115
  case 36:
1108
1116
  _m.vodMediaSeqAudio = _o.sent();
1109
1117
  if (currentVod.sequenceAlwaysContainNewSegments) {
@@ -1481,11 +1489,11 @@ var Session = /** @class */ (function () {
1481
1489
  };
1482
1490
  Session.prototype._insertSlate = function (currentVod) {
1483
1491
  return __awaiter(this, void 0, void 0, function () {
1484
- var slateVod, sessionState, endValue, endValueAudio, lastDiscontinuity, lastDiscontinuityAudio;
1492
+ var slateVod, sessionState, endValue, endValueAudio, lastDiscontinuity, lastDiscontinuityAudio, isLeader;
1485
1493
  return __generator(this, function (_a) {
1486
1494
  switch (_a.label) {
1487
1495
  case 0:
1488
- if (!this.slateUri) return [3 /*break*/, 13];
1496
+ if (!this.slateUri) return [3 /*break*/, 14];
1489
1497
  console.error("[".concat(this._sessionId, "]: Will insert slate"));
1490
1498
  return [4 /*yield*/, this._loadSlate(currentVod)];
1491
1499
  case 1:
@@ -1510,39 +1518,42 @@ var Session = /** @class */ (function () {
1510
1518
  lastDiscontinuity = currentVod.getLastDiscontinuity();
1511
1519
  lastDiscontinuityAudio = currentVod.getLastDiscontinuityAudio();
1512
1520
  }
1513
- return [4 /*yield*/, this._sessionState.set("vodMediaSeqVideo", 0)];
1521
+ return [4 /*yield*/, this._sessionStateStore.isLeader(this._instanceId)];
1514
1522
  case 3:
1515
- _a.sent();
1516
- return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
1523
+ isLeader = _a.sent();
1524
+ return [4 /*yield*/, this._sessionState.set("vodMediaSeqVideo", 0)];
1517
1525
  case 4:
1518
1526
  _a.sent();
1519
- return [4 /*yield*/, this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING)];
1527
+ return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
1520
1528
  case 5:
1521
1529
  _a.sent();
1522
- return [4 /*yield*/, this._sessionState.setCurrentVod(slateVod)];
1530
+ return [4 /*yield*/, this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING)];
1523
1531
  case 6:
1524
1532
  _a.sent();
1525
- return [4 /*yield*/, this._sessionState.set("mediaSeq", sessionState.mediaSeq + endValue)];
1533
+ return [4 /*yield*/, this._sessionState.setCurrentVod(slateVod)];
1526
1534
  case 7:
1527
1535
  _a.sent();
1528
- return [4 /*yield*/, this._sessionState.set("mediaSeqAudio", sessionState.mediaSeqAudio + endValueAudio)];
1536
+ return [4 /*yield*/, this._sessionState.set("mediaSeq", sessionState.mediaSeq + endValue)];
1529
1537
  case 8:
1530
1538
  _a.sent();
1531
- return [4 /*yield*/, this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity)];
1539
+ return [4 /*yield*/, this._sessionState.set("mediaSeqAudio", sessionState.mediaSeqAudio + endValueAudio)];
1532
1540
  case 9:
1533
1541
  _a.sent();
1534
- return [4 /*yield*/, this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio)];
1542
+ return [4 /*yield*/, this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity)];
1535
1543
  case 10:
1536
1544
  _a.sent();
1537
- return [4 /*yield*/, this._sessionState.set("slateCount", sessionState.slateCount + 1)];
1545
+ return [4 /*yield*/, this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio)];
1538
1546
  case 11:
1539
1547
  _a.sent();
1540
- return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now())];
1548
+ return [4 /*yield*/, this._sessionState.set("slateCount", sessionState.slateCount + 1)];
1541
1549
  case 12:
1550
+ _a.sent();
1551
+ return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now(), isLeader)];
1552
+ case 13:
1542
1553
  _a.sent();
1543
1554
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'slateInserted', channel: this._sessionId });
1544
1555
  return [2 /*return*/, slateVod];
1545
- case 13: return [2 /*return*/, null];
1556
+ case 14: return [2 /*return*/, null];
1546
1557
  }
1547
1558
  });
1548
1559
  });
@@ -1636,7 +1647,8 @@ var Session = /** @class */ (function () {
1636
1647
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'loadVod', channel: this._sessionId, loadTimeMs: Date.now() - loadStart });
1637
1648
  debug("[".concat(this._sessionId, "]: first VOD loaded"));
1638
1649
  debug("[".concat(this._sessionId, "]: ").concat(currentVod.getDeltaTimes()));
1639
- debug("[".concat(this._sessionId, "]: ").concat(currentVod.getPlayheadPositions()));
1650
+ debug("[".concat(this._sessionId, "]: playhead positions [V]=").concat(currentVod.getPlayheadPositions("video")));
1651
+ debug("[".concat(this._sessionId, "]: playhead positions [A]=").concat(currentVod.getPlayheadPositions("audio")));
1640
1652
  //debug(newVod);
1641
1653
  _c = sessionState;
1642
1654
  return [4 /*yield*/, this._sessionState.set("mediaSeq", 0)];
@@ -1663,7 +1675,7 @@ var Session = /** @class */ (function () {
1663
1675
  return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
1664
1676
  case 15:
1665
1677
  _h.vodMediaSeqAudio = _2.sent();
1666
- return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now())];
1678
+ return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now(), isLeader)];
1667
1679
  case 16:
1668
1680
  _2.sent();
1669
1681
  this.produceEvent({
@@ -1858,7 +1870,8 @@ var Session = /** @class */ (function () {
1858
1870
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'loadVod', channel: this._sessionId, loadTimeMs: Date.now() - loadStart });
1859
1871
  this.leaderIsSettingNextVod = true;
1860
1872
  debug("[".concat(this._sessionId, "]: next VOD loaded (").concat(newVod.getDeltaTimes(), ")"));
1861
- debug("[".concat(this._sessionId, "]: ").concat(newVod.getPlayheadPositions()));
1873
+ debug("[".concat(this._sessionId, "]: playhead positions [V]=").concat(newVod.getPlayheadPositions("video")));
1874
+ debug("[".concat(this._sessionId, "]: playhead positions [A]=").concat(newVod.getPlayheadPositions("audio")));
1862
1875
  currentVod = newVod;
1863
1876
  debug("[".concat(this._sessionId, "]: msequences=").concat(currentVod.getLiveMediaSequencesCount(), "; audio msequences=").concat(currentVod.getLiveMediaSequencesCount("audio")));
1864
1877
  _r = sessionState;
@@ -1894,10 +1907,10 @@ var Session = /** @class */ (function () {
1894
1907
  case 50:
1895
1908
  _x.currentVod = _2.sent();
1896
1909
  this.leaderIsSettingNextVod = false;
1897
- return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now())];
1910
+ return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now(), isLeader)];
1898
1911
  case 51:
1899
1912
  _2.sent();
1900
- return [4 /*yield*/, this._playheadState.set("diffCompensation", this.diffCompensation)];
1913
+ return [4 /*yield*/, this._playheadState.set("diffCompensation", this.diffCompensation, isLeader)];
1901
1914
  case 52:
1902
1915
  _2.sent();
1903
1916
  debug("[".concat(this._sessionId, "]: sharing durrent vods diffCompensation=").concat(this.diffCompensation));
@@ -1995,7 +2008,7 @@ var Session = /** @class */ (function () {
1995
2008
  return [4 /*yield*/, this._sessionState.set("mediaSeq", mSeq)];
1996
2009
  case 65:
1997
2010
  _2.sent();
1998
- return [4 /*yield*/, this._playheadState.set("mediaSeq", mSeq)];
2011
+ return [4 /*yield*/, this._playheadState.set("mediaSeq", mSeq, isLeader)];
1999
2012
  case 66:
2000
2013
  _2.sent();
2001
2014
  return [4 /*yield*/, this._sessionState.set("discSeq", dSeq)];
@@ -2033,13 +2046,13 @@ var Session = /** @class */ (function () {
2033
2046
  return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
2034
2047
  case 73:
2035
2048
  _2.sent();
2036
- return [4 /*yield*/, this._playheadState.set("vodMediaSeqVideo", 0)];
2049
+ return [4 /*yield*/, this._playheadState.set("vodMediaSeqVideo", 0, isLeader)];
2037
2050
  case 74:
2038
2051
  _2.sent();
2039
- return [4 /*yield*/, this._playheadState.set("vodMediaSeqAudio", 0)];
2052
+ return [4 /*yield*/, this._playheadState.set("vodMediaSeqAudio", 0, isLeader)];
2040
2053
  case 75:
2041
2054
  _2.sent();
2042
- return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now())];
2055
+ return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now(), isLeader)];
2043
2056
  case 76:
2044
2057
  _2.sent();
2045
2058
  // 4) Log to debug and cloudwatch
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.0.8",
3
+ "version": "4.0.10",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -31,8 +31,12 @@ class SharedPlayheadState {
31
31
  return await this.store.getValues(this.sessionId, keys);
32
32
  }
33
33
 
34
- async set(key, value) {
35
- return await this.store.set(this.sessionId, key, value);
34
+ async set(key, value, isLeader) {
35
+ if (isLeader) {
36
+ return await this.store.set(this.sessionId, key, value);
37
+ } else {
38
+ return await this.store.get(this.sessionId, key);
39
+ }
36
40
  }
37
41
 
38
42
  async setState(newState) {
@@ -1,7 +1,7 @@
1
1
  const redis = require("redis");
2
2
  const debug = require("debug")("redis-state-store");
3
3
 
4
- const VOLATILE_KEY_TTL = 5; // Timeout so it should not expire within one normal increment iteration (in seconds)
4
+ const DEFAULT_VOLATILE_KEY_TTL = 5; // Timeout so it should not expire within one normal increment iteration (in seconds)
5
5
 
6
6
  class RedisStateStore {
7
7
  constructor(keyPrefix, opts) {
@@ -11,6 +11,11 @@ class RedisStateStore {
11
11
  this.keyPrefix = prependPrefix + this.keyPrefix;
12
12
  debug(`Prepending keyprefix with ${prependPrefix} => ${this.keyPrefix}`);
13
13
  }
14
+ this.volatileKeyTTL = DEFAULT_VOLATILE_KEY_TTL;
15
+ if (opts.volatileKeyTTL) {
16
+ debug(`Overriding default, volatileKeyTTL=${opts.volatileKeyTTL}s`);
17
+ this.volatileKeyTTL = opts.volatileKeyTTL;
18
+ }
14
19
  this.client = redis.createClient(opts.redisUrl);
15
20
  }
16
21
 
@@ -48,10 +53,12 @@ class RedisStateStore {
48
53
  }
49
54
 
50
55
  async getAsync(id, key) {
56
+ const startMs = Date.now();
51
57
  const storeKey = "" + this.keyPrefix + id + key;
52
58
  const getAsync = new Promise((resolve, reject) => {
53
59
  this.client.get(storeKey, (err, reply) => {
54
- debug(`REDIS get ${storeKey}:${reply ? reply.length + " chars" : "null"}`);
60
+ const ioTimeMs = Date.now() - startMs;
61
+ debug(`REDIS get ${storeKey}:${reply ? reply.length + " chars" : "null"} (${ioTimeMs}ms) ${ioTimeMs > 1000 ? 'REDISSLOW!' : ''}`);
55
62
  if (!err) {
56
63
  resolve(JSON.parse(reply));
57
64
  } else {
@@ -64,10 +71,12 @@ class RedisStateStore {
64
71
  }
65
72
 
66
73
  async setAsync(id, key, value) {
74
+ const startMs = Date.now();
67
75
  const storeKey = "" + this.keyPrefix + id + key;
68
76
  const setAsync = new Promise((resolve, reject) => {
69
77
  this.client.set(storeKey, JSON.stringify(value), (err, res) => {
70
- debug(`REDIS set ${storeKey}: ${res}`);
78
+ const ioTimeMs = Date.now() - startMs;
79
+ debug(`REDIS set ${storeKey}: ${res} (${ioTimeMs}ms) ${ioTimeMs > 1000 ? 'REDISSLOW!' : ''}`);
71
80
  if (!err) {
72
81
  resolve(value);
73
82
  } else {
@@ -82,9 +91,9 @@ class RedisStateStore {
82
91
  const data = await this.setAsync(id, key, value);
83
92
  const storeKey = "" + this.keyPrefix + id + key;
84
93
  const expireAsync = new Promise((resolve, reject) => {
85
- this.client.expire(storeKey, VOLATILE_KEY_TTL, (err, res) => {
94
+ this.client.expire(storeKey, this.volatileKeyTTL, (err, res) => {
86
95
  if (!err) {
87
- debug(`REDIS expire ${storeKey} ${VOLATILE_KEY_TTL}s: ${res === 1 ? "OK" : "KEY DOES NOT EXIST"}`);
96
+ debug(`REDIS expire ${storeKey} ${this.volatileKeyTTL}s: ${res === 1 ? "OK" : "KEY DOES NOT EXIST"}`);
88
97
  resolve();
89
98
  } else {
90
99
  reject(err);
@@ -96,9 +105,12 @@ class RedisStateStore {
96
105
  }
97
106
 
98
107
  async removeAsync(id, key) {
108
+ const startMs = Date.now();
99
109
  const storeKey = "" + this.keyPrefix + id + key;
100
110
  const delAsync = new Promise((resolve, reject) => {
101
111
  this.client.del(storeKey, (err, res) => {
112
+ const ioTimeMs = Date.now() - startMs;
113
+ debug(`REDIS remove ${storeKey}: (${ioTimeMs}ms) ${ioTimeMs > 1000 ? 'REDISSLOW!' : ''}`);
102
114
  if (!err) {
103
115
  resolve();
104
116
  } else {
package/engine/server.ts CHANGED
@@ -47,6 +47,7 @@ export interface ChannelEngineOpts {
47
47
  adCopyMgrUri?: string; // deprecated
48
48
  adXchangeUri?: string; // deprecated
49
49
  noSessionDataTags?: boolean;
50
+ volatileKeyTTL?: number;
50
51
  }
51
52
 
52
53
  interface StreamerOpts {
@@ -206,11 +207,13 @@ export class ChannelEngine {
206
207
  redisUrl: options.redisUrl,
207
208
  memcachedUrl: options.memcachedUrl,
208
209
  cacheTTL: options.sharedStoreCacheTTL,
210
+ volatileKeyTTL: options.volatileKeyTTL,
209
211
  }),
210
212
  playheadStateStore: new PlayheadStateStore({
211
213
  redisUrl: options.redisUrl,
212
214
  memcachedUrl: options.memcachedUrl,
213
215
  cacheTTL: options.sharedStoreCacheTTL,
216
+ volatileKeyTTL: options.volatileKeyTTL,
214
217
  }),
215
218
  instanceId: this.instanceId,
216
219
  };
@@ -220,6 +223,7 @@ export class ChannelEngine {
220
223
  redisUrl: options.redisUrl,
221
224
  memcachedUrl: options.memcachedUrl,
222
225
  cacheTTL: options.sharedStoreCacheTTL,
226
+ volatileKeyTTL: options.volatileKeyTTL,
223
227
  }),
224
228
  instanceId: this.instanceId,
225
229
  };
@@ -353,7 +357,9 @@ export class ChannelEngine {
353
357
  }
354
358
  }
355
359
  }
356
- StreamSwitchLoop(this.streamSwitchTimeIntervalMs);
360
+ if (this.streamSwitchManager) {
361
+ StreamSwitchLoop(this.streamSwitchTimeIntervalMs);
362
+ }
357
363
  }
358
364
 
359
365
  async updateStreamSwitchAsync() {
@@ -706,19 +712,25 @@ export class ChannelEngine {
706
712
  const sessionLive = sessionsLive[req.params[1]];
707
713
  if (session && sessionLive) {
708
714
  try {
709
- while (switcherStatus[req.params[1]] === null || switcherStatus[req.params[1]] === undefined) {
710
- debug(`[${req.params[1]}]: (${switcherStatus[req.params[1]]}) Waiting for streamSwitcher to respond`);
711
- await timer(500);
712
- }
713
715
  let body = null;
714
- debug(`switcherStatus[${req.params[1]}]=[${switcherStatus[req.params[1]]}]`);
715
- if (switcherStatus[req.params[1]]) {
716
- debug(`[${req.params[1]}]: Responding with Live-stream manifest`);
717
- body = await sessionLive.getCurrentMediaManifestAsync(req.params[0]);
718
- } else {
716
+ if (!this.streamSwitchManager) {
719
717
  debug(`[${req.params[1]}]: Responding with VOD2Live manifest`);
720
718
  body = await session.getCurrentMediaManifestAsync(req.params[0], req.headers["x-playback-session-id"]);
719
+ } else {
720
+ while (switcherStatus[req.params[1]] === null || switcherStatus[req.params[1]] === undefined) {
721
+ debug(`[${req.params[1]}]: (${switcherStatus[req.params[1]]}) Waiting for streamSwitcher to respond`);
722
+ await timer(500);
723
+ }
724
+ debug(`switcherStatus[${req.params[1]}]=[${switcherStatus[req.params[1]]}]`);
725
+ if (switcherStatus[req.params[1]]) {
726
+ debug(`[${req.params[1]}]: Responding with Live-stream manifest`);
727
+ body = await sessionLive.getCurrentMediaManifestAsync(req.params[0]);
728
+ } else {
729
+ debug(`[${req.params[1]}]: Responding with VOD2Live manifest`);
730
+ body = await session.getCurrentMediaManifestAsync(req.params[0], req.headers["x-playback-session-id"]);
731
+ }
721
732
  }
733
+
722
734
  //verbose(`[${session.sessionId}] body=`);
723
735
  //verbose(body);
724
736
  res.sendRaw(200, Buffer.from(body, 'utf8'), {
package/engine/session.js CHANGED
@@ -185,7 +185,7 @@ class Session {
185
185
  debug(`[${this._sessionId}]: I am the leader and updated tick interval to ${tickInterval} sec`);
186
186
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
187
187
  { event: 'tickIntervalUpdated', channel: this._sessionId, tickIntervalSec: tickInterval });
188
- this._playheadState.set("tickInterval", tickInterval);
188
+ this._playheadState.set("tickInterval", tickInterval, isLeader);
189
189
  } else if (state == PlayheadState.STOPPED) {
190
190
  debug(`[${this._sessionId}]: Stopping playhead`);
191
191
  return;
@@ -251,14 +251,14 @@ class Session {
251
251
  debug(`[${this._sessionId}]: (${(new Date()).toISOString()}) ${timeSpentInIncrement}sec in increment. Next tick in ${tickInterval} seconds`)
252
252
  await timer((tickInterval * 1000) - 50);
253
253
  const tsTickEnd = Date.now();
254
- await this._playheadState.set("tickMs", (tsTickEnd - tsIncrementBegin));
254
+ await this._playheadState.set("tickMs", (tsTickEnd - tsIncrementBegin), isLeader);
255
255
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session',
256
256
  { event: 'tickInterval', channel: this._sessionId, tickTimeMs: (tsTickEnd - tsIncrementBegin) });
257
257
  if (this.alwaysNewSegments) {
258
258
  // Use dynamic base-tickInterval. Set according to duration of latest segment.
259
259
  const lastDuration = await this._getLastDuration(manifest);
260
260
  const nextTickInterval = lastDuration < 2 ? 2 : lastDuration;
261
- await this._playheadState.set("tickInterval", nextTickInterval);
261
+ await this._playheadState.set("tickInterval", nextTickInterval, isLeader);
262
262
  cloudWatchLog(!this.cloudWatchLogging, "engine-session", { event: "tickIntervalUpdated", channel: this._sessionId, tickIntervalSec: nextTickInterval });
263
263
  }
264
264
  }
@@ -280,8 +280,9 @@ class Session {
280
280
  }
281
281
 
282
282
  async stopPlayheadAsync() {
283
+ const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
283
284
  debug(`[${this._sessionId}]: Stopping playhead consumer`);
284
- await this._playheadState.set("state", PlayheadState.STOPPED);
285
+ await this._playheadState.set("state", PlayheadState.STOPPED, isLeader);
285
286
  }
286
287
 
287
288
  async getStatusAsync() {
@@ -733,6 +734,7 @@ class Session {
733
734
  let index = 0;
734
735
  const audioSeqLastIdx = currentVod.getLiveMediaSequencesCount("audio") - 1;
735
736
  const thresh = 0.5;
737
+ debug(`[${this._sessionId}]: About to determine audio increment`);
736
738
  do {
737
739
  const audioPosition = (await this._getAudioPlayheadPosition(sessionState.vodMediaSeqAudio + index)) * 1000;
738
740
  posDiff = position - audioPosition;
@@ -752,6 +754,7 @@ class Session {
752
754
  } while (!(-thresh < posDiff && posDiff < thresh));
753
755
  audioIncrement = index;
754
756
  }
757
+ debug(`[${this._sessionId}]: Will increment audio with ${audioIncrement}`);
755
758
  sessionState.vodMediaSeqAudio = await this._sessionState.increment("vodMediaSeqAudio", audioIncrement);
756
759
  }
757
760
 
@@ -769,10 +772,13 @@ class Session {
769
772
  this.isSwitchingBackToV2L = false;
770
773
  }
771
774
 
772
- playheadState.mediaSeq = await this._playheadState.set("mediaSeq", sessionState.mediaSeq);
773
- playheadState.mediaSeqAudio = await this._playheadState.set("mediaSeqAudio", sessionState.mediaSeqAudio);
774
- playheadState.vodMediaSeqVideo = await this._playheadState.set("vodMediaSeqVideo", sessionState.vodMediaSeqVideo);
775
- playheadState.vodMediaSeqAudio = await this._playheadState.set("vodMediaSeqAudio", sessionState.vodMediaSeqAudio);
775
+ if (isLeader) {
776
+ debug(`[${this._sessionId}]: I am the leader, updating PlayheadState values`);
777
+ }
778
+ playheadState.mediaSeq = await this._playheadState.set("mediaSeq", sessionState.mediaSeq, isLeader);
779
+ playheadState.mediaSeqAudio = await this._playheadState.set("mediaSeqAudio", sessionState.mediaSeqAudio, isLeader);
780
+ playheadState.vodMediaSeqVideo = await this._playheadState.set("vodMediaSeqVideo", sessionState.vodMediaSeqVideo, isLeader);
781
+ playheadState.vodMediaSeqAudio = await this._playheadState.set("vodMediaSeqAudio", sessionState.vodMediaSeqAudio, isLeader);
776
782
 
777
783
  if (currentVod.sequenceAlwaysContainNewSegments) {
778
784
  const mediaSequenceValue = currentVod.mediaSequenceValues[playheadState.vodMediaSeqVideo];
@@ -1075,6 +1081,7 @@ class Session {
1075
1081
  lastDiscontinuity = currentVod.getLastDiscontinuity();
1076
1082
  lastDiscontinuityAudio = currentVod.getLastDiscontinuityAudio();
1077
1083
  }
1084
+ const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
1078
1085
  await this._sessionState.set("vodMediaSeqVideo", 0);
1079
1086
  await this._sessionState.set("vodMediaSeqAudio", 0);
1080
1087
  await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
@@ -1084,7 +1091,7 @@ class Session {
1084
1091
  await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
1085
1092
  await this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio);
1086
1093
  await this._sessionState.set("slateCount", sessionState.slateCount + 1);
1087
- await this._playheadState.set("playheadRef", Date.now());
1094
+ await this._playheadState.set("playheadRef", Date.now(), isLeader);
1088
1095
 
1089
1096
  cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'slateInserted', channel: this._sessionId });
1090
1097
 
@@ -1160,7 +1167,8 @@ class Session {
1160
1167
  { event: 'loadVod', channel: this._sessionId, loadTimeMs: Date.now() - loadStart });
1161
1168
  debug(`[${this._sessionId}]: first VOD loaded`);
1162
1169
  debug(`[${this._sessionId}]: ${currentVod.getDeltaTimes()}`);
1163
- debug(`[${this._sessionId}]: ${currentVod.getPlayheadPositions()}`);
1170
+ debug(`[${this._sessionId}]: playhead positions [V]=${currentVod.getPlayheadPositions("video")}`);
1171
+ debug(`[${this._sessionId}]: playhead positions [A]=${currentVod.getPlayheadPositions("audio")}`);
1164
1172
  //debug(newVod);
1165
1173
  sessionState.mediaSeq = await this._sessionState.set("mediaSeq", 0);
1166
1174
  sessionState.mediaSeqAudio = await this._sessionState.set("mediaSeqAudio", 0);
@@ -1168,7 +1176,7 @@ class Session {
1168
1176
  sessionState.discSeqAudio = await this._sessionState.set("discSeqAudio", 0);
1169
1177
  sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
1170
1178
  sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
1171
- await this._playheadState.set("playheadRef", Date.now());
1179
+ await this._playheadState.set("playheadRef", Date.now(), isLeader);
1172
1180
  this.produceEvent({
1173
1181
  type: 'NOW_PLAYING',
1174
1182
  data: {
@@ -1323,7 +1331,8 @@ class Session {
1323
1331
  { event: 'loadVod', channel: this._sessionId, loadTimeMs: Date.now() - loadStart });
1324
1332
  this.leaderIsSettingNextVod = true;
1325
1333
  debug(`[${this._sessionId}]: next VOD loaded (${newVod.getDeltaTimes()})`);
1326
- debug(`[${this._sessionId}]: ${newVod.getPlayheadPositions()}`);
1334
+ debug(`[${this._sessionId}]: playhead positions [V]=${newVod.getPlayheadPositions("video")}`);
1335
+ debug(`[${this._sessionId}]: playhead positions [A]=${newVod.getPlayheadPositions("audio")}`);
1327
1336
  currentVod = newVod;
1328
1337
  debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}; audio msequences=${currentVod.getLiveMediaSequencesCount("audio")}`);
1329
1338
  sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
@@ -1336,8 +1345,8 @@ class Session {
1336
1345
  await this._sessionState.remove("nextVod");
1337
1346
  sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
1338
1347
  this.leaderIsSettingNextVod = false;
1339
- await this._playheadState.set("playheadRef", Date.now());
1340
- await this._playheadState.set("diffCompensation", this.diffCompensation);
1348
+ await this._playheadState.set("playheadRef", Date.now(), isLeader);
1349
+ await this._playheadState.set("diffCompensation", this.diffCompensation, isLeader);
1341
1350
  debug(`[${this._sessionId}]: sharing durrent vods diffCompensation=${this.diffCompensation}`);
1342
1351
  this.produceEvent({
1343
1352
  type: 'NOW_PLAYING',
@@ -1414,7 +1423,7 @@ class Session {
1414
1423
  return;
1415
1424
  }
1416
1425
  await this._sessionState.set("mediaSeq", mSeq);
1417
- await this._playheadState.set("mediaSeq", mSeq);
1426
+ await this._playheadState.set("mediaSeq", mSeq, isLeader);
1418
1427
  await this._sessionState.set("discSeq", dSeq);
1419
1428
  // TODO: support demux^
1420
1429
  debug(`[${this._sessionId}]: Setting current media and discontinuity count -> [${mSeq}]:[${dSeq}]`);
@@ -1435,9 +1444,9 @@ class Session {
1435
1444
  await this._sessionState.set("vodReloaded", 1);
1436
1445
  await this._sessionState.set("vodMediaSeqVideo", 0);
1437
1446
  await this._sessionState.set("vodMediaSeqAudio", 0);
1438
- await this._playheadState.set("vodMediaSeqVideo", 0);
1439
- await this._playheadState.set("vodMediaSeqAudio", 0);
1440
- await this._playheadState.set("playheadRef", Date.now());
1447
+ await this._playheadState.set("vodMediaSeqVideo", 0, isLeader);
1448
+ await this._playheadState.set("vodMediaSeqAudio", 0, isLeader);
1449
+ await this._playheadState.set("playheadRef", Date.now(), isLeader);
1441
1450
  // 4) Log to debug and cloudwatch
1442
1451
  debug(`[${this._sessionId}]: LEADER: Set new Reloaded VOD and vodMediaSeq counts in store.`);
1443
1452
  debug(`[${this._sessionId}]: next VOD Reloaded (${currentVod.getDeltaTimes()})`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.0.8",
3
+ "version": "4.0.10",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",