Skip to content

Commit

Permalink
Support for additional HLS interstitials attributes (#14)
Browse files Browse the repository at this point in the history
* chore: update to latest m3u8 parser version

* feat: added support for resume offset, playout limit and snap
  • Loading branch information
birme committed Nov 10, 2021
1 parent cbfe8a8 commit 2b82021
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 14 deletions.
19 changes: 16 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,25 @@ class HLSSpliceVod {
});
}

insertInterstitialAt(offset, id, uri, isAssetList) {
insertInterstitialAt(offset, id, uri, isAssetList, opts) {
return new Promise((resolve, reject) => {
if (this.bumperDuration) {
offset = this.bumperDuration + offset;
}

let extraAttrs = "";
if (opts) {
if (opts.resumeOffset) {
extraAttrs += `,X-RESUME-OFFSET=${opts.resumeOffset / 1000}`;
}
if (opts.playoutLimit) {
extraAttrs += `,X-PLAYOUT-LIMIT=${opts.playoutLimit / 1000}`;
}
if (opts.snap === "IN" || opts.snap === "OUT") {
extraAttrs += `,X-SNAP="${opts.snap}"`;
}
}

const bandwidths = Object.keys(this.playlists);
for (let b = 0; b < bandwidths.length; b++) {
const bw = bandwidths[b];
Expand All @@ -169,10 +182,10 @@ class HLSSpliceVod {
let startDate = (new Date(0 + offset)).toISOString();
if (isAssetList) {
this.playlists[bw].items.PlaylistItem[i].set('daterange',
`ID=${id},CLASS="com.apple.hls.interstitial",START-DATE="${startDate}",X-ASSET-LIST="${uri}"`);
`ID=${id},CLASS="com.apple.hls.interstitial",START-DATE="${startDate}",X-ASSET-LIST="${uri}"${extraAttrs}`);
} else {
this.playlists[bw].items.PlaylistItem[i].set('daterange',
`ID=${id},CLASS="com.apple.hls.interstitial",START-DATE="${startDate}",X-ASSET-URI="${uri}"`);
`ID=${id},CLASS="com.apple.hls.interstitial",START-DATE="${startDate}",X-ASSET-URI="${uri}"${extraAttrs}`);
}
}
resolve();
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"nyc": "^15.0.1"
},
"dependencies": {
"@eyevinn/m3u8": ">=0.3.1",
"@eyevinn/m3u8": ">=0.4.2",
"request": "^2.88.2"
}
}
64 changes: 64 additions & 0 deletions spec/hls_splice_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,4 +428,68 @@ describe("HLSSpliceVod", () => {
done();
});
});

it("can insert interstitial with an asset uri and a resume offset", done => {
const mockVod = new HLSSpliceVod('http://mock.com/mock.m3u8');
mockVod.load(mockMasterManifest, mockMediaManifest)
.then(() => {
return mockVod.insertInterstitialAt(18000, "001", "http://mock.com/asseturi", false, {
resumeOffset: 10500,
});
})
.then(() => {
const m3u8 = mockVod.getMediaManifest(4497000);
const lines = m3u8.split('\n');
expect(lines[12]).toEqual('#EXT-X-DATERANGE:ID="001",CLASS="com.apple.hls.interstitial",START-DATE="1970-01-01T00:00:18.000Z",X-ASSET-URI="http://mock.com/asseturi",X-RESUME-OFFSET=10.5');
done();
});
});

it("can insert interstitial with an asset uri and a playout limit", done => {
const mockVod = new HLSSpliceVod('http://mock.com/mock.m3u8');
mockVod.load(mockMasterManifest, mockMediaManifest)
.then(() => {
return mockVod.insertInterstitialAt(18000, "001", "http://mock.com/asseturi", false, {
playoutLimit: 12500,
});
})
.then(() => {
const m3u8 = mockVod.getMediaManifest(4497000);
const lines = m3u8.split('\n');
expect(lines[12]).toEqual('#EXT-X-DATERANGE:ID="001",CLASS="com.apple.hls.interstitial",START-DATE="1970-01-01T00:00:18.000Z",X-ASSET-URI="http://mock.com/asseturi",X-PLAYOUT-LIMIT=12.5');
done();
});
});

it("can insert interstitial with an asset uri and a snap IN", done => {
const mockVod = new HLSSpliceVod('http://mock.com/mock.m3u8');
mockVod.load(mockMasterManifest, mockMediaManifest)
.then(() => {
return mockVod.insertInterstitialAt(18000, "001", "http://mock.com/asseturi", false, {
snap: "IN",
});
})
.then(() => {
const m3u8 = mockVod.getMediaManifest(4497000);
const lines = m3u8.split('\n');
expect(lines[12]).toEqual('#EXT-X-DATERANGE:ID="001",CLASS="com.apple.hls.interstitial",START-DATE="1970-01-01T00:00:18.000Z",X-ASSET-URI="http://mock.com/asseturi",X-SNAP="IN"');
done();
});
});

it("can insert interstitial with an asset uri and a snap OUT", done => {
const mockVod = new HLSSpliceVod('http://mock.com/mock.m3u8');
mockVod.load(mockMasterManifest, mockMediaManifest)
.then(() => {
return mockVod.insertInterstitialAt(18000, "001", "http://mock.com/asseturi", false, {
snap: "OUT",
});
})
.then(() => {
const m3u8 = mockVod.getMediaManifest(4497000);
const lines = m3u8.split('\n');
expect(lines[12]).toEqual('#EXT-X-DATERANGE:ID="001",CLASS="com.apple.hls.interstitial",START-DATE="1970-01-01T00:00:18.000Z",X-ASSET-URI="http://mock.com/asseturi",X-SNAP="OUT"');
done();
});
});
});
2 changes: 1 addition & 1 deletion spec/integration_tests_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe("HLSSpliceVod", () => {
})
.then(() => {
const mediaManifest = hlsVod.getMediaManifest(4928000);
expect(mediaManifest.match(/#EXT-X-CUE-OUT:DURATION=15\s+#EXTINF:10.8800,\s+https:\/\/maitv-vod.lab.eyevinn.technology\/ads\/apotea-15s.mp4\/2000\/2000-00000.ts/)).not.toBe(null);
expect(mediaManifest.match(/#EXT-X-CUE-OUT:DURATION=15.*\s+#EXTINF:10.8800,\s+https:\/\/maitv-vod.lab.eyevinn.technology\/ads\/apotea-15s.mp4\/2000\/2000-00000.ts/)).not.toBe(null);
expect(mediaManifest.match(/#EXT-X-TARGETDURATION:11/)).not.toBe(null);
const lines = mediaManifest.split("\n");
expect(lines[lines.length - 2]).toEqual("#EXT-X-ENDLIST");
Expand Down

0 comments on commit 2b82021

Please sign in to comment.