eyevinn-channel-engine 4.2.8 → 4.2.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/README.md CHANGED
@@ -134,6 +134,7 @@ In addition there other reference implemetations that can be used:
134
134
  - `examples/multicodec.ts` : example with multicodec
135
135
  - `examples/drm.ts` : example with DRM
136
136
  - `examples/autocreate.ts` : example to auto create channel on demand
137
+ - `examples/truncate.ts` : example when trimming start and end of a VOD
137
138
 
138
139
  ### Source linking hls-vodtolive library
139
140
 
@@ -17,6 +17,7 @@ export interface ChannelEngineOpts {
17
17
  subtitleSliceEndpoint?: string;
18
18
  useVTTSubtitles?: boolean;
19
19
  alwaysNewSegments?: boolean;
20
+ alwaysMapBandwidthByNearest?: boolean;
20
21
  diffCompensationRate?: number;
21
22
  staticDirectory?: string;
22
23
  averageSegmentDuration?: number;
@@ -56,6 +57,7 @@ export interface VodResponse {
56
57
  offset?: number;
57
58
  diffMs?: number;
58
59
  desiredDuration?: number;
60
+ startOffset?: number;
59
61
  type?: string;
60
62
  currentMetadata?: VodResponseMetadata;
61
63
  timedMetadata?: VodTimedMetadata;
@@ -125,6 +127,7 @@ export declare class ChannelEngine {
125
127
  private subtitleSliceEndpoint;
126
128
  private useVTTSubtitles;
127
129
  private alwaysNewSegments;
130
+ private alwaysMapBandwidthByNearest;
128
131
  private defaultSlateUri?;
129
132
  private slateDuration?;
130
133
  private assetMgr;
@@ -89,6 +89,10 @@ var ChannelEngine = /** @class */ (function () {
89
89
  if (options && options.alwaysNewSegments) {
90
90
  this.alwaysNewSegments = true;
91
91
  }
92
+ this.alwaysMapBandwidthByNearest = false;
93
+ if (options && options.alwaysMapBandwidthByNearest) {
94
+ this.alwaysMapBandwidthByNearest = true;
95
+ }
92
96
  if (options && options.defaultSlateUri) {
93
97
  this.defaultSlateUri = options.defaultSlateUri;
94
98
  this.slateRepetitions = options.slateRepetitions || 10;
@@ -437,6 +441,7 @@ var ChannelEngine = /** @class */ (function () {
437
441
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
438
442
  useVTTSubtitles: this.useVTTSubtitles,
439
443
  alwaysNewSegments: options.alwaysNewSegments,
444
+ alwaysMapBandwidthByNearest: options.alwaysMapBandwidthByNearest,
440
445
  noSessionDataTags: options.noSessionDataTags,
441
446
  playheadDiffThreshold: channel.options && channel.options.playheadDiffThreshold ? channel.options.playheadDiffThreshold : this.streamerOpts.defaultPlayheadDiffThreshold,
442
447
  maxTickInterval: channel.options && channel.options.maxTickInterval ? channel.options.maxTickInterval : this.streamerOpts.defaultMaxTickInterval,
@@ -49,6 +49,7 @@ declare class Session {
49
49
  };
50
50
  isAllowedToClearVodCache: boolean;
51
51
  alwaysNewSegments: any;
52
+ alwaysMapBandwidthByNearest: any;
52
53
  _category: any;
53
54
  startWithId: any;
54
55
  _sessionProfile: any;
@@ -101,10 +101,14 @@ var Session = /** @class */ (function () {
101
101
  };
102
102
  this.isAllowedToClearVodCache = null;
103
103
  this.alwaysNewSegments = null;
104
+ this.alwaysMapBandwidthByNearest = null;
104
105
  if (config) {
105
106
  if (config.alwaysNewSegments) {
106
107
  this.alwaysNewSegments = config.alwaysNewSegments;
107
108
  }
109
+ if (config.alwaysMapBandwidthByNearest) {
110
+ this.alwaysMapBandwidthByNearest = config.alwaysMapBandwidthByNearest;
111
+ }
108
112
  if (config.sessionId) {
109
113
  this._sessionId = config.sessionId;
110
114
  }
@@ -1142,7 +1146,7 @@ var Session = /** @class */ (function () {
1142
1146
  return [4 /*yield*/, this._getCurrentPlayheadPosition()];
1143
1147
  case 16:
1144
1148
  position = (_s.sent()) * 1000;
1145
- positionV = position / 1000;
1149
+ positionV = position ? position / 1000 : 0;
1146
1150
  return [4 /*yield*/, this._sessionState.getCurrentVod()];
1147
1151
  case 17:
1148
1152
  currentVod_1 = _s.sent();
@@ -1160,11 +1164,10 @@ var Session = /** @class */ (function () {
1160
1164
  case 19: return [4 /*yield*/, this._getAudioPlayheadPosition(sessionState_1.vodMediaSeqAudio + index)];
1161
1165
  case 20:
1162
1166
  audioPosition = (_s.sent()) * 1000;
1163
- positionA = audioPosition / 1000;
1167
+ positionA = audioPosition ? audioPosition / 1000 : 0;
1164
1168
  posDiff = (positionV - positionA).toFixed(3);
1165
- debug("[".concat(this._sessionId, "]: positionV=").concat(positionV, ";positionA=").concat(positionA, ";posDiff=").concat(posDiff, ";(posDiff <= 0.001)=").concat(posDiff <= 0.001));
1169
+ debug("[".concat(this._sessionId, "]: positionV=").concat(positionV, ";positionA=").concat(positionA, ";posDiff=").concat(posDiff));
1166
1170
  if (posDiff <= maxAcceptableDiff) {
1167
- debug("[".concat(this._sessionId, "]: posDiff value (").concat(posDiff, ") is acceptable"));
1168
1171
  return [3 /*break*/, 22];
1169
1172
  }
1170
1173
  if (posDiff > thresh) {
@@ -1181,7 +1184,7 @@ var Session = /** @class */ (function () {
1181
1184
  }
1182
1185
  _s.label = 21;
1183
1186
  case 21:
1184
- if (!(-thresh < posDiff && posDiff < thresh)) return [3 /*break*/, 19];
1187
+ if (!(-thresh < posDiff && posDiff < thresh) && !isNaN(posDiff)) return [3 /*break*/, 19];
1185
1188
  _s.label = 22;
1186
1189
  case 22:
1187
1190
  audioIncrement = index;
@@ -1836,7 +1839,8 @@ var Session = /** @class */ (function () {
1836
1839
  dummySubtitleEndpoint: this.dummySubtitleEndpoint,
1837
1840
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
1838
1841
  shouldContainSubtitles: this.use_vtt_subtitles,
1839
- expectedSubtitleTracks: this._subtitleTracks
1842
+ expectedSubtitleTracks: this._subtitleTracks,
1843
+ alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
1840
1844
  };
1841
1845
  newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1842
1846
  if (vodResponse.timedMetadata) {
@@ -2084,7 +2088,8 @@ var Session = /** @class */ (function () {
2084
2088
  dummySubtitleEndpoint: this.dummySubtitleEndpoint,
2085
2089
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
2086
2090
  shouldContainSubtitles: this.use_vtt_subtitles,
2087
- expectedSubtitleTracks: this._subtitleTracks
2091
+ expectedSubtitleTracks: this._subtitleTracks,
2092
+ alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
2088
2093
  };
2089
2094
  newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
2090
2095
  if (vodResponse.timedMetadata) {
@@ -2143,53 +2148,53 @@ var Session = /** @class */ (function () {
2143
2148
  currentVod = newVod;
2144
2149
  debug("[".concat(this._sessionId, "]: msequences=").concat(currentVod.getLiveMediaSequencesCount(), "; audio msequences=").concat(currentVod.getLiveMediaSequencesCount("audio"), "; subtitle msequences=").concat(currentVod.getLiveMediaSequencesCount("subtitle")));
2145
2150
  _w = sessionState;
2146
- return [4 /*yield*/, this._sessionState.set("vodMediaSeqVideo", 0)];
2151
+ return [4 /*yield*/, this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 })];
2147
2152
  case 56:
2148
- _w.vodMediaSeqVideo = _10.sent();
2149
- _x = sessionState;
2150
- return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
2153
+ _w.currentVod = _10.sent();
2154
+ return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now(), isLeader)];
2151
2155
  case 57:
2152
- _x.vodMediaSeqAudio = _10.sent();
2153
- _y = sessionState;
2154
- return [4 /*yield*/, this._sessionState.set("vodMediaSeqSubtitle", 0)];
2156
+ _10.sent();
2157
+ _x = sessionState;
2158
+ return [4 /*yield*/, this._sessionState.set("vodMediaSeqVideo", 0)];
2155
2159
  case 58:
2156
- _y.vodMediaSeqSubtitle = _10.sent();
2157
- _z = sessionState;
2158
- return [4 /*yield*/, this._sessionState.set("mediaSeq", sessionState.mediaSeq + endMseqValue)];
2160
+ _x.vodMediaSeqVideo = _10.sent();
2161
+ _y = sessionState;
2162
+ return [4 /*yield*/, this._sessionState.set("vodMediaSeqAudio", 0)];
2159
2163
  case 59:
2160
- _z.mediaSeq = _10.sent();
2161
- _0 = sessionState;
2162
- return [4 /*yield*/, this._sessionState.set("mediaSeqAudio", sessionState.mediaSeqAudio + endMseqValueAudio)];
2164
+ _y.vodMediaSeqAudio = _10.sent();
2165
+ _z = sessionState;
2166
+ return [4 /*yield*/, this._sessionState.set("vodMediaSeqSubtitle", 0)];
2163
2167
  case 60:
2164
- _0.mediaSeqAudio = _10.sent();
2165
- _1 = sessionState;
2166
- return [4 /*yield*/, this._sessionState.set("mediaSeqSubtitle", sessionState.mediaSeqSubtitle + endMseqValueSubtitle)];
2168
+ _z.vodMediaSeqSubtitle = _10.sent();
2169
+ _0 = sessionState;
2170
+ return [4 /*yield*/, this._sessionState.set("mediaSeq", sessionState.mediaSeq + endMseqValue)];
2167
2171
  case 61:
2168
- _1.mediaSeqSubtitle = _10.sent();
2169
- _2 = sessionState;
2170
- return [4 /*yield*/, this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity)];
2172
+ _0.mediaSeq = _10.sent();
2173
+ _1 = sessionState;
2174
+ return [4 /*yield*/, this._sessionState.set("mediaSeqAudio", sessionState.mediaSeqAudio + endMseqValueAudio)];
2171
2175
  case 62:
2172
- _2.discSeq = _10.sent();
2173
- _3 = sessionState;
2174
- return [4 /*yield*/, this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio)];
2176
+ _1.mediaSeqAudio = _10.sent();
2177
+ _2 = sessionState;
2178
+ return [4 /*yield*/, this._sessionState.set("mediaSeqSubtitle", sessionState.mediaSeqSubtitle + endMseqValueSubtitle)];
2175
2179
  case 63:
2176
- _3.discSeqAudio = _10.sent();
2177
- _4 = sessionState;
2178
- return [4 /*yield*/, this._sessionState.set("discSeqSubtitle", sessionState.discSeqSubtitle + lastDiscontinuitySubtitle)];
2180
+ _2.mediaSeqSubtitle = _10.sent();
2181
+ _3 = sessionState;
2182
+ return [4 /*yield*/, this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity)];
2179
2183
  case 64:
2180
- _4.discSeqSubtitle = _10.sent();
2181
- debug("[".concat(this._sessionId, "]: new sequence data set in store V[").concat(sessionState.mediaSeq, "][").concat(sessionState.discSeq, "]_A[").concat(sessionState.mediaSeqAudio, "][").concat(sessionState.discSeqAudio, "]_S[").concat(sessionState.mediaSeqSubtitle, "][").concat(sessionState.discSeqSubtitle, "]"));
2182
- return [4 /*yield*/, this._sessionState.remove("nextVod")];
2184
+ _3.discSeq = _10.sent();
2185
+ _4 = sessionState;
2186
+ return [4 /*yield*/, this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio)];
2183
2187
  case 65:
2184
- _10.sent();
2188
+ _4.discSeqAudio = _10.sent();
2185
2189
  _5 = sessionState;
2186
- return [4 /*yield*/, this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 })];
2190
+ return [4 /*yield*/, this._sessionState.set("discSeqSubtitle", sessionState.discSeqSubtitle + lastDiscontinuitySubtitle)];
2187
2191
  case 66:
2188
- _5.currentVod = _10.sent();
2189
- this.leaderIsSettingNextVod = false;
2190
- return [4 /*yield*/, this._playheadState.set("playheadRef", Date.now(), isLeader)];
2192
+ _5.discSeqSubtitle = _10.sent();
2193
+ debug("[".concat(this._sessionId, "]: new sequence data set in store V[").concat(sessionState.mediaSeq, "][").concat(sessionState.discSeq, "]_A[").concat(sessionState.mediaSeqAudio, "][").concat(sessionState.discSeqAudio, "]_S[").concat(sessionState.mediaSeqSubtitle, "][").concat(sessionState.discSeqSubtitle, "]"));
2194
+ return [4 /*yield*/, this._sessionState.remove("nextVod")];
2191
2195
  case 67:
2192
2196
  _10.sent();
2197
+ this.leaderIsSettingNextVod = false;
2193
2198
  return [4 /*yield*/, this._playheadState.set("diffCompensation", this.diffCompensation, isLeader)];
2194
2199
  case 68:
2195
2200
  _10.sent();
@@ -2429,7 +2434,8 @@ var Session = /** @class */ (function () {
2429
2434
  dummySubtitleEndpoint: _this.dummySubtitleEndpoint,
2430
2435
  subtitleSliceEndpoint: _this.subtitleSliceEndpoint,
2431
2436
  shouldContainSubtitles: _this.use_vtt_subtitles,
2432
- expectedSubtitleTracks: _this._subtitleTracks
2437
+ expectedSubtitleTracks: _this._subtitleTracks,
2438
+ alwaysMapBandwidthByNearest: _this.alwaysMapBandwidthByNearest
2433
2439
  };
2434
2440
  var timestamp = Date.now();
2435
2441
  hlsVod_1 = new HLSVod(_this.slateUri, null, timestamp, null, m3u8Header(_this._instanceId), hlsOpts);
@@ -2482,7 +2488,11 @@ var Session = /** @class */ (function () {
2482
2488
  var _this = this;
2483
2489
  return new Promise(function (resolve, reject) {
2484
2490
  try {
2485
- var truncatedVod_1 = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000);
2491
+ var options = {};
2492
+ if (vodResponse.startOffset) {
2493
+ options.offset = vodResponse.startOffset / 1000;
2494
+ }
2495
+ var truncatedVod_1 = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000, options);
2486
2496
  truncatedVod_1.load()
2487
2497
  .then(function () {
2488
2498
  var audioManifestLoader;
@@ -2532,7 +2542,8 @@ var Session = /** @class */ (function () {
2532
2542
  dummySubtitleEndpoint: _this.dummySubtitleEndpoint,
2533
2543
  subtitleSliceEndpoint: _this.subtitleSliceEndpoint,
2534
2544
  shouldContainSubtitles: _this.use_vtt_subtitles,
2535
- expectedSubtitleTracks: _this._subtitleTracks
2545
+ expectedSubtitleTracks: _this._subtitleTracks,
2546
+ alwaysMapBandwidthByNearest: _this.alwaysMapBandwidthByNearest
2536
2547
  };
2537
2548
  var timestamp = Date.now();
2538
2549
  hlsVod_2 = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(_this._instanceId), hlsOpts);
@@ -2761,9 +2772,9 @@ var Session = /** @class */ (function () {
2761
2772
  }
2762
2773
  };
2763
2774
  Session.prototype._isOldVod = function (refTs, vodDur) {
2764
- var TIME_PADDING_MS = 20 * 1000; // 20secs
2765
2775
  var VOD_DURATION_MS = vodDur * 1000;
2766
- var TIME_SINCE_VOD_STARTED_MS = (Date.now() - refTs) + TIME_PADDING_MS;
2776
+ var TIME_PADDING_MS = VOD_DURATION_MS * 0.05; //5 percent of content duration
2777
+ var TIME_SINCE_VOD_STARTED_MS = Date.now() - refTs + TIME_PADDING_MS;
2767
2778
  if (TIME_SINCE_VOD_STARTED_MS > VOD_DURATION_MS) {
2768
2779
  return true;
2769
2780
  }
@@ -50,7 +50,8 @@ var RefAssetManager = /** @class */ (function () {
50
50
  id: vod.id,
51
51
  title: vod.title,
52
52
  uri: vod.uri,
53
- desiredDuration: 50000
53
+ desiredDuration: 50000,
54
+ startOffset: 10000,
54
55
  };
55
56
  resolve(vodResponse);
56
57
  }
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.2.8",
3
+ "version": "4.2.10",
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.2.0",
33
- "@eyevinn/hls-vodtolive": "^3.0.1",
32
+ "@eyevinn/hls-truncate": "^0.3.0",
33
+ "@eyevinn/hls-vodtolive": "^3.1.1",
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,7 @@ export interface ChannelEngineOpts {
46
46
  subtitleSliceEndpoint?: string;
47
47
  useVTTSubtitles?: boolean;
48
48
  alwaysNewSegments?: boolean;
49
+ alwaysMapBandwidthByNearest?: boolean;
49
50
  diffCompensationRate?: number;
50
51
  staticDirectory?: string;
51
52
  averageSegmentDuration?: number;
@@ -100,6 +101,7 @@ export interface VodResponse {
100
101
  offset?: number;
101
102
  diffMs?: number;
102
103
  desiredDuration?: number;
104
+ startOffset?: number;
103
105
  type?: string;
104
106
  currentMetadata?: VodResponseMetadata;
105
107
  timedMetadata?: VodTimedMetadata;
@@ -181,6 +183,7 @@ export class ChannelEngine {
181
183
  private subtitleSliceEndpoint: string;
182
184
  private useVTTSubtitles: boolean;
183
185
  private alwaysNewSegments: boolean;
186
+ private alwaysMapBandwidthByNearest: boolean;
184
187
  private defaultSlateUri?: string;
185
188
  private slateDuration?: number;
186
189
  private assetMgr: IAssetManager;
@@ -220,6 +223,10 @@ export class ChannelEngine {
220
223
  if (options && options.alwaysNewSegments) {
221
224
  this.alwaysNewSegments = true;
222
225
  }
226
+ this.alwaysMapBandwidthByNearest = false;
227
+ if (options && options.alwaysMapBandwidthByNearest) {
228
+ this.alwaysMapBandwidthByNearest = true;
229
+ }
223
230
  if (options && options.defaultSlateUri) {
224
231
  this.defaultSlateUri = options.defaultSlateUri;
225
232
  this.slateRepetitions = options.slateRepetitions || 10;
@@ -453,6 +460,7 @@ export class ChannelEngine {
453
460
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
454
461
  useVTTSubtitles: this.useVTTSubtitles,
455
462
  alwaysNewSegments: options.alwaysNewSegments,
463
+ alwaysMapBandwidthByNearest: options.alwaysMapBandwidthByNearest,
456
464
  noSessionDataTags: options.noSessionDataTags,
457
465
  playheadDiffThreshold: channel.options && channel.options.playheadDiffThreshold ? channel.options.playheadDiffThreshold : this.streamerOpts.defaultPlayheadDiffThreshold,
458
466
  maxTickInterval: channel.options && channel.options.maxTickInterval ? channel.options.maxTickInterval : this.streamerOpts.defaultMaxTickInterval,
package/engine/session.js CHANGED
@@ -72,11 +72,16 @@ class Session {
72
72
  }
73
73
  this.isAllowedToClearVodCache = null;
74
74
  this.alwaysNewSegments = null;
75
+ this.alwaysMapBandwidthByNearest = null;
75
76
  if (config) {
76
77
  if (config.alwaysNewSegments) {
77
78
  this.alwaysNewSegments = config.alwaysNewSegments;
78
79
  }
79
80
 
81
+ if (config.alwaysMapBandwidthByNearest) {
82
+ this.alwaysMapBandwidthByNearest = config.alwaysMapBandwidthByNearest;
83
+ }
84
+
80
85
  if (config.sessionId) {
81
86
  this._sessionId = config.sessionId;
82
87
  }
@@ -828,7 +833,7 @@ class Session {
828
833
  let positionV = 0;
829
834
  let positionA = 0;
830
835
  const position = (await this._getCurrentPlayheadPosition()) * 1000;
831
- positionV = position / 1000;
836
+ positionV = position ? position / 1000 : 0;
832
837
  let currentVod = await this._sessionState.getCurrentVod();
833
838
  const sessionState = await this._sessionState.getValues(["vodMediaSeqAudio"]);
834
839
  let posDiff;
@@ -840,11 +845,10 @@ class Session {
840
845
  debug(`[${this._sessionId}]: About to determine audio increment`);
841
846
  do {
842
847
  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};(posDiff <= 0.001)=${posDiff <= 0.001}`);
848
+ positionA = audioPosition ? audioPosition / 1000 : 0;
849
+ posDiff = (positionV - positionA).toFixed(3);
850
+ debug(`[${this._sessionId}]: positionV=${positionV};positionA=${positionA};posDiff=${posDiff}`);
846
851
  if (posDiff <= maxAcceptableDiff) {
847
- debug(`[${this._sessionId}]: posDiff value (${posDiff}) is acceptable`);
848
852
  break;
849
853
  }
850
854
  if (posDiff > thresh) {
@@ -858,7 +862,7 @@ class Session {
858
862
  if (sessionState.vodMediaSeqAudio + index > audioSeqLastIdx) {
859
863
  break;
860
864
  }
861
- } while (!(-thresh < posDiff && posDiff < thresh));
865
+ } while (!(-thresh < posDiff && posDiff < thresh) && !isNaN(posDiff));
862
866
  audioIncrement = index;
863
867
  debug(`[${this._sessionId}]: Current VOD Playhead Positions are to be: [${positionV.toFixed(3)}][${positionA.toFixed(3)}] (${posDiff})`);
864
868
  }
@@ -1326,7 +1330,8 @@ class Session {
1326
1330
  dummySubtitleEndpoint: this.dummySubtitleEndpoint,
1327
1331
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
1328
1332
  shouldContainSubtitles: this.use_vtt_subtitles,
1329
- expectedSubtitleTracks: this._subtitleTracks
1333
+ expectedSubtitleTracks: this._subtitleTracks,
1334
+ alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
1330
1335
  };
1331
1336
  newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1332
1337
  if (vodResponse.timedMetadata) {
@@ -1502,7 +1507,8 @@ class Session {
1502
1507
  dummySubtitleEndpoint: this.dummySubtitleEndpoint,
1503
1508
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
1504
1509
  shouldContainSubtitles: this.use_vtt_subtitles,
1505
- expectedSubtitleTracks: this._subtitleTracks
1510
+ expectedSubtitleTracks: this._subtitleTracks,
1511
+ alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
1506
1512
  };
1507
1513
  newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1508
1514
  if (vodResponse.timedMetadata) {
@@ -1553,6 +1559,8 @@ class Session {
1553
1559
  debug(`[${this._sessionId}]: playhead positions [A]=${newVod.getPlayheadPositions("audio")}`);
1554
1560
  currentVod = newVod;
1555
1561
  debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}; audio msequences=${currentVod.getLiveMediaSequencesCount("audio")}; subtitle msequences=${currentVod.getLiveMediaSequencesCount("subtitle")}`);
1562
+ sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
1563
+ await this._playheadState.set("playheadRef", Date.now(), isLeader);
1556
1564
  sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
1557
1565
  sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
1558
1566
  sessionState.vodMediaSeqSubtitle = await this._sessionState.set("vodMediaSeqSubtitle", 0);
@@ -1564,9 +1572,7 @@ class Session {
1564
1572
  sessionState.discSeqSubtitle = await this._sessionState.set("discSeqSubtitle", sessionState.discSeqSubtitle + lastDiscontinuitySubtitle);
1565
1573
  debug(`[${this._sessionId}]: new sequence data set in store V[${sessionState.mediaSeq}][${sessionState.discSeq}]_A[${sessionState.mediaSeqAudio}][${sessionState.discSeqAudio}]_S[${sessionState.mediaSeqSubtitle}][${sessionState.discSeqSubtitle}]`);
1566
1574
  await this._sessionState.remove("nextVod");
1567
- sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
1568
1575
  this.leaderIsSettingNextVod = false;
1569
- await this._playheadState.set("playheadRef", Date.now(), isLeader);
1570
1576
  await this._playheadState.set("diffCompensation", this.diffCompensation, isLeader);
1571
1577
  debug(`[${this._sessionId}]: sharing durrent vods diffCompensation=${this.diffCompensation}`);
1572
1578
  this.produceEvent({
@@ -1749,7 +1755,8 @@ 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
1753
1760
  };
1754
1761
  const timestamp = Date.now();
1755
1762
  hlsVod = new HLSVod(this.slateUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
@@ -1799,7 +1806,11 @@ class Session {
1799
1806
  _truncateVod(vodResponse) {
1800
1807
  return new Promise((resolve, reject) => {
1801
1808
  try {
1802
- const truncatedVod = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000);
1809
+ const options = {};
1810
+ if (vodResponse.startOffset) {
1811
+ options.offset = vodResponse.startOffset / 1000;
1812
+ }
1813
+ const truncatedVod = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000, options);
1803
1814
  truncatedVod.load()
1804
1815
  .then(() => {
1805
1816
  let audioManifestLoader;
@@ -1848,7 +1859,8 @@ class Session {
1848
1859
  dummySubtitleEndpoint: this.dummySubtitleEndpoint,
1849
1860
  subtitleSliceEndpoint: this.subtitleSliceEndpoint,
1850
1861
  shouldContainSubtitles: this.use_vtt_subtitles,
1851
- expectedSubtitleTracks: this._subtitleTracks
1862
+ expectedSubtitleTracks: this._subtitleTracks,
1863
+ alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
1852
1864
  };
1853
1865
  const timestamp = Date.now();
1854
1866
  hlsVod = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
@@ -2037,9 +2049,9 @@ class Session {
2037
2049
  }
2038
2050
 
2039
2051
  _isOldVod(refTs, vodDur) {
2040
- const TIME_PADDING_MS = 20 * 1000 // 20secs
2041
2052
  const VOD_DURATION_MS = vodDur * 1000;
2042
- const TIME_SINCE_VOD_STARTED_MS = (Date.now() - refTs) + TIME_PADDING_MS;
2053
+ const TIME_PADDING_MS = VOD_DURATION_MS * 0.05; //5 percent of content duration
2054
+ const TIME_SINCE_VOD_STARTED_MS = Date.now() - refTs + TIME_PADDING_MS;
2043
2055
  if (TIME_SINCE_VOD_STARTED_MS > VOD_DURATION_MS) {
2044
2056
  return true;
2045
2057
  }
@@ -57,7 +57,8 @@ class RefAssetManager implements IAssetManager {
57
57
  id: vod.id,
58
58
  title: vod.title,
59
59
  uri: vod.uri,
60
- desiredDuration: 50000
60
+ desiredDuration: 50000,
61
+ startOffset: 10000,
61
62
  };
62
63
  resolve(vodResponse);
63
64
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.2.8",
3
+ "version": "4.2.10",
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.2.0",
33
- "@eyevinn/hls-vodtolive": "^3.0.1",
32
+ "@eyevinn/hls-truncate": "^0.3.0",
33
+ "@eyevinn/hls-vodtolive": "^3.1.1",
34
34
  "@eyevinn/m3u8": "^0.5.3",
35
35
  "abort-controller": "^3.0.0",
36
36
  "debug": "^3.2.7",