eyevinn-channel-engine 4.0.19 → 4.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.
@@ -9,7 +9,7 @@ jobs:
9
9
 
10
10
  strategy:
11
11
  matrix:
12
- node-version: [14.x, 16.x]
12
+ node-version: [14.x, 16.x, 18.x]
13
13
 
14
14
  steps:
15
15
  - uses: actions/checkout@v3
package/README.md CHANGED
@@ -35,6 +35,7 @@ A live demo and example is available here: [https://vod2live.eyevinn.technology]
35
35
  - Mix VOD2Live channel with a "real" live HLS stream
36
36
  - Develop adapters to plugin with custom scheduling endpoints
37
37
  - High Availability with Redis Cache as shared state store
38
+ - Support for DRM encrypted HLS VODs (in beta)
38
39
  - And much more!
39
40
 
40
41
  ## System Requirements
@@ -45,7 +46,7 @@ Supported Node.js Versions
45
46
  | ------- | --------- |
46
47
  | 14.x | Yes |
47
48
  | 16.x | Yes |
48
- | 18.x | No |
49
+ | 18.x | Yes |
49
50
 
50
51
  ## Supported Source Formats
51
52
 
@@ -97,14 +98,7 @@ Read [CONTRIBUTING](CONTRIBUTING.md) first and then follow the instructions belo
97
98
 
98
99
  ### Development
99
100
 
100
- Ensure that you are running node version 16 as version 18 is not supported yet.
101
-
102
- ```
103
- node --version
104
- v16.17.0
105
- ```
106
-
107
- Then install the dependencies:
101
+ Install the dependencies:
108
102
 
109
103
  ```
110
104
  npm install
@@ -138,6 +132,7 @@ In addition there other reference implemetations that can be used:
138
132
  - `server-demux.ts` : example with demuxed audio
139
133
  - `server-livemix.ts` : example with live mimxing
140
134
  - `server-multicodec.ts` : example with multicodec
135
+ - `server-drm.ts` : example with DRM
141
136
 
142
137
  ### Source linking hls-vodtolive library
143
138
 
@@ -1403,7 +1403,7 @@ var Session = /** @class */ (function () {
1403
1403
  ",NAME=\"".concat(audioTrack.name, "\"") +
1404
1404
  ",AUTOSELECT=YES,DEFAULT=".concat(audioTrack.default ? 'YES' : 'NO') +
1405
1405
  ",CHANNELS=\"".concat(channels ? channels : 2, "\"") +
1406
- ",URI=\"master-".concat(audioGroupIdFileName, "_").concat(audioTrack.language, ".m3u8;session=").concat(this._sessionId, "\"") +
1406
+ ",URI=\"master-".concat(audioGroupIdFileName, "_").concat(audioTrack.language, ".m3u8%3Bsession=").concat(this._sessionId, "\"") +
1407
1407
  "\n";
1408
1408
  }
1409
1409
  }
@@ -1433,7 +1433,7 @@ var Session = /** @class */ (function () {
1433
1433
  ',CODECS="' + profile.codecs + '"' +
1434
1434
  ",AUDIO=\"".concat(audioGroupIdToUse, "\"") +
1435
1435
  (hasClosedCaptions ? ',CLOSED-CAPTIONS="cc"' : '') + '\n';
1436
- m3u8 += "master" + profile.bw + ".m3u8;session=" + _this._sessionId + "\n";
1436
+ m3u8 += "master" + profile.bw + ".m3u8%3Bsession=" + _this._sessionId + "\n";
1437
1437
  }
1438
1438
  }
1439
1439
  else {
@@ -1442,7 +1442,7 @@ var Session = /** @class */ (function () {
1442
1442
  ',CODECS="' + profile.codecs + '"' +
1443
1443
  (defaultAudioGroupId ? ",AUDIO=\"".concat(defaultAudioGroupId, "\"") : '') +
1444
1444
  (hasClosedCaptions ? ',CLOSED-CAPTIONS="cc"' : '') + '\n';
1445
- m3u8 += "master" + profile.bw + ".m3u8;session=" + _this._sessionId + "\n";
1445
+ m3u8 += "master" + profile.bw + ".m3u8%3Bsession=" + _this._sessionId + "\n";
1446
1446
  }
1447
1447
  });
1448
1448
  }
@@ -1453,7 +1453,7 @@ var Session = /** @class */ (function () {
1453
1453
  ',CODECS="' + profile.codecs + '"' +
1454
1454
  (defaultAudioGroupId ? ",AUDIO=\"".concat(defaultAudioGroupId, "\"") : '') +
1455
1455
  (hasClosedCaptions ? ',CLOSED-CAPTIONS="cc"' : '') + '\n';
1456
- m3u8 += "master" + profile.bw + ".m3u8;session=" + _this._sessionId + "\n";
1456
+ m3u8 += "master" + profile.bw + ".m3u8%3Bsession=" + _this._sessionId + "\n";
1457
1457
  });
1458
1458
  }
1459
1459
  this.produceEvent({
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.0.19",
3
+ "version": "4.1.0",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "start-livemix": "node dist/server-livemix.js"
15
15
  },
16
16
  "engines": {
17
- "node": "<18"
17
+ "node": ">=14 <20"
18
18
  },
19
19
  "author": "Eyevinn Technology AB <work@eyevinn.se>",
20
20
  "contributors": [
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@eyevinn/hls-repeat": "^0.2.0",
31
31
  "@eyevinn/hls-truncate": "^0.2.0",
32
- "@eyevinn/hls-vodtolive": "^2.3.9",
32
+ "@eyevinn/hls-vodtolive": "^2.3.10",
33
33
  "@eyevinn/m3u8": "^0.5.3",
34
34
  "abort-controller": "^3.0.0",
35
35
  "debug": "^3.2.7",
@@ -39,7 +39,7 @@
39
39
  "promise.allsettled": "^1.0.4",
40
40
  "redis": "3.1.0",
41
41
  "request": ">=2.88.0",
42
- "restify": "9.0.0",
42
+ "restify": "10.0.0",
43
43
  "restify-errors": "^8.0.2",
44
44
  "uuid": "^8.3.2"
45
45
  },
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,114 @@
1
+ "use strict";
2
+ /*
3
+ * Reference implementation of Channel Engine library using DRM (HLS+Widevine) VOD assets.
4
+ *
5
+ * Playback: https://shaka-player-demo.appspot.com/demo/#audiolang=sv-SE;textlang=sv-SE;uilang=sv-SE;asset=http://localhost:8000/channels/1/master.m3u8;license=https://cwip-shaka-proxy.appspot.com/no_auth;panel=CUSTOM%20CONTENT;build=uncompiled
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ var index_1 = require("./index");
9
+ var RefAssetManager = /** @class */ (function () {
10
+ function RefAssetManager(opts) {
11
+ this.assets = {
12
+ 1: [
13
+ {
14
+ id: 1,
15
+ title: "VINN DRM",
16
+ uri: "https://testcontent.eyevinn.technology/vinn/multidrm/index.m3u8"
17
+ // License server urL: https://widevine-dash.ezdrm.com/proxy?pX=1D331C
18
+ },
19
+ {
20
+ id: 2,
21
+ title: "VINN No DRM",
22
+ uri: "https://testcontent.eyevinn.technology/vinn/cmaf/index.m3u8"
23
+ },
24
+ {
25
+ id: 3,
26
+ title: "CE Promo DRM",
27
+ uri: "https://testcontent.eyevinn.technology/drm/CE-promo/index.m3u8"
28
+ // License server urL: https://widevine-dash.ezdrm.com/proxy?pX=1D331C
29
+ },
30
+ {
31
+ id: 4,
32
+ title: "Eyevinn Reel DRM",
33
+ uri: "https://testcontent.eyevinn.technology/drm/Eyevinn-Reel/index.m3u8"
34
+ // License server urL: https://widevine-dash.ezdrm.com/proxy?pX=1D331C
35
+ }
36
+ ],
37
+ };
38
+ this.pos = {
39
+ 1: 0,
40
+ };
41
+ }
42
+ /**
43
+ *
44
+ * @param {Object} vodRequest
45
+ * {
46
+ * sessionId,
47
+ * category,
48
+ * playlistId
49
+ * }
50
+ */
51
+ RefAssetManager.prototype.getNextVod = function (vodRequest) {
52
+ var _this = this;
53
+ return new Promise(function (resolve, reject) {
54
+ var channelId = vodRequest.playlistId;
55
+ if (_this.assets[channelId]) {
56
+ var vod = _this.assets[channelId][_this.pos[channelId]++];
57
+ if (_this.pos[channelId] > _this.assets[channelId].length - 1) {
58
+ _this.pos[channelId] = 0;
59
+ }
60
+ resolve(vod);
61
+ }
62
+ else {
63
+ reject("Invalid channelId provided");
64
+ }
65
+ });
66
+ };
67
+ return RefAssetManager;
68
+ }());
69
+ var RefChannelManager = /** @class */ (function () {
70
+ function RefChannelManager() {
71
+ }
72
+ RefChannelManager.prototype.getChannels = function () {
73
+ return [{ id: "1", profile: this._getProfile(), audioTracks: this._getAudioTracks() }];
74
+ };
75
+ RefChannelManager.prototype._getProfile = function () {
76
+ return [
77
+ {
78
+ bw: 7934000,
79
+ codecs: "avc1.4d001f,mp4a.40.2",
80
+ resolution: [2880, 1440],
81
+ },
82
+ {
83
+ bw: 7514000,
84
+ codecs: "avc1.4d001f,mp4a.40.2",
85
+ resolution: [1920, 1080],
86
+ },
87
+ { bw: 7134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1280, 720] },
88
+ { bw: 6134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1024, 458] },
89
+ { bw: 2323000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [640, 286] },
90
+ { bw: 495894, codecs: "avc1.4d001f,mp4a.40.2", resolution: [480, 214] },
91
+ ];
92
+ };
93
+ RefChannelManager.prototype._getAudioTracks = function () {
94
+ return [
95
+ { language: "en", name: "English" },
96
+ ];
97
+ };
98
+ return RefChannelManager;
99
+ }());
100
+ var refAssetManager = new RefAssetManager();
101
+ var refChannelManager = new RefChannelManager();
102
+ var engineOptions = {
103
+ heartbeat: "/",
104
+ averageSegmentDuration: 4000,
105
+ channelManager: refChannelManager,
106
+ defaultSlateUri: "https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
107
+ slateRepetitions: 10,
108
+ redisUrl: process.env.REDIS_URL,
109
+ useDemuxedAudio: true,
110
+ alwaysNewSegments: true,
111
+ };
112
+ var engine = new index_1.ChannelEngine(refAssetManager, engineOptions);
113
+ engine.start();
114
+ engine.listen(process.env.PORT || 8000);
package/docs/demosite.png CHANGED
Binary file
package/engine/session.js CHANGED
@@ -983,7 +983,7 @@ class Session {
983
983
  `,NAME="${audioTrack.name}"` +
984
984
  `,AUTOSELECT=YES,DEFAULT=${audioTrack.default ? 'YES' : 'NO'}` +
985
985
  `,CHANNELS="${channels ? channels : 2}"` +
986
- `,URI="master-${audioGroupIdFileName}_${audioTrack.language}.m3u8;session=${this._sessionId}"` +
986
+ `,URI="master-${audioGroupIdFileName}_${audioTrack.language}.m3u8%3Bsession=${this._sessionId}"` +
987
987
  "\n";
988
988
  }
989
989
  }
@@ -1015,7 +1015,7 @@ class Session {
1015
1015
  ',CODECS="' + profile.codecs + '"' +
1016
1016
  `,AUDIO="${audioGroupIdToUse}"` +
1017
1017
  (hasClosedCaptions ? ',CLOSED-CAPTIONS="cc"' : '') + '\n';
1018
- m3u8 += "master" + profile.bw + ".m3u8;session=" + this._sessionId + "\n";
1018
+ m3u8 += "master" + profile.bw + ".m3u8%3Bsession=" + this._sessionId + "\n";
1019
1019
  }
1020
1020
  } else {
1021
1021
  m3u8 += '#EXT-X-STREAM-INF:BANDWIDTH=' + profile.bw +
@@ -1023,7 +1023,7 @@ class Session {
1023
1023
  ',CODECS="' + profile.codecs + '"' +
1024
1024
  (defaultAudioGroupId ? `,AUDIO="${defaultAudioGroupId}"` : '') +
1025
1025
  (hasClosedCaptions ? ',CLOSED-CAPTIONS="cc"' : '') + '\n';
1026
- m3u8 += "master" + profile.bw + ".m3u8;session=" + this._sessionId + "\n";
1026
+ m3u8 += "master" + profile.bw + ".m3u8%3Bsession=" + this._sessionId + "\n";
1027
1027
  }
1028
1028
  });
1029
1029
  } else {
@@ -1033,7 +1033,7 @@ class Session {
1033
1033
  ',CODECS="' + profile.codecs + '"' +
1034
1034
  (defaultAudioGroupId ? `,AUDIO="${defaultAudioGroupId}"` : '') +
1035
1035
  (hasClosedCaptions ? ',CLOSED-CAPTIONS="cc"' : '') + '\n';
1036
- m3u8 += "master" + profile.bw + ".m3u8;session=" + this._sessionId + "\n";
1036
+ m3u8 += "master" + profile.bw + ".m3u8%3Bsession=" + this._sessionId + "\n";
1037
1037
  });
1038
1038
  }
1039
1039
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eyevinn-channel-engine",
3
- "version": "4.0.19",
3
+ "version": "4.1.0",
4
4
  "description": "OTT TV Channel Engine",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -14,7 +14,7 @@
14
14
  "start-livemix": "node dist/server-livemix.js"
15
15
  },
16
16
  "engines": {
17
- "node": "<18"
17
+ "node": ">=14 <20"
18
18
  },
19
19
  "author": "Eyevinn Technology AB <work@eyevinn.se>",
20
20
  "contributors": [
@@ -29,7 +29,7 @@
29
29
  "dependencies": {
30
30
  "@eyevinn/hls-repeat": "^0.2.0",
31
31
  "@eyevinn/hls-truncate": "^0.2.0",
32
- "@eyevinn/hls-vodtolive": "^2.3.9",
32
+ "@eyevinn/hls-vodtolive": "^2.3.10",
33
33
  "@eyevinn/m3u8": "^0.5.3",
34
34
  "abort-controller": "^3.0.0",
35
35
  "debug": "^3.2.7",
@@ -39,7 +39,7 @@
39
39
  "promise.allsettled": "^1.0.4",
40
40
  "redis": "3.1.0",
41
41
  "request": ">=2.88.0",
42
- "restify": "9.0.0",
42
+ "restify": "10.0.0",
43
43
  "restify-errors": "^8.0.2",
44
44
  "uuid": "^8.3.2"
45
45
  },
package/server-drm.ts ADDED
@@ -0,0 +1,126 @@
1
+ /*
2
+ * Reference implementation of Channel Engine library using DRM (HLS+Widevine) VOD assets.
3
+ *
4
+ * Playback: https://shaka-player-demo.appspot.com/demo/#audiolang=sv-SE;textlang=sv-SE;uilang=sv-SE;asset=http://localhost:8000/channels/1/master.m3u8;license=https://cwip-shaka-proxy.appspot.com/no_auth;panel=CUSTOM%20CONTENT;build=uncompiled
5
+ */
6
+
7
+ import {
8
+ ChannelEngine,
9
+ ChannelEngineOpts,
10
+ IAssetManager,
11
+ IChannelManager,
12
+ VodRequest,
13
+ VodResponse,
14
+ Channel,
15
+ ChannelProfile,
16
+ AudioTracks,
17
+ } from "./index";
18
+
19
+ class RefAssetManager implements IAssetManager {
20
+ private assets;
21
+ private pos;
22
+ constructor(opts?) {
23
+ this.assets = {
24
+ 1: [
25
+ {
26
+ id: 1,
27
+ title: "VINN DRM",
28
+ uri: "https://testcontent.eyevinn.technology/vinn/multidrm/index.m3u8"
29
+ // License server urL: https://widevine-dash.ezdrm.com/proxy?pX=1D331C
30
+ },
31
+ {
32
+ id: 2,
33
+ title: "VINN No DRM",
34
+ uri: "https://testcontent.eyevinn.technology/vinn/cmaf/index.m3u8"
35
+ },
36
+ {
37
+ id: 3,
38
+ title: "CE Promo DRM",
39
+ uri: "https://testcontent.eyevinn.technology/drm/CE-promo/index.m3u8"
40
+ // License server urL: https://widevine-dash.ezdrm.com/proxy?pX=1D331C
41
+ },
42
+ {
43
+ id: 4,
44
+ title: "Eyevinn Reel DRM",
45
+ uri: "https://testcontent.eyevinn.technology/drm/Eyevinn-Reel/index.m3u8"
46
+ // License server urL: https://widevine-dash.ezdrm.com/proxy?pX=1D331C
47
+ }
48
+ ],
49
+ };
50
+ this.pos = {
51
+ 1: 0,
52
+ };
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {Object} vodRequest
58
+ * {
59
+ * sessionId,
60
+ * category,
61
+ * playlistId
62
+ * }
63
+ */
64
+ getNextVod(vodRequest: VodRequest): Promise<VodResponse> {
65
+ return new Promise((resolve, reject) => {
66
+ const channelId = vodRequest.playlistId;
67
+ if (this.assets[channelId]) {
68
+ let vod: VodResponse = this.assets[channelId][this.pos[channelId]++];
69
+ if (this.pos[channelId] > this.assets[channelId].length - 1) {
70
+ this.pos[channelId] = 0;
71
+ }
72
+ resolve(vod);
73
+ } else {
74
+ reject("Invalid channelId provided");
75
+ }
76
+ });
77
+ }
78
+ }
79
+
80
+ class RefChannelManager implements IChannelManager {
81
+ getChannels(): Channel[] {
82
+ return [{ id: "1", profile: this._getProfile(), audioTracks: this._getAudioTracks() }];
83
+ }
84
+
85
+ _getProfile(): ChannelProfile[] {
86
+ return [
87
+ {
88
+ bw: 7934000,
89
+ codecs: "avc1.4d001f,mp4a.40.2",
90
+ resolution: [2880, 1440],
91
+ },
92
+ {
93
+ bw: 7514000,
94
+ codecs: "avc1.4d001f,mp4a.40.2",
95
+ resolution: [1920, 1080],
96
+ },
97
+ { bw: 7134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1280, 720] },
98
+ { bw: 6134000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [1024, 458] },
99
+ { bw: 2323000, codecs: "avc1.4d001f,mp4a.40.2", resolution: [640, 286] },
100
+ { bw: 495894, codecs: "avc1.4d001f,mp4a.40.2", resolution: [480, 214] },
101
+ ];
102
+ }
103
+ _getAudioTracks(): AudioTracks[] {
104
+ return [
105
+ { language: "en", name: "English" },
106
+ ];
107
+ }
108
+ }
109
+
110
+ const refAssetManager = new RefAssetManager();
111
+ const refChannelManager = new RefChannelManager();
112
+
113
+ const engineOptions: ChannelEngineOpts = {
114
+ heartbeat: "/",
115
+ averageSegmentDuration: 4000,
116
+ channelManager: refChannelManager,
117
+ defaultSlateUri: "https://maitv-vod.lab.eyevinn.technology/slate-consuo.mp4/master.m3u8",
118
+ slateRepetitions: 10,
119
+ redisUrl: process.env.REDIS_URL,
120
+ useDemuxedAudio: true,
121
+ alwaysNewSegments: true,
122
+ };
123
+
124
+ const engine = new ChannelEngine(refAssetManager, engineOptions);
125
+ engine.start();
126
+ engine.listen(process.env.PORT || 8000);
package/tsconfig.json CHANGED
@@ -6,5 +6,12 @@
6
6
  "esModuleInterop": true,
7
7
  "declaration": true
8
8
  },
9
- "include": ["./server.ts", "./server-demux.ts", "./server-livemix.ts", "./server-multicodec.ts", "./engine/**/*"]
9
+ "include": [
10
+ "./server.ts",
11
+ "./server-demux.ts",
12
+ "./server-livemix.ts",
13
+ "./server-multicodec.ts",
14
+ "./server-drm.ts",
15
+ "./engine/**/*"
16
+ ]
10
17
  }