eyevinn-channel-engine 4.0.15 → 4.0.17
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/CONTRIBUTING.md +62 -0
- package/README.md +62 -0
- package/dist/engine/server.d.ts +1 -0
- package/dist/engine/session.js +5 -5
- package/dist/package.json +2 -2
- package/engine/server.ts +3 -0
- package/engine/session.js +5 -5
- package/package.json +2 -2
- package/spec/engine/init_switching_spec.js +10 -9
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Submitting Issues
|
|
2
|
+
|
|
3
|
+
We use GitHub issues to track public bugs. If you are submitting a bug, please provide
|
|
4
|
+
as much info as possible to make it easier to reproduce the issue and update unit tests.
|
|
5
|
+
|
|
6
|
+
# Contributing Code
|
|
7
|
+
|
|
8
|
+
This project uses [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary).
|
|
9
|
+
|
|
10
|
+
We follow the [GitHub Flow](https://guides.github.com/introduction/flow/index.html) so all contributions happen through pull requests. We actively welcome your pull requests:
|
|
11
|
+
|
|
12
|
+
1. Fork the repo and create your branch from master.
|
|
13
|
+
2. If you've added code that should be tested, add tests.
|
|
14
|
+
3. If you've changed APIs, update the documentation.
|
|
15
|
+
4. Ensure the test suite passes.
|
|
16
|
+
5. Issue that pull request!
|
|
17
|
+
|
|
18
|
+
When submitting code changes your submissions are understood to be under the same MIT License that covers the project. Feel free to contact Eyevinn Technology if that's a concern.
|
|
19
|
+
|
|
20
|
+
# Code of Conduct
|
|
21
|
+
|
|
22
|
+
## Our Pledge
|
|
23
|
+
|
|
24
|
+
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
|
25
|
+
|
|
26
|
+
## Our Standards
|
|
27
|
+
|
|
28
|
+
Examples of behavior that contributes to creating a positive environment include:
|
|
29
|
+
|
|
30
|
+
- Using welcoming and inclusive language
|
|
31
|
+
- Being respectful of differing viewpoints and experiences
|
|
32
|
+
- Gracefully accepting constructive criticism
|
|
33
|
+
- Focusing on what is best for the community
|
|
34
|
+
- Showing empathy towards other community members
|
|
35
|
+
|
|
36
|
+
Examples of unacceptable behavior by participants include:
|
|
37
|
+
|
|
38
|
+
- The use of sexualized language or imagery and unwelcome sexual attention or advances
|
|
39
|
+
- Trolling, insulting/derogatory comments, and personal or political attacks
|
|
40
|
+
- Public or private harassment
|
|
41
|
+
- Publishing others' private information, such as a physical or electronic address, without explicit permission
|
|
42
|
+
- Other conduct which could reasonably be considered inappropriate in a professional setting
|
|
43
|
+
|
|
44
|
+
## Our Responsibilities
|
|
45
|
+
|
|
46
|
+
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
|
47
|
+
|
|
48
|
+
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
|
49
|
+
|
|
50
|
+
## Scope
|
|
51
|
+
|
|
52
|
+
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
|
53
|
+
|
|
54
|
+
## Enforcement
|
|
55
|
+
|
|
56
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
|
57
|
+
|
|
58
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
|
59
|
+
|
|
60
|
+
## Attribution
|
|
61
|
+
|
|
62
|
+
This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4
|
package/README.md
CHANGED
|
@@ -91,6 +91,68 @@ and this means that it is not possible to mix muxed TS with demuxed CMAF.
|
|
|
91
91
|
|
|
92
92
|
A breaking change was introduced in v3.4.0 when Typescript types were introduced. The library no longer exports a default. This means that you need to change `const ChannelEngine = require('eyevinn-channel-engine')` to `const { ChannelEngine } = require('eyevinn-channel-engine')`;
|
|
93
93
|
|
|
94
|
+
## Contributing
|
|
95
|
+
|
|
96
|
+
Read [CONTRIBUTING](CONTRIBUTING.md) first and then follow the instructions below to get started with the devolpment environment.
|
|
97
|
+
|
|
98
|
+
### Development
|
|
99
|
+
|
|
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:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
npm install
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Build the app:
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
npm run build
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
To start the reference implementation in `server.ts` run:
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
npm start
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
For full debug mode you set the environment variable `DEBUG`.
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
DEBUG=* npm start
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
or if you want be more specific for example:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
DEBUG=engine-* npm start
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
In addition there other reference implemetations that can be used:
|
|
138
|
+
- `server-demux.ts` : example with demuxed audio
|
|
139
|
+
- `server-livemix.ts` : example with live mimxing
|
|
140
|
+
- `server-multicodec.ts` : example with multicodec
|
|
141
|
+
|
|
142
|
+
### Source linking hls-vodtolive library
|
|
143
|
+
|
|
144
|
+
It is not uncommon that you need to test an local branch of the hls-vodtolive library and in order to do that you can use `npm link`.
|
|
145
|
+
|
|
146
|
+
1. Go to the directory where you have the source code for the hls-vodtolive library and run `npm link`
|
|
147
|
+
2. Then go to the channel engine source code directory and run `npm link @eyevinn/hls-vodtolive`
|
|
148
|
+
|
|
149
|
+
To restore and use the npm published version run the following in the channel engine source code directory:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
npm uninstall --no-save @eyevinn/hls-vodtolive
|
|
153
|
+
npm install
|
|
154
|
+
```
|
|
155
|
+
|
|
94
156
|
## Support
|
|
95
157
|
|
|
96
158
|
Join our [community on Slack](http://slack.streamingtech.se) where you can post any questions regarding any of our open source projects. Eyevinn's consulting business can also offer you:
|
package/dist/engine/server.d.ts
CHANGED
package/dist/engine/session.js
CHANGED
|
@@ -1040,7 +1040,7 @@ var Session = /** @class */ (function () {
|
|
|
1040
1040
|
case 20:
|
|
1041
1041
|
audioPosition = (_o.sent()) * 1000;
|
|
1042
1042
|
posDiff = position - audioPosition;
|
|
1043
|
-
if (posDiff
|
|
1043
|
+
if (posDiff <= 0) {
|
|
1044
1044
|
return [3 /*break*/, 22];
|
|
1045
1045
|
}
|
|
1046
1046
|
if (posDiff > thresh) {
|
|
@@ -1628,7 +1628,7 @@ var Session = /** @class */ (function () {
|
|
|
1628
1628
|
if (!vodResponse.type) {
|
|
1629
1629
|
debug("[".concat(this._sessionId, "]: got first VOD uri=").concat(vodResponse.uri, ":").concat(vodResponse.offset || 0));
|
|
1630
1630
|
hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
|
|
1631
|
-
newVod = new HLSVod(vodResponse.uri, [],
|
|
1631
|
+
newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
|
|
1632
1632
|
if (vodResponse.timedMetadata) {
|
|
1633
1633
|
Object.keys(vodResponse.timedMetadata).map(function (k) {
|
|
1634
1634
|
newVod.addMetadata(k, vodResponse.timedMetadata[k]);
|
|
@@ -1836,7 +1836,7 @@ var Session = /** @class */ (function () {
|
|
|
1836
1836
|
if (!vodResponse.type) {
|
|
1837
1837
|
debug("[".concat(this._sessionId, "]: got next VOD uri=").concat(vodResponse.uri, ":").concat(vodResponse.offset));
|
|
1838
1838
|
hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
|
|
1839
|
-
newVod = new HLSVod(vodResponse.uri, null,
|
|
1839
|
+
newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
|
|
1840
1840
|
if (vodResponse.timedMetadata) {
|
|
1841
1841
|
Object.keys(vodResponse.timedMetadata).map(function (k) {
|
|
1842
1842
|
newVod.addMetadata(k, vodResponse.timedMetadata[k]);
|
|
@@ -2147,8 +2147,8 @@ var Session = /** @class */ (function () {
|
|
|
2147
2147
|
slateVod_1.load()
|
|
2148
2148
|
.then(function () {
|
|
2149
2149
|
var hlsOpts = { sequenceAlwaysContainNewSegments: _this.alwaysNewSegments, forcedDemuxMode: _this.use_demuxed_audio };
|
|
2150
|
-
hlsVod_1 = new HLSVod(_this.slateUri, null, null, null, m3u8Header(_this._instanceId), hlsOpts);
|
|
2151
2150
|
var timestamp = Date.now();
|
|
2151
|
+
hlsVod_1 = new HLSVod(_this.slateUri, null, timestamp, null, m3u8Header(_this._instanceId), hlsOpts);
|
|
2152
2152
|
hlsVod_1.addMetadata('id', "slate-".concat(timestamp));
|
|
2153
2153
|
hlsVod_1.addMetadata('start-date', new Date(timestamp).toISOString());
|
|
2154
2154
|
hlsVod_1.addMetadata('planned-duration', ((reps || _this.slateRepetitions) * _this.slateDuration) / 1000);
|
|
@@ -2210,8 +2210,8 @@ var Session = /** @class */ (function () {
|
|
|
2210
2210
|
slateVod_2.load()
|
|
2211
2211
|
.then(function () {
|
|
2212
2212
|
var hlsOpts = { sequenceAlwaysContainNewSegments: _this.alwaysNewSegments, forcedDemuxMode: _this.use_demuxed_audio };
|
|
2213
|
-
hlsVod_2 = new HLSVod(nexVodUri, null, null, null, m3u8Header(_this._instanceId), hlsOpts);
|
|
2214
2213
|
var timestamp = Date.now();
|
|
2214
|
+
hlsVod_2 = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(_this._instanceId), hlsOpts);
|
|
2215
2215
|
hlsVod_2.addMetadata('id', "slate-".concat(timestamp));
|
|
2216
2216
|
hlsVod_2.addMetadata('start-date', new Date(timestamp).toISOString());
|
|
2217
2217
|
hlsVod_2.addMetadata('planned-duration', requestedDuration);
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.17",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@eyevinn/hls-repeat": "^0.2.0",
|
|
28
28
|
"@eyevinn/hls-truncate": "^0.2.0",
|
|
29
|
-
"@eyevinn/hls-vodtolive": "^2.3.
|
|
29
|
+
"@eyevinn/hls-vodtolive": "^2.3.7",
|
|
30
30
|
"@eyevinn/m3u8": "^0.5.3",
|
|
31
31
|
"abort-controller": "^3.0.0",
|
|
32
32
|
"debug": "^3.2.7",
|
package/engine/server.ts
CHANGED
|
@@ -89,6 +89,9 @@ export interface VodResponse {
|
|
|
89
89
|
type?: string;
|
|
90
90
|
currentMetadata?: VodResponseMetadata;
|
|
91
91
|
timedMetadata?: VodTimedMetadata;
|
|
92
|
+
// Wall-clock start time of the VOD as unix ts. If set
|
|
93
|
+
// the program-date-time will be added to each segment
|
|
94
|
+
unixTs?: number;
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
export interface IAssetManager {
|
package/engine/session.js
CHANGED
|
@@ -738,7 +738,7 @@ class Session {
|
|
|
738
738
|
do {
|
|
739
739
|
const audioPosition = (await this._getAudioPlayheadPosition(sessionState.vodMediaSeqAudio + index)) * 1000;
|
|
740
740
|
posDiff = position - audioPosition;
|
|
741
|
-
if (posDiff
|
|
741
|
+
if (posDiff <= 0) {
|
|
742
742
|
break;
|
|
743
743
|
}
|
|
744
744
|
if (posDiff > thresh) {
|
|
@@ -1152,7 +1152,7 @@ class Session {
|
|
|
1152
1152
|
if (!vodResponse.type) {
|
|
1153
1153
|
debug(`[${this._sessionId}]: got first VOD uri=${vodResponse.uri}:${vodResponse.offset || 0}`);
|
|
1154
1154
|
const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
|
|
1155
|
-
newVod = new HLSVod(vodResponse.uri, [],
|
|
1155
|
+
newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
|
|
1156
1156
|
if (vodResponse.timedMetadata) {
|
|
1157
1157
|
Object.keys(vodResponse.timedMetadata).map(k => {
|
|
1158
1158
|
newVod.addMetadata(k, vodResponse.timedMetadata[k]);
|
|
@@ -1301,7 +1301,7 @@ class Session {
|
|
|
1301
1301
|
if (!vodResponse.type) {
|
|
1302
1302
|
debug(`[${this._sessionId}]: got next VOD uri=${vodResponse.uri}:${vodResponse.offset}`);
|
|
1303
1303
|
const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
|
|
1304
|
-
newVod = new HLSVod(vodResponse.uri, null,
|
|
1304
|
+
newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
|
|
1305
1305
|
if (vodResponse.timedMetadata) {
|
|
1306
1306
|
Object.keys(vodResponse.timedMetadata).map(k => {
|
|
1307
1307
|
newVod.addMetadata(k, vodResponse.timedMetadata[k]);
|
|
@@ -1532,8 +1532,8 @@ class Session {
|
|
|
1532
1532
|
slateVod.load()
|
|
1533
1533
|
.then(() => {
|
|
1534
1534
|
const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
|
|
1535
|
-
hlsVod = new HLSVod(this.slateUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
|
|
1536
1535
|
const timestamp = Date.now();
|
|
1536
|
+
hlsVod = new HLSVod(this.slateUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
|
|
1537
1537
|
hlsVod.addMetadata('id', `slate-${timestamp}`);
|
|
1538
1538
|
hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
|
|
1539
1539
|
hlsVod.addMetadata('planned-duration', ((reps || this.slateRepetitions) * this.slateDuration) / 1000);
|
|
@@ -1592,8 +1592,8 @@ class Session {
|
|
|
1592
1592
|
slateVod.load()
|
|
1593
1593
|
.then(() => {
|
|
1594
1594
|
const hlsOpts = { sequenceAlwaysContainNewSegments: this.alwaysNewSegments, forcedDemuxMode: this.use_demuxed_audio };
|
|
1595
|
-
hlsVod = new HLSVod(nexVodUri, null, null, null, m3u8Header(this._instanceId), hlsOpts);
|
|
1596
1595
|
const timestamp = Date.now();
|
|
1596
|
+
hlsVod = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
|
|
1597
1597
|
hlsVod.addMetadata('id', `slate-${timestamp}`);
|
|
1598
1598
|
hlsVod.addMetadata('start-date', new Date(timestamp).toISOString());
|
|
1599
1599
|
hlsVod.addMetadata('planned-duration', requestedDuration);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eyevinn-channel-engine",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.17",
|
|
4
4
|
"description": "OTT TV Channel Engine",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@eyevinn/hls-repeat": "^0.2.0",
|
|
28
28
|
"@eyevinn/hls-truncate": "^0.2.0",
|
|
29
|
-
"@eyevinn/hls-vodtolive": "^2.3.
|
|
29
|
+
"@eyevinn/hls-vodtolive": "^2.3.7",
|
|
30
30
|
"@eyevinn/m3u8": "^0.5.3",
|
|
31
31
|
"abort-controller": "^3.0.0",
|
|
32
32
|
"debug": "^3.2.7",
|
|
@@ -468,6 +468,7 @@ describe("The initialize switching", () => {
|
|
|
468
468
|
if (m) {
|
|
469
469
|
mseqNo = Number(m[1]);
|
|
470
470
|
}
|
|
471
|
+
|
|
471
472
|
expect(mseqNo).toBe(10);
|
|
472
473
|
const size = sessionCurrentSegs["1313000"].length;
|
|
473
474
|
expect(sessionCurrentSegs["1313000"][0]).toEqual({
|
|
@@ -484,7 +485,7 @@ describe("The initialize switching", () => {
|
|
|
484
485
|
const borderSegment = sessionCurrentSegs["1313000"][size - 1 - 3];
|
|
485
486
|
expect(borderSegment.duration).toBe(10.880);
|
|
486
487
|
expect(borderSegment.cue).toBe(null);
|
|
487
|
-
expect(borderSegment.timelinePosition).toBe(null);
|
|
488
|
+
expect(borderSegment.timelinePosition).not.toBe(null);
|
|
488
489
|
expect(borderSegment.uri).toBe("https://maitv-vod.lab.eyevinn.technology/MORBIUS_Trailer_2020.mp4/1000/1000-00000.ts");
|
|
489
490
|
expect(borderSegment.daterange["id"]).not.toBe(null);
|
|
490
491
|
expect(borderSegment.daterange["start-date"]).not.toBe(null);
|
|
@@ -768,13 +769,11 @@ describe("The initialize switching", () => {
|
|
|
768
769
|
}
|
|
769
770
|
|
|
770
771
|
expect(newVodSegments["1313000"][0]).toEqual(expectedLastVODSegItem);
|
|
771
|
-
expect(newVodSegments["1313000"][1]).toEqual(
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
byteRange: undefined,
|
|
777
|
-
});
|
|
772
|
+
expect(newVodSegments["1313000"][1].duration).toEqual(10.846444);
|
|
773
|
+
expect(newVodSegments["1313000"][1].cue).toBeNull();
|
|
774
|
+
expect(newVodSegments["1313000"][1].uri).toEqual('https://maitv-vod.lab.eyevinn.technology/VINN.mp4/600/600-00000.ts');
|
|
775
|
+
|
|
776
|
+
newVodSegments["1313000"][2].timelinePosition = null; // disregard timeline position
|
|
778
777
|
expect(newVodSegments["1313000"][2]).toEqual(expectedFirstV2LSegItem);
|
|
779
778
|
// expect(newVodSegments["1313000"][5]).toEqual({
|
|
780
779
|
// discontinuity: true,
|
|
@@ -784,9 +783,11 @@ describe("The initialize switching", () => {
|
|
|
784
783
|
expect("start-date" in borderSegment.daterange).toBe(true);
|
|
785
784
|
expect("id" in borderSegment.daterange).toBe(true);
|
|
786
785
|
expect(borderSegment.duration).toEqual(11.3447);
|
|
787
|
-
expect(borderSegment.timelinePosition).toEqual(null);
|
|
786
|
+
expect(borderSegment.timelinePosition).not.toEqual(null);
|
|
788
787
|
expect(borderSegment.cue).toEqual(null);
|
|
789
788
|
expect(borderSegment.uri).toEqual("https://maitv-vod.lab.eyevinn.technology/BECKY_Trailer_2020.mp4/1000/1000-00000.ts");
|
|
789
|
+
|
|
790
|
+
newVodSegments["1313000"][7].timelinePosition = null; // disregard timeline position
|
|
790
791
|
expect(newVodSegments["1313000"][7]).toEqual(expectedSecondVODSegItem);
|
|
791
792
|
});
|
|
792
793
|
|