eyevinn-channel-engine 4.2.9 → 4.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/engine/server.d.ts +5 -0
- package/dist/engine/server.js +10 -0
- package/dist/engine/session.d.ts +3 -0
- package/dist/engine/session.js +173 -157
- package/dist/engine/session_state.js +33 -18
- package/dist/examples/truncate.js +2 -1
- package/dist/package.json +3 -3
- package/engine/server.ts +15 -0
- package/engine/session.js +45 -31
- package/engine/session_state.js +13 -1
- package/examples/truncate.ts +2 -1
- package/package.json +3 -3
|
@@ -92,7 +92,7 @@ var SharedSessionState = /** @class */ (function () {
|
|
|
92
92
|
};
|
|
93
93
|
SharedSessionState.prototype.getCurrentVod = function () {
|
|
94
94
|
return __awaiter(this, void 0, void 0, function () {
|
|
95
|
-
var currentVod, hlsVod;
|
|
95
|
+
var currentVod, hlsVod, strToMB;
|
|
96
96
|
return __generator(this, function (_a) {
|
|
97
97
|
switch (_a.label) {
|
|
98
98
|
case 0:
|
|
@@ -107,16 +107,27 @@ var SharedSessionState = /** @class */ (function () {
|
|
|
107
107
|
case 1:
|
|
108
108
|
currentVod = _a.sent();
|
|
109
109
|
hlsVod = null;
|
|
110
|
-
if (currentVod)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
if (!currentVod) return [3 /*break*/, 5];
|
|
111
|
+
if (!this.store.isShared()) return [3 /*break*/, 4];
|
|
112
|
+
strToMB = function (str) {
|
|
113
|
+
var bytesPerCharacter = 2; // Assuming each character takes around 2 bytes in memory
|
|
114
|
+
var stringSizeBytes = str.length * bytesPerCharacter;
|
|
115
|
+
var sizeInMegabytes = stringSizeBytes / (1024 * 1024);
|
|
116
|
+
return sizeInMegabytes.toFixed(1);
|
|
117
|
+
};
|
|
118
|
+
debug("[".concat(this.sessionId, "]: reading ").concat(currentVod.length, " characters or (").concat(strToMB(currentVod), " MB) from shared store (").concat(Date.now(), " < ").concat(this.cache.currentVod.ts + this.cache.currentVod.ttl, ")"));
|
|
119
|
+
hlsVod = new HLSVod();
|
|
120
|
+
hlsVod.fromJSON(currentVod);
|
|
121
|
+
if (!hlsVod.skipSerializeMediaSequences) return [3 /*break*/, 3];
|
|
122
|
+
return [4 /*yield*/, hlsVod.generateMediaSequences()];
|
|
123
|
+
case 2:
|
|
124
|
+
_a.sent();
|
|
125
|
+
_a.label = 3;
|
|
126
|
+
case 3: return [3 /*break*/, 5];
|
|
127
|
+
case 4:
|
|
128
|
+
hlsVod = currentVod;
|
|
129
|
+
_a.label = 5;
|
|
130
|
+
case 5:
|
|
120
131
|
this.cache.currentVod.ts = Date.now();
|
|
121
132
|
this.cache.currentVod.value = hlsVod;
|
|
122
133
|
return [2 /*return*/, hlsVod];
|
|
@@ -133,7 +144,7 @@ var SharedSessionState = /** @class */ (function () {
|
|
|
133
144
|
if (!this.sessionId) {
|
|
134
145
|
throw new Error("shared session state store has not been initialized");
|
|
135
146
|
}
|
|
136
|
-
if (!this.store.isShared()) return [3 /*break*/,
|
|
147
|
+
if (!this.store.isShared()) return [3 /*break*/, 8];
|
|
137
148
|
return [4 /*yield*/, this.store.clearLeaderCache()];
|
|
138
149
|
case 1:
|
|
139
150
|
_a.sent();
|
|
@@ -144,7 +155,7 @@ var SharedSessionState = /** @class */ (function () {
|
|
|
144
155
|
return [4 /*yield*/, this.set("currentVod", hlsVod.toJSON())];
|
|
145
156
|
case 3:
|
|
146
157
|
_a.sent();
|
|
147
|
-
return [3 /*break*/,
|
|
158
|
+
return [3 /*break*/, 7];
|
|
148
159
|
case 4:
|
|
149
160
|
debug("[".concat(this.sessionId, "]: Not a leader. Will not overwrite. Getting currentVod in shared store"));
|
|
150
161
|
return [4 /*yield*/, this.get("currentVod")];
|
|
@@ -152,13 +163,17 @@ var SharedSessionState = /** @class */ (function () {
|
|
|
152
163
|
currentVod = _a.sent();
|
|
153
164
|
hlsVod = new HLSVod();
|
|
154
165
|
hlsVod.fromJSON(currentVod);
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
case
|
|
158
|
-
case 8:
|
|
166
|
+
if (!hlsVod.skipSerializeMediaSequences) return [3 /*break*/, 7];
|
|
167
|
+
return [4 /*yield*/, hlsVod.generateMediaSequences()];
|
|
168
|
+
case 6:
|
|
159
169
|
_a.sent();
|
|
160
|
-
_a.label =
|
|
170
|
+
_a.label = 7;
|
|
171
|
+
case 7: return [3 /*break*/, 10];
|
|
172
|
+
case 8: return [4 /*yield*/, this.set("currentVod", hlsVod)];
|
|
161
173
|
case 9:
|
|
174
|
+
_a.sent();
|
|
175
|
+
_a.label = 10;
|
|
176
|
+
case 10:
|
|
162
177
|
if (this.cache.currentVod) {
|
|
163
178
|
this.cache.currentVod.ts = Date.now();
|
|
164
179
|
this.cache.currentVod.ttl = CURRENTVOD_CACHE_TTL;
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@eyevinn/hls-repeat": "^0.2.0",
|
|
32
|
-
"@eyevinn/hls-truncate": "^0.
|
|
33
|
-
"@eyevinn/hls-vodtolive": "^
|
|
32
|
+
"@eyevinn/hls-truncate": "^0.3.0",
|
|
33
|
+
"@eyevinn/hls-vodtolive": "^4.0.0",
|
|
34
34
|
"@eyevinn/m3u8": "^0.5.3",
|
|
35
35
|
"abort-controller": "^3.0.0",
|
|
36
36
|
"debug": "^3.2.7",
|
package/engine/server.ts
CHANGED
|
@@ -46,6 +46,8 @@ export interface ChannelEngineOpts {
|
|
|
46
46
|
subtitleSliceEndpoint?: string;
|
|
47
47
|
useVTTSubtitles?: boolean;
|
|
48
48
|
alwaysNewSegments?: boolean;
|
|
49
|
+
partialStoreHLSVod?: boolean;
|
|
50
|
+
alwaysMapBandwidthByNearest?: boolean;
|
|
49
51
|
diffCompensationRate?: number;
|
|
50
52
|
staticDirectory?: string;
|
|
51
53
|
averageSegmentDuration?: number;
|
|
@@ -100,6 +102,7 @@ export interface VodResponse {
|
|
|
100
102
|
offset?: number;
|
|
101
103
|
diffMs?: number;
|
|
102
104
|
desiredDuration?: number;
|
|
105
|
+
startOffset?: number;
|
|
103
106
|
type?: string;
|
|
104
107
|
currentMetadata?: VodResponseMetadata;
|
|
105
108
|
timedMetadata?: VodTimedMetadata;
|
|
@@ -181,6 +184,8 @@ export class ChannelEngine {
|
|
|
181
184
|
private subtitleSliceEndpoint: string;
|
|
182
185
|
private useVTTSubtitles: boolean;
|
|
183
186
|
private alwaysNewSegments: boolean;
|
|
187
|
+
private partialStoreHLSVod: boolean;
|
|
188
|
+
private alwaysMapBandwidthByNearest: boolean;
|
|
184
189
|
private defaultSlateUri?: string;
|
|
185
190
|
private slateDuration?: number;
|
|
186
191
|
private assetMgr: IAssetManager;
|
|
@@ -220,6 +225,14 @@ export class ChannelEngine {
|
|
|
220
225
|
if (options && options.alwaysNewSegments) {
|
|
221
226
|
this.alwaysNewSegments = true;
|
|
222
227
|
}
|
|
228
|
+
this.partialStoreHLSVod = false;
|
|
229
|
+
if (options && options.partialStoreHLSVod) {
|
|
230
|
+
this.partialStoreHLSVod = true;
|
|
231
|
+
}
|
|
232
|
+
this.alwaysMapBandwidthByNearest = false;
|
|
233
|
+
if (options && options.alwaysMapBandwidthByNearest) {
|
|
234
|
+
this.alwaysMapBandwidthByNearest = true;
|
|
235
|
+
}
|
|
223
236
|
if (options && options.defaultSlateUri) {
|
|
224
237
|
this.defaultSlateUri = options.defaultSlateUri;
|
|
225
238
|
this.slateRepetitions = options.slateRepetitions || 10;
|
|
@@ -453,6 +466,8 @@ export class ChannelEngine {
|
|
|
453
466
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
454
467
|
useVTTSubtitles: this.useVTTSubtitles,
|
|
455
468
|
alwaysNewSegments: options.alwaysNewSegments,
|
|
469
|
+
partialStoreHLSVod: options.partialStoreHLSVod,
|
|
470
|
+
alwaysMapBandwidthByNearest: options.alwaysMapBandwidthByNearest,
|
|
456
471
|
noSessionDataTags: options.noSessionDataTags,
|
|
457
472
|
playheadDiffThreshold: channel.options && channel.options.playheadDiffThreshold ? channel.options.playheadDiffThreshold : this.streamerOpts.defaultPlayheadDiffThreshold,
|
|
458
473
|
maxTickInterval: channel.options && channel.options.maxTickInterval ? channel.options.maxTickInterval : this.streamerOpts.defaultMaxTickInterval,
|
package/engine/session.js
CHANGED
|
@@ -72,11 +72,22 @@ class Session {
|
|
|
72
72
|
}
|
|
73
73
|
this.isAllowedToClearVodCache = null;
|
|
74
74
|
this.alwaysNewSegments = null;
|
|
75
|
+
this.alwaysMapBandwidthByNearest = null;
|
|
76
|
+
this.partialStoreHLSVod = null;
|
|
77
|
+
this.currentPlayheadRef = null;
|
|
75
78
|
if (config) {
|
|
76
79
|
if (config.alwaysNewSegments) {
|
|
77
80
|
this.alwaysNewSegments = config.alwaysNewSegments;
|
|
78
81
|
}
|
|
79
82
|
|
|
83
|
+
if (config.partialStoreHLSVod) {
|
|
84
|
+
this.partialStoreHLSVod = config.partialStoreHLSVod;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (config.alwaysMapBandwidthByNearest) {
|
|
88
|
+
this.alwaysMapBandwidthByNearest = config.alwaysMapBandwidthByNearest;
|
|
89
|
+
}
|
|
90
|
+
|
|
80
91
|
if (config.sessionId) {
|
|
81
92
|
this._sessionId = config.sessionId;
|
|
82
93
|
}
|
|
@@ -632,7 +643,7 @@ class Session {
|
|
|
632
643
|
}
|
|
633
644
|
let currentVod = null;
|
|
634
645
|
const sessionState = await this._sessionState.getValues(["discSeqAudio", "vodMediaSeqAudio"]);
|
|
635
|
-
let playheadState = await this._playheadState.getValues(["mediaSeqAudio", "vodMediaSeqAudio"]);
|
|
646
|
+
let playheadState = await this._playheadState.getValues(["mediaSeqAudio", "vodMediaSeqAudio", "playheadRef"]);
|
|
636
647
|
|
|
637
648
|
if (playheadState.vodMediaSeqAudio > sessionState.vodMediaSeqAudio || (playheadState.vodMediaSeqAudio < sessionState.vodMediaSeqAudio && playheadState.mediaSeqAudio === this.prevMediaSeqOffset.audio)) {
|
|
638
649
|
const state = await this._sessionState.get("state");
|
|
@@ -653,14 +664,10 @@ class Session {
|
|
|
653
664
|
if (currentVod) {
|
|
654
665
|
// condition suggesting that a new vod should exist
|
|
655
666
|
if (playheadState.vodMediaSeqAudio < 2 || playheadState.mediaSeqAudio !== this.prevMediaSeqOffset.audio) {
|
|
656
|
-
|
|
657
|
-
let cacheAge = null;
|
|
658
|
-
if (this._sessionState.cache && this._sessionState.cache.currentVod.ts) {
|
|
659
|
-
cacheAge = Date.now() - this._sessionState.cache.currentVod.ts;
|
|
660
|
-
}
|
|
661
|
-
if (cacheAge !== null && cacheAge > AGE_THRESH) {
|
|
667
|
+
if (playheadState.playheadRef > this.currentPlayheadRef) {
|
|
662
668
|
await timer(500);
|
|
663
|
-
|
|
669
|
+
this.currentPlayheadRef = playheadState.playheadRef;
|
|
670
|
+
debug(`[${this._sessionId}]: While requesting audio manifest for ${audioGroupId}-${audioLanguage}, (mseq=${playheadState.vodMediaSeqAudio})`);
|
|
664
671
|
await this._sessionState.clearCurrentVodCache(); // force reading up from shared store
|
|
665
672
|
currentVod = await this._sessionState.getCurrentVod();
|
|
666
673
|
}
|
|
@@ -707,7 +714,7 @@ class Session {
|
|
|
707
714
|
}
|
|
708
715
|
let currentVod = null;
|
|
709
716
|
const sessionState = await this._sessionState.getValues(["discSeqSubtitle", "vodMediaSeqSubtitle"]);
|
|
710
|
-
let playheadState = await this._playheadState.getValues(["mediaSeqSubtitle", "vodMediaSeqSubtitle"]);
|
|
717
|
+
let playheadState = await this._playheadState.getValues(["mediaSeqSubtitle", "vodMediaSeqSubtitle", "playheadRef"]);
|
|
711
718
|
|
|
712
719
|
if (playheadState.vodMediaSeqSubtitle > sessionState.vodMediaSeqSubtitle || (playheadState.vodMediaSeqSubtitle < sessionState.vodMediaSeqSubtitle && playheadState.mediaSeqSubtitle === this.prevMediaSeqOffset.subtitle)) {
|
|
713
720
|
const state = await this._sessionState.get("state");
|
|
@@ -728,14 +735,10 @@ class Session {
|
|
|
728
735
|
if (currentVod) {
|
|
729
736
|
// condition suggesting that a new vod should exist
|
|
730
737
|
if (playheadState.vodMediaSeqSubtitle < 2 || playheadState.mediaSeqSubtitle !== this.prevMediaSeqOffset.subtitle) {
|
|
731
|
-
|
|
732
|
-
let cacheAge = null;
|
|
733
|
-
if (this._sessionState.cache && this._sessionState.cache.currentVod.ts) {
|
|
734
|
-
cacheAge = Date.now() - this._sessionState.cache.currentVod.ts;
|
|
735
|
-
}
|
|
736
|
-
if (cacheAge !== null && cacheAge > AGE_THRESH) {
|
|
738
|
+
if (playheadState.playheadRef > this.currentPlayheadRef) {
|
|
737
739
|
await timer(500);
|
|
738
|
-
|
|
740
|
+
this.currentPlayheadRef = playheadState.playheadRef;
|
|
741
|
+
debug(`[${this._sessionId}]: While requesting subtitle manifest for ${subtitleGroupId}-${subtitleLanguage}, (mseq=${playheadState.vodMediaSeqSubtitle})`);
|
|
739
742
|
await this._sessionState.clearCurrentVodCache(); // force reading up from shared store
|
|
740
743
|
currentVod = await this._sessionState.getCurrentVod();
|
|
741
744
|
}
|
|
@@ -828,7 +831,7 @@ class Session {
|
|
|
828
831
|
let positionV = 0;
|
|
829
832
|
let positionA = 0;
|
|
830
833
|
const position = (await this._getCurrentPlayheadPosition()) * 1000;
|
|
831
|
-
positionV = position / 1000;
|
|
834
|
+
positionV = position ? position / 1000 : 0;
|
|
832
835
|
let currentVod = await this._sessionState.getCurrentVod();
|
|
833
836
|
const sessionState = await this._sessionState.getValues(["vodMediaSeqAudio"]);
|
|
834
837
|
let posDiff;
|
|
@@ -840,11 +843,10 @@ class Session {
|
|
|
840
843
|
debug(`[${this._sessionId}]: About to determine audio increment`);
|
|
841
844
|
do {
|
|
842
845
|
const audioPosition = (await this._getAudioPlayheadPosition(sessionState.vodMediaSeqAudio + index)) * 1000;
|
|
843
|
-
positionA = audioPosition / 1000;
|
|
844
|
-
posDiff = (positionV-positionA).toFixed(3);
|
|
845
|
-
debug(`[${this._sessionId}]: positionV=${positionV};positionA=${positionA};posDiff=${posDiff}
|
|
846
|
+
positionA = audioPosition ? audioPosition / 1000 : 0;
|
|
847
|
+
posDiff = (positionV - positionA).toFixed(3);
|
|
848
|
+
debug(`[${this._sessionId}]: positionV=${positionV};positionA=${positionA};posDiff=${posDiff}`);
|
|
846
849
|
if (posDiff <= maxAcceptableDiff) {
|
|
847
|
-
debug(`[${this._sessionId}]: posDiff value (${posDiff}) is acceptable`);
|
|
848
850
|
break;
|
|
849
851
|
}
|
|
850
852
|
if (posDiff > thresh) {
|
|
@@ -858,7 +860,7 @@ class Session {
|
|
|
858
860
|
if (sessionState.vodMediaSeqAudio + index > audioSeqLastIdx) {
|
|
859
861
|
break;
|
|
860
862
|
}
|
|
861
|
-
} while (!(-thresh < posDiff && posDiff < thresh));
|
|
863
|
+
} while (!(-thresh < posDiff && posDiff < thresh) && !isNaN(posDiff));
|
|
862
864
|
audioIncrement = index;
|
|
863
865
|
debug(`[${this._sessionId}]: Current VOD Playhead Positions are to be: [${positionV.toFixed(3)}][${positionA.toFixed(3)}] (${posDiff})`);
|
|
864
866
|
}
|
|
@@ -1271,7 +1273,7 @@ class Session {
|
|
|
1271
1273
|
await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
|
|
1272
1274
|
await this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio);
|
|
1273
1275
|
await this._sessionState.set("slateCount", sessionState.slateCount + 1);
|
|
1274
|
-
await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1276
|
+
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1275
1277
|
|
|
1276
1278
|
cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'slateInserted', channel: this._sessionId });
|
|
1277
1279
|
|
|
@@ -1326,7 +1328,9 @@ class Session {
|
|
|
1326
1328
|
dummySubtitleEndpoint: this.dummySubtitleEndpoint,
|
|
1327
1329
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
1328
1330
|
shouldContainSubtitles: this.use_vtt_subtitles,
|
|
1329
|
-
expectedSubtitleTracks: this._subtitleTracks
|
|
1331
|
+
expectedSubtitleTracks: this._subtitleTracks,
|
|
1332
|
+
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
|
|
1333
|
+
skipSerializeMediaSequences: this.partialStoreHLSVod
|
|
1330
1334
|
};
|
|
1331
1335
|
newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
|
|
1332
1336
|
if (vodResponse.timedMetadata) {
|
|
@@ -1370,7 +1374,6 @@ class Session {
|
|
|
1370
1374
|
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
1371
1375
|
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
1372
1376
|
sessionState.vodMediaSeqSubtitle = await this._sessionState.set("vodMediaSeqSubtitle", 0);
|
|
1373
|
-
await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1374
1377
|
this.produceEvent({
|
|
1375
1378
|
type: 'NOW_PLAYING',
|
|
1376
1379
|
data: {
|
|
@@ -1380,6 +1383,7 @@ class Session {
|
|
|
1380
1383
|
});
|
|
1381
1384
|
sessionState.state = await this._sessionState.set("state", SessionState.VOD_PLAYING);
|
|
1382
1385
|
sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
|
|
1386
|
+
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1383
1387
|
await this._sessionState.remove("nextVod");
|
|
1384
1388
|
return;
|
|
1385
1389
|
} else {
|
|
@@ -1502,7 +1506,9 @@ class Session {
|
|
|
1502
1506
|
dummySubtitleEndpoint: this.dummySubtitleEndpoint,
|
|
1503
1507
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
1504
1508
|
shouldContainSubtitles: this.use_vtt_subtitles,
|
|
1505
|
-
expectedSubtitleTracks: this._subtitleTracks
|
|
1509
|
+
expectedSubtitleTracks: this._subtitleTracks,
|
|
1510
|
+
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
|
|
1511
|
+
skipSerializeMediaSequences: this.partialStoreHLSVod
|
|
1506
1512
|
};
|
|
1507
1513
|
newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
|
|
1508
1514
|
if (vodResponse.timedMetadata) {
|
|
@@ -1554,7 +1560,7 @@ class Session {
|
|
|
1554
1560
|
currentVod = newVod;
|
|
1555
1561
|
debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}; audio msequences=${currentVod.getLiveMediaSequencesCount("audio")}; subtitle msequences=${currentVod.getLiveMediaSequencesCount("subtitle")}`);
|
|
1556
1562
|
sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
|
|
1557
|
-
await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1563
|
+
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1558
1564
|
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
1559
1565
|
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
1560
1566
|
sessionState.vodMediaSeqSubtitle = await this._sessionState.set("vodMediaSeqSubtitle", 0);
|
|
@@ -1669,7 +1675,7 @@ class Session {
|
|
|
1669
1675
|
await this._playheadState.set("vodMediaSeqVideo", 0, isLeader);
|
|
1670
1676
|
await this._playheadState.set("vodMediaSeqAudio", 0, isLeader);
|
|
1671
1677
|
await this._playheadState.set("vodMediaSeqSubtitle", 0, isLeader);
|
|
1672
|
-
await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1678
|
+
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
|
|
1673
1679
|
// 4) Log to debug and cloudwatch
|
|
1674
1680
|
debug(`[${this._sessionId}]: LEADER: Set new Reloaded VOD and vodMediaSeq counts in store.`);
|
|
1675
1681
|
debug(`[${this._sessionId}]: next VOD Reloaded (${currentVod.getDeltaTimes()})`);
|
|
@@ -1749,7 +1755,9 @@ class Session {
|
|
|
1749
1755
|
dummySubtitleEndpoint: this.dummySubtitleEndpoint,
|
|
1750
1756
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
1751
1757
|
shouldContainSubtitles: this.use_vtt_subtitles,
|
|
1752
|
-
expectedSubtitleTracks: this._subtitleTracks
|
|
1758
|
+
expectedSubtitleTracks: this._subtitleTracks,
|
|
1759
|
+
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
|
|
1760
|
+
skipSerializeMediaSequences: this.partialStoreHLSVod
|
|
1753
1761
|
};
|
|
1754
1762
|
const timestamp = Date.now();
|
|
1755
1763
|
hlsVod = new HLSVod(this.slateUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
|
|
@@ -1799,7 +1807,11 @@ class Session {
|
|
|
1799
1807
|
_truncateVod(vodResponse) {
|
|
1800
1808
|
return new Promise((resolve, reject) => {
|
|
1801
1809
|
try {
|
|
1802
|
-
const
|
|
1810
|
+
const options = {};
|
|
1811
|
+
if (vodResponse.startOffset) {
|
|
1812
|
+
options.offset = vodResponse.startOffset / 1000;
|
|
1813
|
+
}
|
|
1814
|
+
const truncatedVod = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000, options);
|
|
1803
1815
|
truncatedVod.load()
|
|
1804
1816
|
.then(() => {
|
|
1805
1817
|
let audioManifestLoader;
|
|
@@ -1848,7 +1860,9 @@ class Session {
|
|
|
1848
1860
|
dummySubtitleEndpoint: this.dummySubtitleEndpoint,
|
|
1849
1861
|
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
|
|
1850
1862
|
shouldContainSubtitles: this.use_vtt_subtitles,
|
|
1851
|
-
expectedSubtitleTracks: this._subtitleTracks
|
|
1863
|
+
expectedSubtitleTracks: this._subtitleTracks,
|
|
1864
|
+
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
|
|
1865
|
+
skipSerializeMediaSequences: this.partialStoreHLSVod
|
|
1852
1866
|
};
|
|
1853
1867
|
const timestamp = Date.now();
|
|
1854
1868
|
hlsVod = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
|
package/engine/session_state.js
CHANGED
|
@@ -57,9 +57,18 @@ class SharedSessionState {
|
|
|
57
57
|
let hlsVod = null;
|
|
58
58
|
if (currentVod) {
|
|
59
59
|
if (this.store.isShared()) {
|
|
60
|
-
|
|
60
|
+
const strToMB = (str) => {
|
|
61
|
+
const bytesPerCharacter = 2; // Assuming each character takes around 2 bytes in memory
|
|
62
|
+
const stringSizeBytes = str.length * bytesPerCharacter;
|
|
63
|
+
const sizeInMegabytes = stringSizeBytes / (1024 * 1024);
|
|
64
|
+
return sizeInMegabytes.toFixed(1);
|
|
65
|
+
}
|
|
66
|
+
debug(`[${this.sessionId}]: reading ${currentVod.length} characters or (${strToMB(currentVod)} MB) from shared store (${Date.now()} < ${this.cache.currentVod.ts + this.cache.currentVod.ttl})`);
|
|
61
67
|
hlsVod = new HLSVod();
|
|
62
68
|
hlsVod.fromJSON(currentVod);
|
|
69
|
+
if (hlsVod.skipSerializeMediaSequences) {
|
|
70
|
+
await hlsVod.generateMediaSequences();
|
|
71
|
+
}
|
|
63
72
|
} else {
|
|
64
73
|
hlsVod = currentVod;
|
|
65
74
|
}
|
|
@@ -83,6 +92,9 @@ class SharedSessionState {
|
|
|
83
92
|
const currentVod = await this.get("currentVod");
|
|
84
93
|
hlsVod = new HLSVod();
|
|
85
94
|
hlsVod.fromJSON(currentVod);
|
|
95
|
+
if (hlsVod.skipSerializeMediaSequences) {
|
|
96
|
+
await hlsVod.generateMediaSequences();
|
|
97
|
+
}
|
|
86
98
|
}
|
|
87
99
|
} else {
|
|
88
100
|
await this.set("currentVod", hlsVod);
|
package/examples/truncate.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"license": "Apache-2.0",
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@eyevinn/hls-repeat": "^0.2.0",
|
|
32
|
-
"@eyevinn/hls-truncate": "^0.
|
|
33
|
-
"@eyevinn/hls-vodtolive": "^
|
|
32
|
+
"@eyevinn/hls-truncate": "^0.3.0",
|
|
33
|
+
"@eyevinn/hls-vodtolive": "^4.0.0",
|
|
34
34
|
"@eyevinn/m3u8": "^0.5.3",
|
|
35
35
|
"abort-controller": "^3.0.0",
|
|
36
36
|
"debug": "^3.2.7",
|