eyevinn-channel-engine 4.2.3 → 4.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/engine/session.js +24 -16
- package/dist/engine/session_state.js +15 -12
- package/dist/examples/truncate.d.ts +1 -0
- package/dist/examples/truncate.js +92 -0
- package/dist/package.json +2 -2
- package/engine/session.js +23 -18
- package/engine/session_state.js +4 -4
- package/examples/truncate.ts +104 -0
- package/package.json +2 -2
package/dist/engine/session.js
CHANGED
|
@@ -2477,26 +2477,34 @@ var Session = /** @class */ (function () {
|
|
|
2477
2477
|
Session.prototype._truncateVod = function (vodResponse) {
|
|
2478
2478
|
var _this = this;
|
|
2479
2479
|
return new Promise(function (resolve, reject) {
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
.
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
var
|
|
2486
|
-
mediaManifestStream.push(truncatedVod.getMediaManifest(bw));
|
|
2487
|
-
mediaManifestStream.push(null);
|
|
2488
|
-
return mediaManifestStream;
|
|
2489
|
-
};
|
|
2490
|
-
if (_this.use_demuxed_audio) {
|
|
2491
|
-
audioManifestLoader = function (audioGroupId, audioLanguage) {
|
|
2480
|
+
try {
|
|
2481
|
+
var truncatedVod_1 = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000);
|
|
2482
|
+
truncatedVod_1.load()
|
|
2483
|
+
.then(function () {
|
|
2484
|
+
var audioManifestLoader;
|
|
2485
|
+
var mediaManifestLoader = function (bw) {
|
|
2492
2486
|
var mediaManifestStream = new Readable();
|
|
2493
|
-
mediaManifestStream.push(
|
|
2487
|
+
mediaManifestStream.push(truncatedVod_1.getMediaManifest(bw));
|
|
2494
2488
|
mediaManifestStream.push(null);
|
|
2495
2489
|
return mediaManifestStream;
|
|
2496
2490
|
};
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2491
|
+
if (_this.use_demuxed_audio) {
|
|
2492
|
+
audioManifestLoader = function (audioGroupId, audioLanguage) {
|
|
2493
|
+
var mediaManifestStream = new Readable();
|
|
2494
|
+
mediaManifestStream.push(truncatedVod_1.getAudioManifest(audioGroupId, audioLanguage));
|
|
2495
|
+
mediaManifestStream.push(null);
|
|
2496
|
+
return mediaManifestStream;
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
resolve({ mediaManifestLoader: mediaManifestLoader, audioManifestLoader: audioManifestLoader });
|
|
2500
|
+
}).catch(function (err) {
|
|
2501
|
+
debug(err);
|
|
2502
|
+
reject(err);
|
|
2503
|
+
});
|
|
2504
|
+
}
|
|
2505
|
+
catch (err) {
|
|
2506
|
+
reject(err);
|
|
2507
|
+
}
|
|
2500
2508
|
});
|
|
2501
2509
|
};
|
|
2502
2510
|
Session.prototype._truncateSlate = function (afterVod, requestedDuration, vodUri) {
|
|
@@ -126,31 +126,34 @@ var SharedSessionState = /** @class */ (function () {
|
|
|
126
126
|
};
|
|
127
127
|
SharedSessionState.prototype.setCurrentVod = function (hlsVod, opts) {
|
|
128
128
|
return __awaiter(this, void 0, void 0, function () {
|
|
129
|
-
var
|
|
129
|
+
var isLeader;
|
|
130
130
|
return __generator(this, function (_a) {
|
|
131
131
|
switch (_a.label) {
|
|
132
132
|
case 0:
|
|
133
133
|
if (!this.sessionId) {
|
|
134
134
|
throw new Error("shared session state store has not been initialized");
|
|
135
135
|
}
|
|
136
|
-
if (!this.store.isShared()) return [3 /*break*/,
|
|
137
|
-
return [4 /*yield*/, this.
|
|
136
|
+
if (!this.store.isShared()) return [3 /*break*/, 6];
|
|
137
|
+
return [4 /*yield*/, this.store.clearLeaderCache()];
|
|
138
138
|
case 1:
|
|
139
|
-
|
|
139
|
+
_a.sent();
|
|
140
140
|
return [4 /*yield*/, this.store.isLeader(this.instanceId)];
|
|
141
141
|
case 2:
|
|
142
142
|
isLeader = _a.sent();
|
|
143
|
-
if (!isLeader)
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
143
|
+
if (!isLeader) return [3 /*break*/, 4];
|
|
144
|
+
return [4 /*yield*/, this.set("currentVod", hlsVod.toJSON())];
|
|
145
|
+
case 3:
|
|
146
|
+
_a.sent();
|
|
148
147
|
return [3 /*break*/, 5];
|
|
149
|
-
case 3: return [4 /*yield*/, this.set("currentVod", hlsVod)];
|
|
150
148
|
case 4:
|
|
151
|
-
|
|
149
|
+
debug("[".concat(this.sessionId, "]: not a leader, will not overwrite currentVod in shared store"));
|
|
152
150
|
_a.label = 5;
|
|
153
|
-
case 5:
|
|
151
|
+
case 5: return [3 /*break*/, 8];
|
|
152
|
+
case 6: return [4 /*yield*/, this.set("currentVod", hlsVod)];
|
|
153
|
+
case 7:
|
|
154
|
+
_a.sent();
|
|
155
|
+
_a.label = 8;
|
|
156
|
+
case 8:
|
|
154
157
|
if (this.cache.currentVod) {
|
|
155
158
|
this.cache.currentVod.ts = Date.now();
|
|
156
159
|
this.cache.currentVod.ttl = CURRENTVOD_CACHE_TTL;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Reference implementation of Channel Engine library
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
var index_1 = require("../index");
|
|
7
|
+
var DEFAULT_ASSETS = [
|
|
8
|
+
{
|
|
9
|
+
id: 1,
|
|
10
|
+
title: "Tears of Steel",
|
|
11
|
+
uri: "https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/master.m3u8",
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
id: 2,
|
|
15
|
+
title: "VINN",
|
|
16
|
+
uri: "https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8",
|
|
17
|
+
},
|
|
18
|
+
];
|
|
19
|
+
var RefAssetManager = /** @class */ (function () {
|
|
20
|
+
function RefAssetManager(opts) {
|
|
21
|
+
this.assets = {};
|
|
22
|
+
this.assets['1'] = DEFAULT_ASSETS;
|
|
23
|
+
this.pos = {
|
|
24
|
+
'1': 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} vodRequest
|
|
30
|
+
* {
|
|
31
|
+
* sessionId,
|
|
32
|
+
* category,
|
|
33
|
+
* playlistId
|
|
34
|
+
* }
|
|
35
|
+
*/
|
|
36
|
+
RefAssetManager.prototype.getNextVod = function (vodRequest) {
|
|
37
|
+
var _this = this;
|
|
38
|
+
return new Promise(function (resolve, reject) {
|
|
39
|
+
var channelId = vodRequest.playlistId;
|
|
40
|
+
if (_this.pos[channelId] === undefined) {
|
|
41
|
+
_this.pos[channelId] = 0;
|
|
42
|
+
_this.assets[channelId] = DEFAULT_ASSETS;
|
|
43
|
+
}
|
|
44
|
+
if (_this.assets[channelId]) {
|
|
45
|
+
var vod = _this.assets[channelId][_this.pos[channelId]++];
|
|
46
|
+
if (_this.pos[channelId] > _this.assets[channelId].length - 1) {
|
|
47
|
+
_this.pos[channelId] = 0;
|
|
48
|
+
}
|
|
49
|
+
var vodResponse = {
|
|
50
|
+
id: vod.id,
|
|
51
|
+
title: vod.title,
|
|
52
|
+
uri: vod.uri,
|
|
53
|
+
desiredDuration: 50000
|
|
54
|
+
};
|
|
55
|
+
resolve(vodResponse);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
reject("Invalid channelId provided");
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
return RefAssetManager;
|
|
63
|
+
}());
|
|
64
|
+
var RefChannelManager = /** @class */ (function () {
|
|
65
|
+
function RefChannelManager() {
|
|
66
|
+
this.channels = [];
|
|
67
|
+
this.channels.push({ id: "1", profile: this._getProfile() });
|
|
68
|
+
}
|
|
69
|
+
RefChannelManager.prototype.getChannels = function () {
|
|
70
|
+
return this.channels;
|
|
71
|
+
};
|
|
72
|
+
RefChannelManager.prototype._getProfile = function () {
|
|
73
|
+
return [
|
|
74
|
+
{ bw: 6134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1024, 458] },
|
|
75
|
+
{ bw: 2323000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [640, 286] },
|
|
76
|
+
{ bw: 1313000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [480, 214] },
|
|
77
|
+
];
|
|
78
|
+
};
|
|
79
|
+
return RefChannelManager;
|
|
80
|
+
}());
|
|
81
|
+
var refAssetManager = new RefAssetManager();
|
|
82
|
+
var refChannelManager = new RefChannelManager();
|
|
83
|
+
var engineOptions = {
|
|
84
|
+
heartbeat: "/",
|
|
85
|
+
averageSegmentDuration: 2000,
|
|
86
|
+
channelManager: refChannelManager,
|
|
87
|
+
defaultSlateUri: "https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
|
|
88
|
+
slateRepetitions: 10,
|
|
89
|
+
};
|
|
90
|
+
var engine = new index_1.ChannelEngine(refAssetManager, engineOptions);
|
|
91
|
+
engine.start();
|
|
92
|
+
engine.listen(process.env.PORT || 8000);
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.5",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@eyevinn/hls-repeat": "^0.2.0",
|
|
32
32
|
"@eyevinn/hls-truncate": "^0.2.0",
|
|
33
|
-
"@eyevinn/hls-vodtolive": "^3.0.
|
|
33
|
+
"@eyevinn/hls-vodtolive": "^3.0.1",
|
|
34
34
|
"@eyevinn/m3u8": "^0.5.3",
|
|
35
35
|
"abort-controller": "^3.0.0",
|
|
36
36
|
"debug": "^3.2.7",
|
package/engine/session.js
CHANGED
|
@@ -1794,29 +1794,34 @@ class Session {
|
|
|
1794
1794
|
|
|
1795
1795
|
_truncateVod(vodResponse) {
|
|
1796
1796
|
return new Promise((resolve, reject) => {
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
.
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
mediaManifestStream.push(truncatedVod.getMediaManifest(bw));
|
|
1804
|
-
mediaManifestStream.push(null);
|
|
1805
|
-
return mediaManifestStream;
|
|
1806
|
-
};
|
|
1807
|
-
if (this.use_demuxed_audio) {
|
|
1808
|
-
audioManifestLoader = (audioGroupId, audioLanguage) => {
|
|
1797
|
+
try {
|
|
1798
|
+
const truncatedVod = new HLSTruncateVod(vodResponse.uri, vodResponse.desiredDuration / 1000);
|
|
1799
|
+
truncatedVod.load()
|
|
1800
|
+
.then(() => {
|
|
1801
|
+
let audioManifestLoader;
|
|
1802
|
+
const mediaManifestLoader = (bw) => {
|
|
1809
1803
|
let mediaManifestStream = new Readable();
|
|
1810
|
-
mediaManifestStream.push(truncatedVod.
|
|
1804
|
+
mediaManifestStream.push(truncatedVod.getMediaManifest(bw));
|
|
1811
1805
|
mediaManifestStream.push(null);
|
|
1812
1806
|
return mediaManifestStream;
|
|
1813
1807
|
};
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1808
|
+
if (this.use_demuxed_audio) {
|
|
1809
|
+
audioManifestLoader = (audioGroupId, audioLanguage) => {
|
|
1810
|
+
let mediaManifestStream = new Readable();
|
|
1811
|
+
mediaManifestStream.push(truncatedVod.getAudioManifest(audioGroupId, audioLanguage));
|
|
1812
|
+
mediaManifestStream.push(null);
|
|
1813
|
+
return mediaManifestStream;
|
|
1814
|
+
};
|
|
1815
|
+
}
|
|
1816
|
+
resolve({ mediaManifestLoader, audioManifestLoader });
|
|
1817
|
+
}).catch(err => {
|
|
1818
|
+
debug(err);
|
|
1819
|
+
reject(err);
|
|
1820
|
+
});
|
|
1821
|
+
} catch (err) {
|
|
1822
|
+
reject(err);
|
|
1823
|
+
}
|
|
1817
1824
|
});
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
1825
|
}
|
|
1821
1826
|
|
|
1822
1827
|
_truncateSlate(afterVod, requestedDuration, vodUri) {
|
package/engine/session_state.js
CHANGED
|
@@ -75,12 +75,12 @@ class SharedSessionState {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
if (this.store.isShared()) {
|
|
78
|
-
|
|
78
|
+
await this.store.clearLeaderCache();
|
|
79
79
|
const isLeader = await this.store.isLeader(this.instanceId);
|
|
80
|
-
if (
|
|
80
|
+
if (isLeader) {
|
|
81
|
+
await this.set("currentVod", hlsVod.toJSON());
|
|
82
|
+
} else {
|
|
81
83
|
debug(`[${this.sessionId}]: not a leader, will not overwrite currentVod in shared store`);
|
|
82
|
-
hlsVod = new HLSVod();
|
|
83
|
-
hlsVod.fromJSON(newHlsVodJson);
|
|
84
84
|
}
|
|
85
85
|
} else {
|
|
86
86
|
await this.set("currentVod", hlsVod);
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Reference implementation of Channel Engine library
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { ChannelEngine, ChannelEngineOpts,
|
|
6
|
+
IAssetManager, IChannelManager,
|
|
7
|
+
VodRequest, VodResponse, Channel, ChannelProfile
|
|
8
|
+
} from "../index";
|
|
9
|
+
|
|
10
|
+
const DEFAULT_ASSETS = [
|
|
11
|
+
{
|
|
12
|
+
id: 1,
|
|
13
|
+
title: "Tears of Steel",
|
|
14
|
+
uri: "https://maitv-vod.lab.eyevinn.technology/tearsofsteel_4k.mov/master.m3u8",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 2,
|
|
18
|
+
title: "VINN",
|
|
19
|
+
uri: "https://maitv-vod.lab.eyevinn.technology/VINN.mp4/master.m3u8",
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
class RefAssetManager implements IAssetManager {
|
|
24
|
+
private assets;
|
|
25
|
+
private pos;
|
|
26
|
+
|
|
27
|
+
constructor(opts?) {
|
|
28
|
+
this.assets = {};
|
|
29
|
+
this.assets['1'] = DEFAULT_ASSETS;
|
|
30
|
+
this.pos = {
|
|
31
|
+
'1': 0,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {Object} vodRequest
|
|
38
|
+
* {
|
|
39
|
+
* sessionId,
|
|
40
|
+
* category,
|
|
41
|
+
* playlistId
|
|
42
|
+
* }
|
|
43
|
+
*/
|
|
44
|
+
getNextVod(vodRequest: VodRequest): Promise<VodResponse> {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
const channelId = vodRequest.playlistId;
|
|
47
|
+
if (this.pos[channelId] === undefined) {
|
|
48
|
+
this.pos[channelId] = 0;
|
|
49
|
+
this.assets[channelId] = DEFAULT_ASSETS;
|
|
50
|
+
}
|
|
51
|
+
if (this.assets[channelId]) {
|
|
52
|
+
let vod = this.assets[channelId][this.pos[channelId]++];
|
|
53
|
+
if (this.pos[channelId] > this.assets[channelId].length - 1) {
|
|
54
|
+
this.pos[channelId] = 0;
|
|
55
|
+
}
|
|
56
|
+
const vodResponse = {
|
|
57
|
+
id: vod.id,
|
|
58
|
+
title: vod.title,
|
|
59
|
+
uri: vod.uri,
|
|
60
|
+
desiredDuration: 50000
|
|
61
|
+
};
|
|
62
|
+
resolve(vodResponse);
|
|
63
|
+
} else {
|
|
64
|
+
reject("Invalid channelId provided");
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
class RefChannelManager implements IChannelManager {
|
|
71
|
+
private channels: Channel[] = [];
|
|
72
|
+
|
|
73
|
+
constructor() {
|
|
74
|
+
this.channels.push({ id: "1", profile: this._getProfile() });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
getChannels(): Channel[] {
|
|
78
|
+
return this.channels;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
_getProfile(): ChannelProfile[] {
|
|
82
|
+
return [
|
|
83
|
+
{ bw: 6134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1024, 458] },
|
|
84
|
+
{ bw: 2323000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [640, 286] },
|
|
85
|
+
{ bw: 1313000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [480, 214] },
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const refAssetManager = new RefAssetManager();
|
|
91
|
+
const refChannelManager = new RefChannelManager();
|
|
92
|
+
|
|
93
|
+
const engineOptions: ChannelEngineOpts = {
|
|
94
|
+
heartbeat: "/",
|
|
95
|
+
averageSegmentDuration: 2000,
|
|
96
|
+
channelManager: refChannelManager,
|
|
97
|
+
defaultSlateUri:
|
|
98
|
+
"https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
|
|
99
|
+
slateRepetitions: 10,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const engine = new ChannelEngine(refAssetManager, engineOptions);
|
|
103
|
+
engine.start();
|
|
104
|
+
engine.listen(process.env.PORT || 8000);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.5",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@eyevinn/hls-repeat": "^0.2.0",
|
|
32
32
|
"@eyevinn/hls-truncate": "^0.2.0",
|
|
33
|
-
"@eyevinn/hls-vodtolive": "^3.0.
|
|
33
|
+
"@eyevinn/hls-vodtolive": "^3.0.1",
|
|
34
34
|
"@eyevinn/m3u8": "^0.5.3",
|
|
35
35
|
"abort-controller": "^3.0.0",
|
|
36
36
|
"debug": "^3.2.7",
|