eyevinn-channel-engine 3.0.7 → 3.1.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/.github/workflows/publish.yml +1 -1
- package/engine/session.js +23 -9
- package/engine/session_live.js +187 -136
- package/package.json +1 -1
- package/spec/engine/playhead_live_spec.js +30 -24
package/engine/session.js
CHANGED
|
@@ -276,7 +276,8 @@ class Session {
|
|
|
276
276
|
debug(`[${this._sessionId}]: FOLLOWER: WARNING! Attempts=0 - Risk of using wrong currentVod`);
|
|
277
277
|
}
|
|
278
278
|
if (!isLeader || vodReloaded) {
|
|
279
|
-
debug(`[${this._sessionId}]: FOLLOWER: leader is alive, and has presumably updated currentVod`);
|
|
279
|
+
debug(`[${this._sessionId}]: FOLLOWER: leader is alive, and has presumably updated currentVod. Clearing the cache now`);
|
|
280
|
+
await this._sessionState.clearCurrentVodCache();
|
|
280
281
|
return;
|
|
281
282
|
}
|
|
282
283
|
}
|
|
@@ -394,9 +395,20 @@ class Session {
|
|
|
394
395
|
if (!this._sessionState) {
|
|
395
396
|
throw new Error('Session not ready');
|
|
396
397
|
}
|
|
397
|
-
const sessionState = await this._sessionState.getValues(["discSeq"]);
|
|
398
|
+
const sessionState = await this._sessionState.getValues(["discSeq", "vodMediaSeqVideo"]);
|
|
398
399
|
const playheadState = await this._playheadState.getValues(["mediaSeq", "vodMediaSeqVideo"]);
|
|
399
|
-
|
|
400
|
+
|
|
401
|
+
/*
|
|
402
|
+
Since sessionState is always newer. If it is a smaller value, then this signals that
|
|
403
|
+
a new vod has been loaded but playheadState has yet to get the new mseq value in 'incrementAsync()'.
|
|
404
|
+
Without this, we risk returning an m3u8 with an old mseq on a new vod to the client.
|
|
405
|
+
*/
|
|
406
|
+
if (playheadState.vodMediaSeqVideo > sessionState.vodMediaSeqVideo) {
|
|
407
|
+
playheadState.vodMediaSeqVideo = sessionState.vodMediaSeqVideo;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (playheadState.vodMediaSeqVideo === 0 || this.waitingForNextVod) {
|
|
411
|
+
this.waitingForNextVod = false;
|
|
400
412
|
const isLeader = await this._sessionStateStore.isLeader(this._instanceId);
|
|
401
413
|
if (!isLeader) {
|
|
402
414
|
debug(`[${this._sessionId}]: Not a leader and first media sequence in a VOD is requested. Invalidate cache to ensure having the correct VOD.`);
|
|
@@ -406,6 +418,7 @@ class Session {
|
|
|
406
418
|
const currentVod = await this._sessionState.getCurrentVod();
|
|
407
419
|
if (currentVod) {
|
|
408
420
|
try {
|
|
421
|
+
debug(`[${this._sessionId}]: [${playheadState.vodMediaSeqVideo}]_[${currentVod.getLiveMediaSequencesCount()}]`);
|
|
409
422
|
const m3u8 = currentVod.getLiveMediaSequences(playheadState.mediaSeq, bw, playheadState.vodMediaSeqVideo, sessionState.discSeq, this.targetDurationPadding, this.forceTargetDuration);
|
|
410
423
|
debug(`[${this._sessionId}]: [${playheadState.mediaSeq + playheadState.vodMediaSeqVideo}][${sessionState.discSeq}][+${this.targetDurationPadding || 0}] Current media manifest for ${bw} requested`);
|
|
411
424
|
return m3u8;
|
|
@@ -726,10 +739,11 @@ class Session {
|
|
|
726
739
|
length = currentVod.getLiveMediaSequencesCount();
|
|
727
740
|
lastDiscontinuity = currentVod.getLastDiscontinuity();
|
|
728
741
|
}
|
|
742
|
+
await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
|
|
743
|
+
await this._sessionState.setCurrentVod(slateVod);
|
|
744
|
+
|
|
729
745
|
await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
730
746
|
await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
731
|
-
await this._sessionState.set("state", SessionState.VOD_NEXT_INITIATING);
|
|
732
|
-
await this._sessionState.setCurrentVod(slateVod);
|
|
733
747
|
await this._sessionState.set("mediaSeq", sessionState.mediaSeq + length);
|
|
734
748
|
await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
|
|
735
749
|
await this._sessionState.set("slateCount", sessionState.slateCount + 1);
|
|
@@ -813,10 +827,6 @@ class Session {
|
|
|
813
827
|
debug(`[${this._sessionId}]: ${currentVod.getDeltaTimes()}`);
|
|
814
828
|
debug(`[${this._sessionId}]: ${currentVod.getPlayheadPositions()}`);
|
|
815
829
|
//debug(newVod);
|
|
816
|
-
sessionState.mediaSeq = await this._sessionState.set("mediaSeq", 0);
|
|
817
|
-
sessionState.discSeq = await this._sessionState.set("discSeq", 0);
|
|
818
|
-
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
819
|
-
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
820
830
|
await this._playheadState.set("playheadRef", Date.now());
|
|
821
831
|
this.produceEvent({
|
|
822
832
|
type: 'NOW_PLAYING',
|
|
@@ -827,6 +837,10 @@ class Session {
|
|
|
827
837
|
});
|
|
828
838
|
sessionState.state = await this._sessionState.set("state", SessionState.VOD_PLAYING);
|
|
829
839
|
sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
|
|
840
|
+
sessionState.mediaSeq = await this._sessionState.set("mediaSeq", 0);
|
|
841
|
+
sessionState.discSeq = await this._sessionState.set("discSeq", 0);
|
|
842
|
+
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
|
|
843
|
+
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
|
|
830
844
|
await this._sessionState.remove("nextVod");
|
|
831
845
|
return;
|
|
832
846
|
} else {
|
package/engine/session_live.js
CHANGED
|
@@ -15,6 +15,7 @@ const daterangeAttribute = (key, attr) => {
|
|
|
15
15
|
return key.toUpperCase() + "=" + `"${attr}"`;
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
|
+
const TARGET_PLAYLIST_DURATION_SEC = 60;
|
|
18
19
|
const RESET_DELAY = 5000;
|
|
19
20
|
const FAIL_TIMEOUT = 4000;
|
|
20
21
|
const PlayheadState = Object.freeze({
|
|
@@ -30,7 +31,9 @@ class SessionLive {
|
|
|
30
31
|
this.sessionLiveStateStore = sessionLiveStore.sessionLiveStateStore;
|
|
31
32
|
this.instanceId = sessionLiveStore.instanceId;
|
|
32
33
|
this.mediaSeqCount = 0;
|
|
34
|
+
this.prevMediaSeqCount = 0;
|
|
33
35
|
this.discSeqCount = 0;
|
|
36
|
+
this.prevDiscSeqCount = 0;
|
|
34
37
|
this.targetDuration = 0;
|
|
35
38
|
this.masterManifestUri = null;
|
|
36
39
|
this.vodSegments = {};
|
|
@@ -106,6 +109,7 @@ class SessionLive {
|
|
|
106
109
|
}
|
|
107
110
|
|
|
108
111
|
this.mediaSeqCount = 0;
|
|
112
|
+
this.prevMediaSeqCount = 0;
|
|
109
113
|
this.discSeqCount = 0;
|
|
110
114
|
this.targetDuration = 0;
|
|
111
115
|
this.masterManifestUri = null;
|
|
@@ -303,7 +307,7 @@ class SessionLive {
|
|
|
303
307
|
const leadersMediaSeqCount = liveCounts.mediaSeqCount;
|
|
304
308
|
const leadersDiscSeqCount = liveCounts.discSeqCount;
|
|
305
309
|
if (leadersMediaSeqCount !== null) {
|
|
306
|
-
this.mediaSeqCount = leadersMediaSeqCount
|
|
310
|
+
this.mediaSeqCount = leadersMediaSeqCount;
|
|
307
311
|
debug(`[${this.sessionId}]: Setting mediaSeqCount to: [${this.mediaSeqCount}]`);
|
|
308
312
|
}
|
|
309
313
|
if (leadersDiscSeqCount !== null) {
|
|
@@ -342,7 +346,7 @@ class SessionLive {
|
|
|
342
346
|
let bw = Object.keys(this.mediaManifestURIs)[i];
|
|
343
347
|
|
|
344
348
|
const liveTargetBandwidth = this._findNearestBw(bw, Object.keys(this.mediaManifestURIs));
|
|
345
|
-
const vodTargetBandwidth = this.
|
|
349
|
+
const vodTargetBandwidth = this._getNearestBandwidth(bw, Object.keys(this.vodSegments));
|
|
346
350
|
|
|
347
351
|
// Remove segments and disc-tag if they are on top
|
|
348
352
|
if (this.vodSegments[vodTargetBandwidth].length > 0 && this.vodSegments[vodTargetBandwidth][0].discontinuity) {
|
|
@@ -481,68 +485,28 @@ class SessionLive {
|
|
|
481
485
|
// FOLLOWER only function
|
|
482
486
|
_updateLiveSegQueue() {
|
|
483
487
|
if (Object.keys(this.liveSegsForFollowers).length === 0) {
|
|
484
|
-
debug(`[${this.sessionId}]: Error No Segments found at all.`);
|
|
488
|
+
debug(`[${this.sessionId}]: FOLLOWER: Error No Segments found at all.`);
|
|
485
489
|
}
|
|
486
|
-
const vodBws = Object.keys(this.vodSegments);
|
|
487
490
|
const liveBws = Object.keys(this.liveSegsForFollowers);
|
|
488
491
|
const size = this.liveSegsForFollowers[liveBws[0]].length;
|
|
489
492
|
|
|
490
|
-
|
|
491
|
-
this.liveSegsForFollowers[liveBws[0]].map((seg) => {
|
|
492
|
-
if (seg.uri) {
|
|
493
|
-
newSegCount++;
|
|
494
|
-
}
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
debug(`[${this.sessionId}]: FOLLOWER: liveSegsForFollowers Bws=${Object.keys(this.liveSegsForFollowers)}_newSegCount=${newSegCount} `);
|
|
498
|
-
// Remove transitional segs & add live source segs collected from store
|
|
499
|
-
for (let k = 0; k < newSegCount; k++) {
|
|
500
|
-
let incrementDiscSeqCount = false;
|
|
501
|
-
// Shift the top vod segment on all variants
|
|
502
|
-
for (let i = 0; i < vodBws.length; i++) {
|
|
503
|
-
let seg = this.vodSegments[vodBws[i]].shift();
|
|
504
|
-
if (seg && seg.discontinuity) {
|
|
505
|
-
incrementDiscSeqCount = true;
|
|
506
|
-
this.vodSegments[vodBws[i]].shift();
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
if (incrementDiscSeqCount) {
|
|
510
|
-
this.discSeqCount++;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
// Push to bottom, new live source segment on all variants
|
|
493
|
+
// Push the New Live Segments to All Variants
|
|
514
494
|
for (let segIdx = 0; segIdx < size; segIdx++) {
|
|
515
|
-
let
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
if (!this.liveSegQueue[
|
|
519
|
-
this.liveSegQueue[
|
|
495
|
+
for (let i = 0; i < liveBws.length; i++) {
|
|
496
|
+
const liveBw = liveBws[i];
|
|
497
|
+
const liveSegFromLeader = this.liveSegsForFollowers[liveBw][segIdx];
|
|
498
|
+
if (!this.liveSegQueue[liveBw]) {
|
|
499
|
+
this.liveSegQueue[liveBw] = [];
|
|
520
500
|
}
|
|
521
|
-
this.liveSegQueue[
|
|
522
|
-
|
|
523
|
-
let segCount = 0;
|
|
524
|
-
this.liveSegQueue[bw].map((seg) => {
|
|
525
|
-
if (seg.uri) {
|
|
526
|
-
segCount++;
|
|
527
|
-
}
|
|
528
|
-
});
|
|
529
|
-
if (segCount > this.targetNumSeg) {
|
|
530
|
-
let seg = this.liveSegQueue[bw].shift();
|
|
531
|
-
if (seg && seg.discontinuity) {
|
|
532
|
-
incrementDiscSeqCount = true;
|
|
533
|
-
seg = this.liveSegQueue[bw].shift();
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
debug(`[${this.sessionId}]: Pushed segment (${liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag"}) to 'liveSegQueue'`);
|
|
537
|
-
});
|
|
538
|
-
if (incrementDiscSeqCount) {
|
|
539
|
-
this.discSeqCount++;
|
|
501
|
+
this.liveSegQueue[liveBw].push(liveSegFromLeader);
|
|
502
|
+
debug(`[${this.sessionId}]: FOLLOWER: Pushed segment (${liveSegFromLeader.uri ? liveSegFromLeader.uri : "Disc-tag"}) to 'liveSegQueue' (${liveBw})`);
|
|
540
503
|
}
|
|
541
504
|
}
|
|
542
|
-
|
|
543
|
-
this.
|
|
544
|
-
|
|
545
|
-
|
|
505
|
+
// Remove older segments and update counts
|
|
506
|
+
const newTotalDuration = this._incrementAndShift("FOLLOWER");
|
|
507
|
+
if (newTotalDuration) {
|
|
508
|
+
debug(`[${this.sessionId}]: FOLLOWER: New Adjusted Playlist Duration=${newTotalDuration}s`);
|
|
509
|
+
}
|
|
546
510
|
}
|
|
547
511
|
|
|
548
512
|
/**
|
|
@@ -718,8 +682,9 @@ class SessionLive {
|
|
|
718
682
|
|
|
719
683
|
// Respawners never do this, only starter followers.
|
|
720
684
|
// Edge Case: FOLLOWER transitioned from session with different segments from LEADER
|
|
721
|
-
|
|
722
|
-
|
|
685
|
+
const leaderStartedOnCount = leadersFirstSeqCounts.mediaSeqCount;
|
|
686
|
+
if (leaderStartedOnCount !== this.mediaSeqCount) {
|
|
687
|
+
this.mediaSeqCount = leaderStartedOnCount;
|
|
723
688
|
const transitSegs = await this.sessionLiveState.get("transitSegs");
|
|
724
689
|
if (!this._isEmpty(transitSegs)) {
|
|
725
690
|
this.vodSegments = transitSegs;
|
|
@@ -735,7 +700,7 @@ class SessionLive {
|
|
|
735
700
|
}
|
|
736
701
|
|
|
737
702
|
// Prepare to load segments...
|
|
738
|
-
debug(`[${this.instanceId}]: Newest mseq from LIVE=${allMediaSeqCounts[0]} First mseq in store=${leadersFirstSeqCounts.liveSourceMseqCount}`);
|
|
703
|
+
debug(`[${this.instanceId}][${this.sessionId}]: Newest mseq from LIVE=${allMediaSeqCounts[0]} First mseq in store=${leadersFirstSeqCounts.liveSourceMseqCount}`);
|
|
739
704
|
if (allMediaSeqCounts[0] === leadersFirstSeqCounts.liveSourceMseqCount) {
|
|
740
705
|
this.pushAmount = 1; // Follower from start
|
|
741
706
|
} else {
|
|
@@ -805,34 +770,34 @@ class SessionLive {
|
|
|
805
770
|
let pushPromises = [];
|
|
806
771
|
for (let i = 0; i < Object.keys(this.mediaManifestURIs).length; i++) {
|
|
807
772
|
let bw = Object.keys(this.mediaManifestURIs)[i];
|
|
773
|
+
// will add new segments to live seg queue
|
|
808
774
|
pushPromises.push(this._parseMediaManifest(this.liveSourceM3Us[bw].M3U, this.mediaManifestURIs[bw], bw));
|
|
809
775
|
debug(`[${this.sessionId}]: Pushed pushPromise for bw=${bw}`);
|
|
810
776
|
}
|
|
811
777
|
// Segment Pushing
|
|
812
778
|
debug(`[${this.sessionId}]: Executing Promises II: Segment Pushing`);
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
});
|
|
820
|
-
if (removedDiscSeqList.every((val, i, arr) => val === arr[0])) {
|
|
821
|
-
// if all variants removed equal amounts of disc-tags
|
|
822
|
-
this.discSeqCount += removedDiscSeqList[0];
|
|
779
|
+
await allSettled(pushPromises);
|
|
780
|
+
|
|
781
|
+
// UPDATE COUNTS, & Shift Segments in vodSegments and liveSegQueue if needed.
|
|
782
|
+
const newTotalDuration = this._incrementAndShift("LEADER");
|
|
783
|
+
if (newTotalDuration) {
|
|
784
|
+
debug(`[${this.sessionId}]: New Adjusted Playlist Duration=${newTotalDuration}s`);
|
|
823
785
|
}
|
|
824
|
-
// UPDATE COUNTS, & Shift Segments in vodSegments
|
|
825
|
-
await this._incrementAndShift();
|
|
826
786
|
}
|
|
827
787
|
|
|
828
788
|
// -----------------------------------------------------
|
|
829
789
|
// Leader writes to store so that Followers can read.
|
|
830
790
|
// -----------------------------------------------------
|
|
831
791
|
if (isLeader) {
|
|
832
|
-
debug(`[${this.sessionId}]: LEADER: Adding data to store!`);
|
|
833
792
|
if (this.allowedToSet) {
|
|
834
|
-
|
|
835
|
-
|
|
793
|
+
const liveBws = Object.keys(this.liveSegsForFollowers);
|
|
794
|
+
const segListSize = this.liveSegsForFollowers[liveBws[0]].length;
|
|
795
|
+
// Do not replace old data with empty data
|
|
796
|
+
if (segListSize > 0) {
|
|
797
|
+
debug(`[${this.sessionId}]: LEADER: Adding data to store!`);
|
|
798
|
+
await this.sessionLiveState.set("liveSegsForFollowers", this.liveSegsForFollowers);
|
|
799
|
+
await this.sessionLiveState.set("lastRequestedMediaSeqRaw", this.lastRequestedMediaSeqRaw);
|
|
800
|
+
}
|
|
836
801
|
}
|
|
837
802
|
|
|
838
803
|
// [LASTLY]: LEADER does this for respawned-FOLLOWERS' sake.
|
|
@@ -841,8 +806,8 @@ class SessionLive {
|
|
|
841
806
|
await timer(1000); // maybe remove
|
|
842
807
|
const firstCounts = {
|
|
843
808
|
liveSourceMseqCount: this.lastRequestedMediaSeqRaw,
|
|
844
|
-
mediaSeqCount: this.
|
|
845
|
-
discSeqCount: this.
|
|
809
|
+
mediaSeqCount: this.prevMediaSeqCount,
|
|
810
|
+
discSeqCount: this.prevDiscSeqCount,
|
|
846
811
|
};
|
|
847
812
|
debug(`[${this.sessionId}]: LEADER: I am adding 'firstCounts'=${JSON.stringify(firstCounts)} to Store for future followers`);
|
|
848
813
|
await this.sessionLiveState.set("firstCounts", firstCounts);
|
|
@@ -858,39 +823,148 @@ class SessionLive {
|
|
|
858
823
|
return;
|
|
859
824
|
}
|
|
860
825
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
826
|
+
_shiftSegments(opt) {
|
|
827
|
+
let _totalDur = 0;
|
|
828
|
+
let _segments = {};
|
|
829
|
+
let _name = "";
|
|
830
|
+
let _removedSegments = 0;
|
|
831
|
+
let _removedDiscontinuities = 0;
|
|
832
|
+
|
|
833
|
+
if (opt && opt.totalDur) {
|
|
834
|
+
_totalDur = opt.totalDur;
|
|
835
|
+
}
|
|
836
|
+
if (opt && opt.segments) {
|
|
837
|
+
_segments = JSON.parse(JSON.stringify(opt.segments)); // clone it
|
|
838
|
+
}
|
|
839
|
+
if (opt && opt.name) {
|
|
840
|
+
_name = opt.name || "NONE";
|
|
841
|
+
}
|
|
842
|
+
if (opt && opt.removedSegments) {
|
|
843
|
+
_removedSegments = opt.removedSegments;
|
|
844
|
+
}
|
|
845
|
+
if (opt && opt.removedDiscontinuities) {
|
|
846
|
+
_removedDiscontinuities = opt.removedDiscontinuities;
|
|
847
|
+
}
|
|
848
|
+
const bws = Object.keys(_segments);
|
|
849
|
+
|
|
850
|
+
/* When Total Duration is past the Limit, start Shifting V2L|LIVE segments if found */
|
|
851
|
+
while (_totalDur > TARGET_PLAYLIST_DURATION_SEC) {
|
|
852
|
+
// Skip loop if there are no more segments to remove...
|
|
853
|
+
if (_segments[bws[0]].length === 0) {
|
|
854
|
+
return { totalDuration: _totalDur, removedSegments: _removedSegments, removedDiscontinuities: _removedDiscontinuities, shiftedSegments: _segments };
|
|
855
|
+
}
|
|
856
|
+
debug(`[${this.sessionId}]: ${_name}: (${_totalDur})s/(${TARGET_PLAYLIST_DURATION_SEC})s - Playlist Duration is Over the Target. Shift needed!`);
|
|
857
|
+
let timeToRemove = 0;
|
|
864
858
|
let incrementDiscSeqCount = false;
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
859
|
+
|
|
860
|
+
// Shift Segments for each variant...
|
|
861
|
+
for (let i = 0; i < bws.length; i++) {
|
|
862
|
+
let seg = _segments[bws[i]].shift();
|
|
863
|
+
if (i === 0) {
|
|
864
|
+
debug(`[${this.sessionId}]: ${_name}: (${bws[i]}) Ejected from playlist->: ${JSON.stringify(seg, null, 2)}`);
|
|
865
|
+
}
|
|
866
|
+
if (seg && seg.discontinuity) {
|
|
867
|
+
incrementDiscSeqCount = true;
|
|
868
|
+
if (_segments[bws[i]].length > 0) {
|
|
869
|
+
seg = _segments[bws[i]].shift();
|
|
870
|
+
if (i === 0) {
|
|
871
|
+
debug(`[${this.sessionId}]: ${_name}: (${bws[i]}) Ejected from playlist->: ${JSON.stringify(seg, null, 2)}`);
|
|
872
|
+
}
|
|
872
873
|
}
|
|
873
874
|
}
|
|
875
|
+
if (seg && seg.duration) {
|
|
876
|
+
timeToRemove = seg.duration;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (timeToRemove) {
|
|
880
|
+
_totalDur -= timeToRemove;
|
|
881
|
+
// Increment number of removed segments...
|
|
882
|
+
_removedSegments++;
|
|
874
883
|
}
|
|
875
884
|
if (incrementDiscSeqCount) {
|
|
876
|
-
|
|
885
|
+
// Update Session Live Discontinuity Sequence Count
|
|
886
|
+
_removedDiscontinuities++;
|
|
877
887
|
}
|
|
878
|
-
this.mediaSeqCount++;
|
|
879
888
|
}
|
|
889
|
+
return { totalDuration: _totalDur, removedSegments: _removedSegments, removedDiscontinuities: _removedDiscontinuities, shiftedSegments: _segments };
|
|
890
|
+
}
|
|
880
891
|
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
892
|
+
/**
|
|
893
|
+
* Shifts V2L or LIVE items if total segment duration (V2L+LIVE) are over the target duration.
|
|
894
|
+
* It will also update and increment SessionLive's MediaSeqCount and DiscSeqCount based
|
|
895
|
+
* on what was shifted.
|
|
896
|
+
* @param {string} instanceName Name of instance "LEADER" | "FOLLOWER"
|
|
897
|
+
* @returns {number} The new total duration in seconds
|
|
898
|
+
*/
|
|
899
|
+
_incrementAndShift(instanceName) {
|
|
900
|
+
if (!instanceName) {
|
|
901
|
+
instanceName = "UNKNOWN";
|
|
902
|
+
}
|
|
903
|
+
const vodBws = Object.keys(this.vodSegments);
|
|
904
|
+
const liveBws = Object.keys(this.liveSegQueue);
|
|
905
|
+
let vodTotalDur = 0;
|
|
906
|
+
let liveTotalDur = 0;
|
|
907
|
+
let totalDur = 0;
|
|
908
|
+
let removedSegments = 0;
|
|
909
|
+
let removedDiscontinuities = 0;
|
|
910
|
+
|
|
911
|
+
// Calculate Playlist Total Duration
|
|
912
|
+
this.vodSegments[vodBws[0]].forEach((seg) => {
|
|
913
|
+
if (seg.duration) {
|
|
914
|
+
vodTotalDur += seg.duration;
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
this.liveSegQueue[liveBws[0]].forEach((seg) => {
|
|
918
|
+
if (seg.duration) {
|
|
919
|
+
liveTotalDur += seg.duration;
|
|
920
|
+
}
|
|
921
|
+
});
|
|
922
|
+
totalDur = vodTotalDur + liveTotalDur;
|
|
923
|
+
debug(`[${this.sessionId}]: ${instanceName}: L2L dur->: ${liveTotalDur}s | V2L dur->: ${vodTotalDur}s | Total dur->: ${totalDur}s`);
|
|
924
|
+
|
|
925
|
+
/** --- SHIFT then INCREMENT --- **/
|
|
926
|
+
|
|
927
|
+
// Shift V2L Segments
|
|
928
|
+
const outputV2L = this._shiftSegments({
|
|
929
|
+
name: instanceName,
|
|
930
|
+
totalDur: totalDur,
|
|
931
|
+
segments: this.vodSegments,
|
|
932
|
+
removedSegments: removedSegments,
|
|
933
|
+
removedDiscontinuities: removedDiscontinuities,
|
|
934
|
+
});
|
|
935
|
+
// Update V2L Segments
|
|
936
|
+
this.vodSegments = outputV2L.shiftedSegments;
|
|
937
|
+
// Update values
|
|
938
|
+
totalDur = outputV2L.totalDuration;
|
|
939
|
+
removedSegments = outputV2L.removedSegments;
|
|
940
|
+
removedDiscontinuities = outputV2L.removedDiscontinuities;
|
|
941
|
+
// Shift LIVE Segments
|
|
942
|
+
const outputLIVE = this._shiftSegments({
|
|
943
|
+
name: instanceName,
|
|
944
|
+
totalDur: totalDur,
|
|
945
|
+
segments: this.liveSegQueue,
|
|
946
|
+
removedSegments: removedSegments,
|
|
947
|
+
removedDiscontinuities: removedDiscontinuities,
|
|
948
|
+
});
|
|
949
|
+
// Update LIVE Segments
|
|
950
|
+
this.liveSegQueue = outputLIVE.shiftedSegments;
|
|
951
|
+
// Update values
|
|
952
|
+
totalDur = outputLIVE.totalDuration;
|
|
953
|
+
removedSegments = outputLIVE.removedSegments;
|
|
954
|
+
removedDiscontinuities = outputLIVE.removedDiscontinuities;
|
|
955
|
+
|
|
956
|
+
// Update Session Live Discontinuity Sequence Count...
|
|
957
|
+
this.prevDiscSeqCount = this.discSeqCount;
|
|
958
|
+
this.discSeqCount += removedDiscontinuities;
|
|
959
|
+
// Update Session Live Media Sequence Count...
|
|
960
|
+
this.prevMediaSeqCount = this.mediaSeqCount;
|
|
961
|
+
this.mediaSeqCount += removedSegments;
|
|
962
|
+
if (this.discSeqCount !== this.prevDiscSeqCount) {
|
|
963
|
+
debug(`[${this.sessionId}]: ${instanceName}: Incrementing Dseq Count from {${this.prevDiscSeqCount}} -> {${this.discSeqCount}}`);
|
|
964
|
+
}
|
|
965
|
+
debug(`[${this.sessionId}]: ${instanceName}: Incrementing Mseq Count from [${this.prevMediaSeqCount}] -> [${this.mediaSeqCount}]`);
|
|
966
|
+
debug(`[${this.sessionId}]: ${instanceName}: Finished updating all Counts and Segment Queues!`);
|
|
967
|
+
return totalDur;
|
|
894
968
|
}
|
|
895
969
|
|
|
896
970
|
async _loadMediaManifest(bw) {
|
|
@@ -957,28 +1031,17 @@ class SessionLive {
|
|
|
957
1031
|
//debug(`[${this.sessionId}]: Current RAW Mseq: [${m3u.get("mediaSequence")}]`);
|
|
958
1032
|
//debug(`[${this.sessionId}]: Previous RAW Mseq: [${this.lastRequestedMediaSeqRaw}]`);
|
|
959
1033
|
|
|
960
|
-
// TODO: UPDATE target for number of segments in a manifest, if appropriate.
|
|
961
|
-
// if (m3u.items.PlaylistItem.length < this.targetNumSeg) {
|
|
962
|
-
// debug(`[${this.sessionId}]: WE PLAN TO LOWER SEGMENT AMOUNT IN MANIFEST FROM (${this.targetNumSeg}) TO (${m3u.items.PlaylistItem.length})`);
|
|
963
|
-
// this.targetNumSeg = m3u.items.PlaylistItem.length;
|
|
964
|
-
// }
|
|
965
|
-
|
|
966
1034
|
if (this.pushAmount >= 0) {
|
|
967
1035
|
this.lastRequestedMediaSeqRaw = m3u.get("mediaSequence");
|
|
968
1036
|
}
|
|
969
1037
|
this.targetDuration = m3u.get("targetDuration");
|
|
970
|
-
let amountRemovedDiscSeqs = 0;
|
|
971
1038
|
let startIdx = m3u.items.PlaylistItem.length - this.pushAmount;
|
|
972
1039
|
startIdx = startIdx < 0 ? 0 : startIdx;
|
|
973
1040
|
if (mediaManifestUri) {
|
|
974
1041
|
// push segments
|
|
975
|
-
|
|
1042
|
+
this._addLiveSegmentsToQueue(startIdx, m3u.items.PlaylistItem, baseUrl, liveTargetBandwidth);
|
|
976
1043
|
}
|
|
977
|
-
|
|
978
|
-
removedDiscSeqs: amountRemovedDiscSeqs,
|
|
979
|
-
bw: liveTargetBandwidth,
|
|
980
|
-
};
|
|
981
|
-
resolve(resolveObj);
|
|
1044
|
+
resolve();
|
|
982
1045
|
} catch (exc) {
|
|
983
1046
|
console.error("ERROR: " + exc);
|
|
984
1047
|
reject(exc);
|
|
@@ -986,8 +1049,15 @@ class SessionLive {
|
|
|
986
1049
|
});
|
|
987
1050
|
}
|
|
988
1051
|
|
|
1052
|
+
/**
|
|
1053
|
+
* Collects 'new' PlaylistItems and converts them into custom SegmentItems,
|
|
1054
|
+
* then Pushes them to the LiveSegQueue for all variants.
|
|
1055
|
+
* @param {number} startIdx
|
|
1056
|
+
* @param {m3u8.Item.PlaylistItem} playlistItems
|
|
1057
|
+
* @param {string} baseUrl
|
|
1058
|
+
* @param {string} liveTargetBandwidth
|
|
1059
|
+
*/
|
|
989
1060
|
_addLiveSegmentsToQueue(startIdx, playlistItems, baseUrl, liveTargetBandwidth) {
|
|
990
|
-
let amountRemovedDiscSeqs = 0;
|
|
991
1061
|
for (let i = startIdx; i < playlistItems.length; i++) {
|
|
992
1062
|
debug(`[${this.sessionId}]: Adding Live Segment(s) to Queue (for bw=${liveTargetBandwidth})`);
|
|
993
1063
|
let seg = {};
|
|
@@ -1056,30 +1126,11 @@ class SessionLive {
|
|
|
1056
1126
|
if (daterangeData) {
|
|
1057
1127
|
seg["daterange"] = daterangeData;
|
|
1058
1128
|
}
|
|
1059
|
-
|
|
1129
|
+
// Push new Live Segments!
|
|
1060
1130
|
this.liveSegQueue[liveTargetBandwidth].push(seg);
|
|
1061
|
-
this.liveSegsForFollowers[liveTargetBandwidth].push(seg);
|
|
1062
|
-
let segCount = 0;
|
|
1063
|
-
this.liveSegQueue[liveTargetBandwidth].map((seg) => {
|
|
1064
|
-
if (seg.uri) {
|
|
1065
|
-
segCount++;
|
|
1066
|
-
}
|
|
1067
|
-
});
|
|
1068
|
-
debug(`[${this.sessionId}]: size of queue=${segCount}_targetNumseg=${this.targetNumSeg}`);
|
|
1069
|
-
if (segCount > this.targetNumSeg) {
|
|
1070
|
-
debug(`[${this.sessionId}]: liveTargetBandwidth=${liveTargetBandwidth}_seg=${JSON.stringify(seg, null, 2)}`);
|
|
1071
|
-
seg = this.liveSegQueue[liveTargetBandwidth].shift();
|
|
1072
|
-
if (seg) {
|
|
1073
|
-
if (seg.discontinuity) {
|
|
1074
|
-
this.liveSegQueue[liveTargetBandwidth].shift();
|
|
1075
|
-
amountRemovedDiscSeqs++;
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
debug(`[${this.sessionId}]: After Shifting -> size of queue=${segCount}_targetNumseg=${this.targetNumSeg}`);
|
|
1079
|
-
}
|
|
1131
|
+
this.liveSegsForFollowers[liveTargetBandwidth].push(seg);
|
|
1080
1132
|
}
|
|
1081
1133
|
}
|
|
1082
|
-
return amountRemovedDiscSeqs;
|
|
1083
1134
|
}
|
|
1084
1135
|
|
|
1085
1136
|
/*
|
package/package.json
CHANGED
|
@@ -202,36 +202,42 @@ segment_0_8.ts
|
|
|
202
202
|
|
|
203
203
|
const mockLiveSegments = {
|
|
204
204
|
180000: [
|
|
205
|
-
{ duration:
|
|
206
|
-
{ duration:
|
|
207
|
-
{ duration:
|
|
208
|
-
{ duration:
|
|
209
|
-
{ duration:
|
|
210
|
-
{ duration:
|
|
211
|
-
{ duration:
|
|
212
|
-
{ duration:
|
|
205
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg09.ts" },
|
|
206
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg10.ts" },
|
|
207
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg11.ts" },
|
|
208
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg12.ts" },
|
|
209
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg13.ts" },
|
|
210
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg14.ts" },
|
|
211
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg15.ts" },
|
|
212
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg16.ts" },
|
|
213
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg17.ts" },
|
|
214
|
+
{ duration: 6, uri: "http://mock.mock.com/180000/seg18.ts" },
|
|
213
215
|
{ discontinuity: true },
|
|
214
216
|
],
|
|
215
217
|
1258000: [
|
|
216
|
-
{ duration:
|
|
217
|
-
{ duration:
|
|
218
|
-
{ duration:
|
|
219
|
-
{ duration:
|
|
220
|
-
{ duration:
|
|
221
|
-
{ duration:
|
|
222
|
-
{ duration:
|
|
223
|
-
{ duration:
|
|
218
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg09.ts" },
|
|
219
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg10.ts" },
|
|
220
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg11.ts" },
|
|
221
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg12.ts" },
|
|
222
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg13.ts" },
|
|
223
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg14.ts" },
|
|
224
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg15.ts" },
|
|
225
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg16.ts" },
|
|
226
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg17.ts" },
|
|
227
|
+
{ duration: 6, uri: "http://mock.mock.com/1258000/seg18.ts" },
|
|
224
228
|
{ discontinuity: true },
|
|
225
229
|
],
|
|
226
230
|
2488000: [
|
|
227
|
-
{ duration:
|
|
228
|
-
{ duration:
|
|
229
|
-
{ duration:
|
|
230
|
-
{ duration:
|
|
231
|
-
{ duration:
|
|
232
|
-
{ duration:
|
|
233
|
-
{ duration:
|
|
234
|
-
{ duration:
|
|
231
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg09.ts" },
|
|
232
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg10.ts" },
|
|
233
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg11.ts" },
|
|
234
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg12.ts" },
|
|
235
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg13.ts" },
|
|
236
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg14.ts" },
|
|
237
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg15.ts" },
|
|
238
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg16.ts" },
|
|
239
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg17.ts" },
|
|
240
|
+
{ duration: 6, uri: "http://mock.mock.com/2488000/seg18.ts" },
|
|
235
241
|
{ discontinuity: true },
|
|
236
242
|
],
|
|
237
243
|
};
|