Skip to content

Commit

Permalink
feat: option to remove cue tags in the source m3u8 before inserting ads
Browse files Browse the repository at this point in the history
  • Loading branch information
Nfrederiksen committed Apr 25, 2023
1 parent 5158ce4 commit e5c009e
Show file tree
Hide file tree
Showing 6 changed files with 193 additions and 1 deletion.
44 changes: 44 additions & 0 deletions index.js
Expand Up @@ -55,6 +55,7 @@ class HLSSpliceVod {
this.mergeBreaks = false; // Merge ad breaks at the same position into one single break
this.bumperDuration = null;
this.log = null;
this.clearCueTagsInSource = null;
this.logger = (str) => {
if (this.log) {
console.log(str);
Expand All @@ -77,7 +78,11 @@ class HLSSpliceVod {
if (options && options.log) {
this.log = options.log;
}
if (options && options.clearCueTagsInSource) {
this.clearCueTagsInSource = options.clearCueTagsInSource;
}
this.cmafMapUri = { video: {}, audio: {} };

}

loadMasterManifest(_injectMasterManifest, _injectMediaManifest, _injectAudioManifest) {
Expand Down Expand Up @@ -562,6 +567,20 @@ class HLSSpliceVod {
}
}
}
if (this.clearCueTagsInSource) {
for (let i = 0; i < this.playlists[bandwidth].items.PlaylistItem.length; i++) {
const plItem = this.playlists[bandwidth].items.PlaylistItem[i];
if (plItem.get("cuein")) {
plItem.set("cuein", false);
}
if (plItem.get("cueout")) {
plItem.set("cueout", null);
}
if (plItem.get("daterange")) {
plItem.attributes.attributes.daterange = null;
}
}
}
const targetDuration = this.playlists[bandwidth].get("targetDuration");
if (targetDuration > this.targetDuration) {
this.targetDuration = targetDuration;
Expand Down Expand Up @@ -612,6 +631,31 @@ class HLSSpliceVod {
if (map_uri && !map_uri.includes("http")) {
plItem.attributes.attributes["map-uri"] = this.baseUrl + map_uri;
}
if (this.clearCueTagsInSource) {
if (plItem.get("cuein")) {
plItem.set("cuein", null);
}
if (plItem.get("cueout")) {
plItem.set("cueout", null);
}
if (plItem.get("daterange")) {
plItem.attributes.attributes.daterange = null;
}
}
}
}
if (this.clearCueTagsInSource) {
for (let i = 0; i < this.playlistsAudio[group][lang].items.PlaylistItem.length; i++) {
let plItem = this.playlistsAudio[group][lang].items.PlaylistItem[i];
if (plItem.get("cuein")) {
plItem.set("cuein", null);
}
if (plItem.get("cueout")) {
plItem.set("cueout", null);
}
if (plItem.get("daterange")) {
plItem.attributes.attributes.daterange = null;
}
}
}
const targetDuration = this.playlistsAudio[group][lang].get("targetDuration");
Expand Down
47 changes: 46 additions & 1 deletion spec/hls_splice_spec.js
Expand Up @@ -599,6 +599,9 @@ describe("HLSSpliceVod with Demuxed Audio Tracks,", () => {
let mockMasterManifest;
let mockMediaManifest;
let mockAudioManifest;
let mockMasterManifest2;
let mockMediaManifest2;
let mockAudioManifest2;
let mockAdMasterManifest;
let mockAdMediaManifest;
let mockAdAudioManifest;
Expand Down Expand Up @@ -718,7 +721,21 @@ describe("HLSSpliceVod with Demuxed Audio Tracks,", () => {
};
mockAdAudioManifest5 = (g, l) => {
return fs.createReadStream(`testvectors/demux/ad5/index_${g}-${l}_a.m3u8`);
};
};
// MOCK VOD #9
mockMasterManifest2 = () => {
return fs.createReadStream("testvectors/hls2_cue/master.m3u8");
};
mockMediaManifest2 = (bw) => {
const bwmap = {
4497000: "0",
2497000: "1",
};
return fs.createReadStream(`testvectors/hls2_cue/index_${bwmap[bw]}_v.m3u8`);
};
mockAudioManifest2 = (g, l) => {
return fs.createReadStream(`testvectors/hls2_cue/index_${g}-${l}_a.m3u8`);
};
});

it("can prepend a baseurl on each segment", (done) => {
Expand Down Expand Up @@ -1561,6 +1578,34 @@ describe("HLSSpliceVod with Demuxed Audio Tracks,", () => {
done();
});
});

it("can clear out existing cue tags in source vod before adding inserting ad", (done) => {
const mockVod = new HLSSpliceVod("http://mock.com/mock.m3u8", { clearCueTagsInSource: 1 });
mockVod
.load(mockMasterManifest2, mockMediaManifest2, mockAudioManifest2)
.then(() => {
return mockVod.insertAdAt(
0,
"http://mock.com/ad/mockad.m3u8",
mockAdMasterManifest,
mockAdMediaManifest,
mockAdAudioManifest
);
})
.then(() => {
const m3u8 = mockVod.getMediaManifest(4497000);
let lines = m3u8.split("\n");
expect(lines[31]).toBe("segment5_0_av.ts")
expect(lines[32]).not.toBe("#EXT-X-CUE-IN")
expect(lines[33]).not.toBe(`#EXT-X-DATERANGE:ID="1-804",START-DATE="1970-01-01T00:13:24Z",PLANNED-DURATION="0",SCTE35-OUT="0xFC302000000000000000FFF00F05000000017FFFFE000000000000000000007A3D9BBD"`)
const m3u8Audio = mockVod.getAudioManifest("stereo", "en");
lines = m3u8Audio.split("\n");
expect(lines[31]).toBe("segment5_sen_a.ts")
expect(lines[32]).not.toBe("#EXT-X-CUE-IN")
expect(lines[33]).not.toBe(`#EXT-X-DATERANGE:ID="1-804",START-DATE="1970-01-01T00:13:24Z",PLANNED-DURATION="0",SCTE35-OUT="0xFC302000000000000000FFF00F05000000017FFFFE000000000000000000007A3D9BBD"`)
done();
});
});
});

describe("HLSSpliceVod with CMAF and Demuxed Audio Tracks,", () => {
Expand Down
32 changes: 32 additions & 0 deletions testvectors/hls2_cue/index_0_v.m3u8
@@ -0,0 +1,32 @@
#EXTM3U
#EXT-X-TARGETDURATION:9
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:9.000,
segment1_0_av.ts
#EXTINF:9.000,
segment2_0_av.ts
#EXTINF:9.000,
segment3_0_av.ts
#EXTINF:9.000,
segment4_0_av.ts
#EXTINF:9.000,
segment5_0_av.ts
## splice_insert(auto_return)
#EXT-X-DATERANGE:ID="1-804",START-DATE="1970-01-01T00:13:24Z",PLANNED-DURATION=0,SCTE35-OUT=0xFC302000000000000000FFF00F05000000017FFFFE000000000000000000007A3D9BBD
#EXT-X-CUE-OUT:0
#EXT-X-CUE-IN
#EXTINF:9.000,
segment6_0_av.ts
#EXTINF:9.000,
segment7_0_av.ts
#EXTINF:9.000,
segment8_0_av.ts
#EXTINF:9.000,
segment9_0_av.ts
#EXTINF:6.266,
segment10_0_av.ts
#EXT-X-ENDLIST
32 changes: 32 additions & 0 deletions testvectors/hls2_cue/index_1_v.m3u8
@@ -0,0 +1,32 @@
#EXTM3U
#EXT-X-TARGETDURATION:9
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
##EXT-X-MEDIA-SEQUENCE:1
#EXTINF:9.000,
segment1_1_av.ts
#EXTINF:9.000,
segment2_1_av.ts
#EXTINF:9.000,
segment3_1_av.ts
#EXTINF:9.000,
segment4_1_av.ts
#EXTINF:9.000,
segment5_1_av.ts
## splice_insert(auto_return)
#EXT-X-DATERANGE:ID="1-804",START-DATE="1970-01-01T00:13:24Z",PLANNED-DURATION=0,SCTE35-OUT=0xFC302000000000000000FFF00F05000000017FFFFE000000000000000000007A3D9BBD
#EXT-X-CUE-OUT:0
#EXT-X-CUE-IN
#EXTINF:9.000,
segment6_1_av.ts
#EXTINF:9.000,
segment7_1_av.ts
#EXTINF:9.000,
segment8_1_av.ts
#EXTINF:9.000,
segment9_1_av.ts
#EXTINF:6.266,
segment10_1_av.ts
#EXT-X-ENDLIST
32 changes: 32 additions & 0 deletions testvectors/hls2_cue/index_stereo-en_a.m3u8
@@ -0,0 +1,32 @@
#EXTM3U
#EXT-X-TARGETDURATION:9
#EXT-X-ALLOW-CACHE:YES
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-MEDIA-SEQUENCE:1
#EXTINF:9.000,
segment1_sen_a.ts
#EXTINF:9.000,
segment2_sen_a.ts
#EXTINF:9.000,
segment3_sen_a.ts
#EXTINF:9.000,
segment4_sen_a.ts
#EXTINF:9.000,
segment5_sen_a.ts
## splice_insert(auto_return)
#EXT-X-DATERANGE:ID="1-804",START-DATE="1970-01-01T00:13:24Z",PLANNED-DURATION=0,SCTE35-OUT=0xFC302000000000000000FFF00F05000000017FFFFE000000000000000000007A3D9BBD
#EXT-X-CUE-OUT:0
#EXT-X-CUE-IN
#EXTINF:9.000,
segment6_sen_a.ts
#EXTINF:9.000,
segment7_sen_a.ts
#EXTINF:9.000,
segment8_sen_a.ts
#EXTINF:9.000,
segment9_sen_a.ts
#EXTINF:6.266,
segment10_sen_a.ts
#EXT-X-ENDLIST
7 changes: 7 additions & 0 deletions testvectors/hls2_cue/master.m3u8
@@ -0,0 +1,7 @@
#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=4497000,RESOLUTION=1280x720,CODECS="avc1.77.30, mp4a.40.2",CLOSED-CAPTIONS=NONE,AUDIO="stereo"
index_0_v.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2497000,RESOLUTION=1024x576,CODECS="avc1.77.30, mp4a.40.2",CLOSED-CAPTIONS=NONE,AUDIO="stereo"
index_1_v.m3u8

#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="stereo",LANGUAGE="en",NAME="English",AUTOSELECT=NO,DEFAULT=NO,URI="index_stereo-en_a.m3u8"

0 comments on commit e5c009e

Please sign in to comment.