eyevinn-channel-engine 3.2.6 → 3.3.1

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
@@ -108,6 +108,7 @@ Available options when constructing the Channel Engine object are:
108
108
  - `maxTickInterval`: The maximum interval for playhead tick interval. Default is 10000 ms.
109
109
  - `cloudWatchMetrics`: Output CloudWatch JSON metrics on console log. Default is false.
110
110
  - `useDemuxedAudio`: Enable playing VODs with multiple audio tracks. Default is false.
111
+ - `alwaysNewSegments`: Force all new HLS media sequences to always contain at least 1 new segment. Default is false.
111
112
 
112
113
  ## High Availability
113
114
 
package/engine/server.js CHANGED
@@ -33,9 +33,13 @@ class ChannelEngine {
33
33
  this.adXchangeUri = options.adXchangeUri;
34
34
  }
35
35
  this.useDemuxedAudio = false;
36
- if (options && options.useDemuxedAudio === true) {
36
+ if (options && options.useDemuxedAudio) {
37
37
  this.useDemuxedAudio = true;
38
38
  }
39
+ this.alwaysNewSegments = false;
40
+ if (options && options.alwaysNewSegments) {
41
+ this.alwaysNewSegments = true;
42
+ }
39
43
  if (options && options.defaultSlateUri) {
40
44
  this.defaultSlateUri = options.defaultSlateUri;
41
45
  this.slateRepetitions = options.slateRepetitions || 10;
@@ -228,6 +232,7 @@ class ChannelEngine {
228
232
  sessionId: channel.id,
229
233
  averageSegmentDuration: channel.options && channel.options.averageSegmentDuration ? channel.options.averageSegmentDuration : this.streamerOpts.defaultAverageSegmentDuration,
230
234
  useDemuxedAudio: options.useDemuxedAudio,
235
+ alwaysNewSegments: options.alwaysNewSegments,
231
236
  playheadDiffThreshold: channel.options && channel.options.playheadDiffThreshold ? channel.options.playheadDiffThreshold : this.streamerOpts.defaultPlayheadDiffThreshold,
232
237
  maxTickInterval: channel.options && channel.options.maxTickInterval ? channel.options.maxTickInterval : this.streamerOpts.defaultMaxTickInterval,
233
238
  targetDurationPadding: channel.options && channel.options.targetDurationPadding ? channel.options.targetDurationPadding : this.streamerOpts.targetDurationPadding,
@@ -444,6 +449,7 @@ class ChannelEngine {
444
449
  options.adXchangeUri = this.adXchangeUri;
445
450
  options.averageSegmentDuration = this.streamerOpts.defaultAverageSegmentDuration;
446
451
  options.useDemuxedAudio = this.useDemuxedAudio;
452
+ options.alwaysNewSegments = this.alwaysNewSegments;
447
453
  options.playheadDiffThreshold = this.streamerOpts.defaultPlayheadDiffThreshold;
448
454
  options.maxTickInterval = this.streamerOpts.defaultMaxTickInterval;
449
455
  options.targetDurationPadding = this.streamerOpts.targetDurationPadding;
package/engine/session.js CHANGED
@@ -47,6 +47,10 @@ class Session {
47
47
  video: null,
48
48
  audio: null
49
49
  }
50
+ this.prevMediaSeqOffset = {
51
+ video: null,
52
+ audio: null
53
+ }
50
54
  this.waitingForNextVod = false;
51
55
  this.leaderIsSettingNextVod = false;
52
56
  this.isSwitchingBackToV2L = false;
@@ -58,8 +62,12 @@ class Session {
58
62
  reloadBehind: null,
59
63
  }
60
64
  this.isAllowedToClearVodCache = null;
61
-
65
+ this.alwaysNewSegments = null;
62
66
  if (config) {
67
+ if (config.alwaysNewSegments) {
68
+ this.alwaysNewSegments = config.alwaysNewSegments;
69
+ }
70
+
63
71
  if (config.sessionId) {
64
72
  this._sessionId = config.sessionId;
65
73
  }
@@ -195,7 +203,7 @@ class Session {
195
203
  this.diffCompensation -= DIFF_COMPENSATION;
196
204
  }
197
205
  debug(`[${this._sessionId}]: Requested tickInterval=${tickInterval}s (max=${this.maxTickInterval / 1000}s, diffThreshold=${this.playheadDiffThreshold}msec)`);
198
- if (tickInterval <= 0) {
206
+ if (tickInterval <= 0.5) {
199
207
  tickInterval = 0.5;
200
208
  } else if (tickInterval > (this.maxTickInterval / 1000)) {
201
209
  tickInterval = this.maxTickInterval / 1000;
@@ -361,9 +369,13 @@ class Session {
361
369
  state = await this.getSessionState();
362
370
  }
363
371
 
364
- const playheadState = await this._playheadState.getValues(["vodMediaSeqVideo"]);
365
- if (opts && opts.targetMseq) {
372
+ const playheadState = {
373
+ vodMediaSeqVideo: null
374
+ }
375
+ if (opts && opts.targetMseq !== undefined) {
366
376
  playheadState.vodMediaSeqVideo = opts.targetMseq;
377
+ } else {
378
+ playheadState.vodMediaSeqVideo = await this._playheadState.get("vodMediaSeqVideo");
367
379
  }
368
380
 
369
381
  // NOTE: Assume that VOD cache was already cleared in 'getCurrentMediaAndDiscSequenceCount()'
@@ -372,7 +384,16 @@ class Session {
372
384
  if (currentVod) {
373
385
  try {
374
386
  const mediaSegments = currentVod.getLiveMediaSequenceSegments(playheadState.vodMediaSeqVideo);
375
- debug(`[${this._sessionId}]: Requesting all segments from Media Sequence: ${playheadState.vodMediaSeqVideo}_${currentVod.getLiveMediaSequencesCount()}`);
387
+
388
+ let mediaSequenceValue = 0;
389
+ if (currentVod.sequenceAlwaysContainNewSegments) {
390
+ mediaSequenceValue = currentVod.mediaSequenceValues[playheadState.vodMediaSeqVideo];
391
+ debug(`[${this._sessionId}]: {${mediaSequenceValue}}_{${currentVod.getLastSequenceMediaSequenceValue()}}`);
392
+ } else {
393
+ mediaSequenceValue = playheadState.vodMediaSeqVideo;
394
+ }
395
+
396
+ debug(`[${this._sessionId}]: Requesting all segments from Media Sequence: ${playheadState.vodMediaSeqVideo}(${mediaSequenceValue})_${currentVod.getLiveMediaSequencesCount()}`);
376
397
  return mediaSegments;
377
398
  } catch (err) {
378
399
  logerror(this._sessionId, err);
@@ -424,10 +445,18 @@ class Session {
424
445
  const currentVod = await this._sessionState.getCurrentVod();
425
446
  if (currentVod) {
426
447
  try {
448
+ let mediaSequenceValue = 0;
449
+ if (currentVod.sequenceAlwaysContainNewSegments) {
450
+ mediaSequenceValue = currentVod.mediaSequenceValues[playheadState.vodMediaSeqVideo];
451
+ debug(`[${this._sessionId}]: seqIndex=${playheadState.vodMediaSeqVideo}_seqValue=${mediaSequenceValue}`)
452
+ } else {
453
+ mediaSequenceValue = playheadState.vodMediaSeqVideo;
454
+ }
427
455
  const discSeqCount = discSeqOffset + currentVod.discontinuities[playheadState.vodMediaSeqVideo];
428
- debug(`[${this._sessionId}]: MediaSeq: (${(playheadState.mediaSeq + playheadState.vodMediaSeqVideo)}) and DiscSeq: (${discSeqCount}) requested `);
456
+
457
+ debug(`[${this._sessionId}]: MediaSeq: (${playheadState.mediaSeq}+${mediaSequenceValue}=${(playheadState.mediaSeq + mediaSequenceValue)}) and DiscSeq: (${discSeqCount}) requested `);
429
458
  return {
430
- 'mediaSeq': (playheadState.mediaSeq + playheadState.vodMediaSeqVideo),
459
+ 'mediaSeq': (playheadState.mediaSeq + mediaSequenceValue),
431
460
  'discSeq': discSeqCount,
432
461
  'vodMediaSeqVideo': playheadState.vodMediaSeqVideo,
433
462
  };
@@ -460,6 +489,9 @@ class Session {
460
489
  if (this.prevVodMediaSeq.video === null) {
461
490
  this.prevVodMediaSeq.video = playheadState.vodMediaSeqVideo;
462
491
  }
492
+ if (this.prevMediaSeqOffset.video === null) {
493
+ this.prevMediaSeqOffset.video = playheadState.mediaSeq;
494
+ }
463
495
 
464
496
  if (playheadState.vodMediaSeqVideo > sessionState.vodMediaSeqVideo) {
465
497
  debug(`[${this._sessionId}]: Recently Loaded Next Vod. PlayheadState not up-to-date. Return the last generated m3u8`);
@@ -473,8 +505,13 @@ class Session {
473
505
  }
474
506
 
475
507
  // Force reading up from store, but only once if the condition is right
476
- if (playheadState.vodMediaSeqVideo < 2 || playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video) {
508
+ if (playheadState.vodMediaSeqVideo < 2 || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
477
509
  debug(`[${this._sessionId}]: current[${playheadState.vodMediaSeqVideo}]_prev[${this.prevVodMediaSeq.video}]`);
510
+ debug(`[${this._sessionId}]: current-offset[${playheadState.mediaSeq}]_prev-offset[${this.prevMediaSeqOffset.video}]`);
511
+ // If true, then we have not updated the prev-values and not cleared the cache yet.
512
+ if (playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
513
+ this.isAllowedToClearVodCache = true;
514
+ }
478
515
  const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
479
516
  if (!isLeader && this.isAllowedToClearVodCache) {
480
517
  debug(`[${this._sessionId}]: Not a leader and first|second media sequence in a VOD is requested. Invalidate cache to ensure having the correct VOD.`);
@@ -488,10 +525,17 @@ class Session {
488
525
  const currentVod = await this._sessionState.getCurrentVod();
489
526
  if (currentVod) {
490
527
  try {
528
+
529
+ if (currentVod.sequenceAlwaysContainNewSegments) {
530
+ const mediaSequenceValue = currentVod.mediaSequenceValues[playheadState.vodMediaSeqVideo];
531
+ debug(`[${this._sessionId}]: {${mediaSequenceValue}}_{${currentVod.getLastSequenceMediaSequenceValue()}}`);
532
+ }
533
+
491
534
  debug(`[${this._sessionId}]: [${playheadState.vodMediaSeqVideo}]_[${currentVod.getLiveMediaSequencesCount()}]`);
492
535
  const m3u8 = currentVod.getLiveMediaSequences(playheadState.mediaSeq, bw, playheadState.vodMediaSeqVideo, sessionState.discSeq, this.targetDurationPadding, this.forceTargetDuration);
493
536
  debug(`[${this._sessionId}]: [${playheadState.mediaSeq + playheadState.vodMediaSeqVideo}][${sessionState.discSeq}][+${this.targetDurationPadding || 0}] Current media manifest for ${bw} requested`);
494
537
  this.prevVodMediaSeq.video = playheadState.vodMediaSeqVideo;
538
+ this.prevMediaSeqOffset.video = playheadState.mediaSeq;
495
539
  return m3u8;
496
540
  } catch (err) {
497
541
  logerror(this._sessionId, err);
@@ -566,6 +610,7 @@ class Session {
566
610
  } else {
567
611
  debug(`[${this._sessionId}]: We don't have any previously generated m3u8`);
568
612
  }
613
+ this.isAllowedToClearVodCache = true;
569
614
  }
570
615
  } else {
571
616
  sessionState.vodMediaSeqVideo = await this._sessionState.increment("vodMediaSeqVideo");
@@ -586,19 +631,25 @@ class Session {
586
631
  playheadState.mediaSeq = await this._playheadState.set("mediaSeq", sessionState.mediaSeq);
587
632
  playheadState.vodMediaSeqVideo = await this._playheadState.set("vodMediaSeqVideo", sessionState.vodMediaSeqVideo);
588
633
  playheadState.vodMediaSeqAudio = await this._playheadState.set("vodMediaSeqAudio", sessionState.vodMediaSeqAudio);
634
+
635
+ if (currentVod.sequenceAlwaysContainNewSegments) {
636
+ const mediaSequenceValue = currentVod.mediaSequenceValues[playheadState.vodMediaSeqVideo];
637
+ debug(`[${this._sessionId}]: Session can now serve mseq={${playheadState.mediaSeq + mediaSequenceValue}}`);
638
+ }
639
+
589
640
  debug(`[${this._sessionId}]: INCREMENT (mseq=${playheadState.mediaSeq + playheadState.vodMediaSeqVideo}) vodMediaSeq=(${playheadState.vodMediaSeqVideo}_${playheadState.vodMediaSeqAudio} of ${currentVod.getLiveMediaSequencesCount()})`);
590
- let m3u8 = currentVod.getLiveMediaSequences(playheadState.mediaSeq, 180000, playheadState.vodMediaSeqVideo, sessionState.discSeq);
591
- await this._playheadState.setLastM3u8(m3u8);
592
641
 
593
642
  // As a FOLLOWER, we might need to read up from shared store...
594
- if (playheadState.vodMediaSeqVideo < 2 || playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video) {
643
+ if (playheadState.vodMediaSeqVideo < 2 || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
595
644
  debug(`[${this._sessionId}]: current[${playheadState.vodMediaSeqVideo}]_prev[${this.prevVodMediaSeq.video}]`);
596
- if (playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video) {
597
- this.alreadyCleredVodCache = false;
645
+ debug(`[${this._sessionId}]: current-offset[${playheadState.mediaSeq}]_prev-offset[${this.prevMediaSeqOffset.video}]`);
646
+ if (playheadState.vodMediaSeqVideo < this.prevVodMediaSeq.video || playheadState.mediaSeq !== this.prevMediaSeqOffset.video) {
647
+ this.isAllowedToClearVodCache = true;
598
648
  }
599
649
  if (!isLeader && this.isAllowedToClearVodCache) {
600
650
  debug(`[${this._sessionId}]: Not a leader and have just set 'playheadState.vodMediaSeqVideo' to 0|1. Invalidate cache to ensure having the correct VOD.`);
601
651
  await this._sessionState.clearCurrentVodCache();
652
+ currentVod = await this._sessionState.getCurrentVod();
602
653
  this.isAllowedToClearVodCache = false;
603
654
  }
604
655
  } else {
@@ -606,7 +657,10 @@ class Session {
606
657
  }
607
658
  // Update Value for Previous Sequence
608
659
  this.prevVodMediaSeq.video = playheadState.vodMediaSeqVideo;
660
+ this.prevMediaSeqOffset.video = playheadState.mediaSeq;
609
661
 
662
+ let m3u8 = currentVod.getLiveMediaSequences(playheadState.mediaSeq, 180000, playheadState.vodMediaSeqVideo, sessionState.discSeq);
663
+ await this._playheadState.setLastM3u8(m3u8);
610
664
  return m3u8;
611
665
  }
612
666
 
@@ -823,17 +877,21 @@ class Session {
823
877
  const slateVod = await this._loadSlate(currentVod);
824
878
  debug(`[${this._sessionId}]: slate loaded`);
825
879
  const sessionState = await this._sessionState.getValues(["slateCount", "mediaSeq", "discSeq"]);
826
- let length = 0;
880
+ let endValue = 0;
827
881
  let lastDiscontinuity = 0;
828
882
  if (currentVod) {
829
- length = currentVod.getLiveMediaSequencesCount();
883
+ if (currentVod.sequenceAlwaysContainNewSegments) {
884
+ endValue = currentVod.getLastSequenceMediaSequenceValue();
885
+ } else {
886
+ endValue = currentVod.getLiveMediaSequencesCount();
887
+ }
830
888
  lastDiscontinuity = currentVod.getLastDiscontinuity();
831
889
  }
832
890
  await this._sessionState.set("vodMediaSeqVideo", 0);
833
891
  await this._sessionState.set("vodMediaSeqAudio", 0);
834
892
  await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
835
893
  await this._sessionState.setCurrentVod(slateVod);
836
- await this._sessionState.set("mediaSeq", sessionState.mediaSeq + length);
894
+ await this._sessionState.set("mediaSeq", sessionState.mediaSeq + endValue);
837
895
  await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
838
896
  await this._sessionState.set("slateCount", sessionState.slateCount + 1);
839
897
  await this._playheadState.set("playheadRef", Date.now());
@@ -886,7 +944,8 @@ class Session {
886
944
  let loadPromise;
887
945
  if (!vodResponse.type) {
888
946
  debug(`[${this._sessionId}]: got first VOD uri=${vodResponse.uri}:${vodResponse.offset || 0}`);
889
- newVod = new HLSVod(vodResponse.uri, [], null, vodResponse.offset * 1000, m3u8Header(this._instanceId));
947
+ const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
948
+ newVod = new HLSVod(vodResponse.uri, [], null, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
890
949
  if (vodResponse.timedMetadata) {
891
950
  Object.keys(vodResponse.timedMetadata).map(k => {
892
951
  newVod.addMetadata(k, vodResponse.timedMetadata[k]);
@@ -956,6 +1015,9 @@ class Session {
956
1015
  if (this.prevVodMediaSeq.video === null) {
957
1016
  this.prevVodMediaSeq.video = sessionState.vodMediaSeqVideo;
958
1017
  }
1018
+ if (this.prevMediaSeqOffset.video === null) {
1019
+ this.prevMediaSeqOffset.video = sessionState.mediaSeq;
1020
+ }
959
1021
  // Clear Cache if prev count is HIGHER than current...
960
1022
  if (sessionState.vodMediaSeqVideo < this.prevVodMediaSeq.video) {
961
1023
  debug(`[${this._sessionId}]: state=VOD_PLAYING, current[${sessionState.vodMediaSeqVideo}], prev[${this.prevVodMediaSeq.video}], total[${currentVod.getLiveMediaSequencesCount()}]`);
@@ -965,8 +1027,9 @@ class Session {
965
1027
  }
966
1028
  } else {
967
1029
  // Handle edge case where Leader loaded next vod but Follower remained in state=VOD_PLAYING
968
- if (this.prevVodMediaSeq.video !== null & sessionState.vodMediaSeqVideo < this.prevVodMediaSeq.video) {
1030
+ if (this.prevMediaSeqOffset.video !== null & sessionState.mediaSeq !== this.prevMediaSeqOffset.video) {
969
1031
  debug(`[${this._sessionId}]: state=VOD_PLAYING, current[${sessionState.vodMediaSeqVideo}], prev[${this.prevVodMediaSeq.video}], total[${currentVod.getLiveMediaSequencesCount()}]`);
1032
+ debug(`[${this._sessionId}]: mediaSeq offsets -> current[${sessionState.vodMediaSeqVideo}], prev[${this.prevVodMediaSeq.video}]`);
970
1033
  // Allow Follower to clear VodCache...
971
1034
  this.isAllowedToClearVodCache = true;
972
1035
  }
@@ -991,6 +1054,12 @@ class Session {
991
1054
  return;
992
1055
  }
993
1056
  const length = currentVod.getLiveMediaSequencesCount();
1057
+ let endMseqValue;
1058
+ if (currentVod.sequenceAlwaysContainNewSegments) {
1059
+ endMseqValue = currentVod.getLastSequenceMediaSequenceValue();
1060
+ } else {
1061
+ endMseqValue = currentVod.getLiveMediaSequencesCount();
1062
+ }
994
1063
  const lastDiscontinuity = currentVod.getLastDiscontinuity();
995
1064
  sessionState.state = await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
996
1065
  let vodPromise = this._getNextVod();
@@ -1008,7 +1077,8 @@ class Session {
1008
1077
  let loadPromise;
1009
1078
  if (!vodResponse.type) {
1010
1079
  debug(`[${this._sessionId}]: got next VOD uri=${vodResponse.uri}:${vodResponse.offset}`);
1011
- newVod = new HLSVod(vodResponse.uri, null, null, vodResponse.offset * 1000, m3u8Header(this._instanceId));
1080
+ const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
1081
+ newVod = new HLSVod(vodResponse.uri, null, null, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
1012
1082
  if (vodResponse.timedMetadata) {
1013
1083
  Object.keys(vodResponse.timedMetadata).map(k => {
1014
1084
  newVod.addMetadata(k, vodResponse.timedMetadata[k]);
@@ -1046,7 +1116,7 @@ class Session {
1046
1116
  debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}`);
1047
1117
  sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
1048
1118
  sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
1049
- sessionState.mediaSeq = await this._sessionState.set("mediaSeq", sessionState.mediaSeq + length);
1119
+ sessionState.mediaSeq = await this._sessionState.set("mediaSeq", sessionState.mediaSeq + endMseqValue);
1050
1120
  sessionState.discSeq = await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
1051
1121
  debug(`[${this._sessionId}]: new sequence data set in store [${sessionState.mediaSeq}][${sessionState.discSeq}]`);
1052
1122
  await this._sessionState.remove("nextVod");
@@ -1065,6 +1135,8 @@ class Session {
1065
1135
  debug(`[${this._sessionId}]: not a leader so will go directly to state VOD_NEXT_INITIATING`);
1066
1136
  this.waitingForNextVod = true;
1067
1137
  sessionState.state = await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
1138
+ // Allow Leader|Follower to clear vodCache...
1139
+ this.isAllowedToClearVodCache = true;
1068
1140
  sessionState.currentVod = await this._sessionState.getCurrentVod();
1069
1141
  }
1070
1142
  } catch (err) {
@@ -1081,6 +1153,8 @@ class Session {
1081
1153
  debug("No slate to load");
1082
1154
  throw err;
1083
1155
  }
1156
+ // Allow Leader|Follower to clear vodCache...
1157
+ this.isAllowedToClearVodCache = true;
1084
1158
  }
1085
1159
  break;
1086
1160
  case SessionState.VOD_RELOAD_INIT:
@@ -1094,7 +1168,12 @@ class Session {
1094
1168
  sessionState.state = await this._sessionState.set("state", SessionState.VOD_RELOAD_INITIATING);
1095
1169
 
1096
1170
  // 2) Set new 'offset' sequences, to carry on the continuity from session-live
1097
- const mSeq = this.switchDataForSession.mediaSeq;
1171
+ let mSeq = this.switchDataForSession.mediaSeq;
1172
+ let currentVod = await this._sessionState.getCurrentVod();
1173
+ if (currentVod.sequenceAlwaysContainNewSegments) {
1174
+ // (!) will need to compensate if using this setting on HLSVod Object.
1175
+ mSeq -= 1;
1176
+ }
1098
1177
  const dSeq = this.switchDataForSession.discSeq;
1099
1178
  const mSeqOffset = this.switchDataForSession.mediaSeqOffset;
1100
1179
  const reloadBehind = this.switchDataForSession.reloadBehind;
@@ -1113,15 +1192,14 @@ class Session {
1113
1192
  // 3) Set new media segments/currentVod, to carry on the continuity from session-live
1114
1193
  debug(`[${this._sessionId}]: LEADER: making changes to current VOD. I will also update currentVod in store.`);
1115
1194
  const playheadState = await this._playheadState.getValues(["vodMediaSeqVideo"]);
1116
- let currentVod = await this._sessionState.getCurrentVod();
1117
- let currentMseq = playheadState.vodMediaSeqVideo + mSeqOffset;
1118
- if (currentMseq > currentVod.getLiveMediaSequencesCount() - 1) {
1119
- currentMseq = currentVod.getLiveMediaSequencesCount() - 1;
1195
+ let nextMseq = playheadState.vodMediaSeqVideo + 1;
1196
+ if (nextMseq > currentVod.getLiveMediaSequencesCount() - 1) {
1197
+ nextMseq = currentVod.getLiveMediaSequencesCount() - 1;
1120
1198
  }
1121
1199
 
1122
1200
  // TODO: Support reloading with audioSegments as well
1123
1201
 
1124
- await currentVod.reload(currentMseq, segments, null, reloadBehind);
1202
+ await currentVod.reload(nextMseq, segments, null, reloadBehind);
1125
1203
  await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
1126
1204
  await this._sessionState.set("vodReloaded", 1);
1127
1205
  await this._sessionState.set("vodMediaSeqVideo", 0);
@@ -1201,7 +1279,8 @@ class Session {
1201
1279
 
1202
1280
  slateVod.load()
1203
1281
  .then(() => {
1204
- hlsVod = new HLSVod(this.slateUri, null, null, null, m3u8Header(this._instanceId));
1282
+ const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
1283
+ hlsVod = new HLSVod(this.slateUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
1205
1284
  const timestamp = Date.now();
1206
1285
  hlsVod.addMetadata('id', `slate-${timestamp}`);
1207
1286
  hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
@@ -1245,7 +1324,8 @@ class Session {
1245
1324
 
1246
1325
  slateVod.load()
1247
1326
  .then(() => {
1248
- hlsVod = new HLSVod(nexVodUri, null, null, null, m3u8Header(this._instanceId));
1327
+ const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments };
1328
+ hlsVod = new HLSVod(nexVodUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
1249
1329
  const timestamp = Date.now();
1250
1330
  hlsVod.addMetadata('id', `slate-${timestamp}`);
1251
1331
  hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
@@ -542,7 +542,7 @@ class SessionLive {
542
542
  this.blockGenerateManifest = false;
543
543
  }
544
544
 
545
- let attempts = 5;
545
+ let attempts = 10;
546
546
  // CHECK AGAIN CASE 1: Store Empty
547
547
  while (!leadersMediaSeqRaw && attempts > 0) {
548
548
  const segDur = this._getAnyFirstSegmentDurationMs() || DEFAULT_PLAYHEAD_INTERVAL_MS;
@@ -565,7 +565,7 @@ class SessionLive {
565
565
  }
566
566
  }
567
567
 
568
- attempts = 5;
568
+ attempts = 10;
569
569
  // CHECK AGAIN CASE 2: Store Old
570
570
  while (leadersMediaSeqRaw <= this.lastRequestedMediaSeqRaw && attempts > 0) {
571
571
  const segDur = this._getAnyFirstSegmentDurationMs() || DEFAULT_PLAYHEAD_INTERVAL_MS;
@@ -654,8 +654,8 @@ class SessionLive {
654
654
  debug(`[${this.sessionId}]: Live Mseq counts=[${allMediaSeqCounts}]`);
655
655
  // Decrement fetch counter
656
656
  FETCH_ATTEMPTS--;
657
- // Calculate retry delay time. Default=1500
658
- let retryDelayMs = 1500;
657
+ // Calculate retry delay time. Default=1000
658
+ let retryDelayMs = 1000;
659
659
  if (Object.keys(this.liveSegQueue).length > 0) {
660
660
  const firstBw = Object.keys(this.liveSegQueue)[0];
661
661
  const lastIdx = this.liveSegQueue[firstBw].length - 1;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "3.2.6",
3
+ "version": "3.3.1",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,7 +22,7 @@
22
22
  "dependencies": {
23
23
  "@eyevinn/hls-repeat": "^0.1.1",
24
24
  "@eyevinn/hls-truncate": "^0.1.0",
25
- "@eyevinn/hls-vodtolive": "^1.17.1",
25
+ "@eyevinn/hls-vodtolive": "^2.0.1",
26
26
  "@eyevinn/m3u8": "^0.4.1",
27
27
  "abort-controller": "^3.0.0",
28
28
  "debug": "^3.2.7",
package/server.js CHANGED
@@ -100,4 +100,4 @@ const engineOptions = {
100
100
 
101
101
  const engine = new ChannelEngine(refAssetManager, engineOptions);
102
102
  engine.start();
103
- engine.listen(process.env.port || 8000);
103
+ engine.listen(process.env.PORT || 8000);
@@ -413,12 +413,14 @@ describe("The initialize switching", () => {
413
413
  uri: "http://mock.mock.com/180000/seg12.ts",
414
414
  });
415
415
  expect(sessionCurrentSegs["1313000"][size - 1 - 2]).toEqual({
416
- discontinuity: true,
417
- cue: { in: true },
416
+ duration: 7.5,
417
+ timelinePosition: null,
418
+ uri: 'https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/600/600-00008.ts',
419
+ cue: null
418
420
  });
419
421
  expect(sessionCurrentSegs["1313000"][size - 1]).toEqual({
420
422
  duration: 7.5,
421
- uri: "https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/600/600-00016.ts",
423
+ uri: "https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/600/600-00010.ts",
422
424
  timelinePosition: null,
423
425
  cue: null,
424
426
  });
@@ -469,7 +471,7 @@ describe("The initialize switching", () => {
469
471
  const size = sessionCurrentSegs["1313000"].length;
470
472
  expect(sessionCurrentSegs["1313000"][0]).toEqual({
471
473
  duration: 7.5,
472
- uri: "https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/600/600-00011.ts",
474
+ uri: "https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/600/600-00012.ts",
473
475
  timelinePosition: null,
474
476
  cue: null,
475
477
  });
@@ -745,41 +747,41 @@ describe("The initialize switching", () => {
745
747
  const borderSegment = newVodSegments["1313000"][6];
746
748
 
747
749
  const expectedLastVODSegItem = {
748
- duration: 7.2,
749
- uri: "https://maitv-vod.lab.eyevinn.technology/MORBIUS_Trailer_2020.mp4/1000/1000-00008.ts",
750
- timelinePosition: null,
751
- cue: null,
750
+ discontinuity: true,
751
+ daterange: null
752
752
  }
753
753
  const expectedFirstV2LSegItem = {
754
- duration: 10.846444,
755
- uri: "https://maitv-vod.lab.eyevinn.technology/VINN.mp4/600/600-00000.ts",
754
+ duration: 7.2,
756
755
  timelinePosition: null,
757
756
  cue: null,
757
+ uri: 'https://maitv-vod.lab.eyevinn.technology/VINN.mp4/600/600-00001.ts'
758
758
  }
759
759
  const expectedSecondVODSegItem = {
760
760
  duration: 7.5075,
761
- uri: "https://maitv-vod.lab.eyevinn.technology/BECKY_Trailer_2020.mp4/600/600-00001.ts",
761
+ uri: "https://maitv-vod.lab.eyevinn.technology/BECKY_Trailer_2020.mp4/1000/1000-00001.ts",
762
762
  timelinePosition: null,
763
763
  cue: null,
764
764
  }
765
765
 
766
766
  expect(newVodSegments["1313000"][0]).toEqual(expectedLastVODSegItem);
767
767
  expect(newVodSegments["1313000"][1]).toEqual({
768
- discontinuity: true,
769
- daterange: null,
768
+ duration: 10.846444,
769
+ timelinePosition: null,
770
+ cue: null,
771
+ uri: 'https://maitv-vod.lab.eyevinn.technology/VINN.mp4/600/600-00000.ts'
770
772
  });
771
773
  expect(newVodSegments["1313000"][2]).toEqual(expectedFirstV2LSegItem);
772
- expect(newVodSegments["1313000"][5]).toEqual({
773
- discontinuity: true,
774
- cue: { in: true },
775
- });
774
+ // expect(newVodSegments["1313000"][5]).toEqual({
775
+ // discontinuity: true,
776
+ // cue: { in: true },
777
+ // });
776
778
  expect(borderSegment.daterange["planned-duration"]).toEqual(vodEventDurationMs2 / 1000);
777
779
  expect("start-date" in borderSegment.daterange).toBe(true);
778
780
  expect("id" in borderSegment.daterange).toBe(true);
779
- expect(borderSegment.duration).toEqual(11.3077);
781
+ expect(borderSegment.duration).toEqual(11.3447);
780
782
  expect(borderSegment.timelinePosition).toEqual(null);
781
783
  expect(borderSegment.cue).toEqual(null);
782
- expect(borderSegment.uri).toEqual("https://maitv-vod.lab.eyevinn.technology/BECKY_Trailer_2020.mp4/600/600-00000.ts");
784
+ expect(borderSegment.uri).toEqual("https://maitv-vod.lab.eyevinn.technology/BECKY_Trailer_2020.mp4/1000/1000-00000.ts");
783
785
  expect(newVodSegments["1313000"][7]).toEqual(expectedSecondVODSegItem);
784
786
  });
785
787