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.
- package/dist/engine/playhead_state.d.ts +1 -1
- package/dist/engine/playhead_state.js +6 -2
- package/dist/engine/redis_state_store.d.ts +1 -0
- package/dist/engine/redis_state_store.js +20 -8
- package/dist/engine/server.d.ts +1 -0
- package/dist/engine/server.js +33 -23
- package/dist/engine/session.js +46 -33
- package/dist/package.json +1 -1
- package/engine/playhead_state.js +6 -2
- package/engine/redis_state_store.js +17 -5
- package/engine/server.ts +22 -10
- package/engine/session.js +27 -18
- package/package.json +1 -1
|
@@ -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:
|
|
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
|
});
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
199
|
+
_this.client.expire(storeKey, _this.volatileKeyTTL, function (err, res) {
|
|
191
200
|
if (!err) {
|
|
192
|
-
debug("REDIS expire ".concat(storeKey, " ").concat(
|
|
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
|
}
|
package/dist/engine/server.d.ts
CHANGED
package/dist/engine/server.js
CHANGED
|
@@ -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
|
-
|
|
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*/,
|
|
892
|
+
if (!(session && sessionLive)) return [3 /*break*/, 12];
|
|
888
893
|
_a.label = 1;
|
|
889
894
|
case 1:
|
|
890
|
-
_a.trys.push([1,
|
|
891
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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*/,
|
|
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*/,
|
|
925
|
-
case
|
|
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*/,
|
|
929
|
-
case
|
|
930
|
-
case
|
|
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 =
|
|
934
|
-
case
|
|
943
|
+
_a.label = 13;
|
|
944
|
+
case 13: return [2 /*return*/];
|
|
935
945
|
}
|
|
936
946
|
});
|
|
937
947
|
});
|
package/dist/engine/session.js
CHANGED
|
@@ -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*/,
|
|
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.
|
|
1521
|
+
return [4 /*yield*/, this._sessionStateStore.isLeader(this._instanceId)];
|
|
1514
1522
|
case 3:
|
|
1515
|
-
_a.sent();
|
|
1516
|
-
return [4 /*yield*/, this._sessionState.set("
|
|
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("
|
|
1527
|
+
return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
|
|
1520
1528
|
case 5:
|
|
1521
1529
|
_a.sent();
|
|
1522
|
-
return [4 /*yield*/, this._sessionState.
|
|
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.
|
|
1533
|
+
return [4 /*yield*/, this._sessionState.setCurrentVod(slateVod)];
|
|
1526
1534
|
case 7:
|
|
1527
1535
|
_a.sent();
|
|
1528
|
-
return [4 /*yield*/, this._sessionState.set("
|
|
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("
|
|
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("
|
|
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("
|
|
1545
|
+
return [4 /*yield*/, this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio)];
|
|
1538
1546
|
case 11:
|
|
1539
1547
|
_a.sent();
|
|
1540
|
-
return [4 /*yield*/, this.
|
|
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
|
|
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
package/engine/playhead_state.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
94
|
+
this.client.expire(storeKey, this.volatileKeyTTL, (err, res) => {
|
|
86
95
|
if (!err) {
|
|
87
|
-
debug(`REDIS expire ${storeKey} ${
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
playheadState.
|
|
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}]:
|
|
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}]:
|
|
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()})`);
|