Skip to content

Commit

Permalink
feat: handle new partialStoreHLSVod option
Browse files Browse the repository at this point in the history
  • Loading branch information
Nfrederiksen committed Aug 11, 2023
1 parent 4619e56 commit 03bc945
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 25 deletions.
7 changes: 7 additions & 0 deletions engine/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export interface ChannelEngineOpts {
subtitleSliceEndpoint?: string;
useVTTSubtitles?: boolean;
alwaysNewSegments?: boolean;
partialStoreHLSVod?: boolean;
alwaysMapBandwidthByNearest?: boolean;
diffCompensationRate?: number;
staticDirectory?: string;
Expand Down Expand Up @@ -183,6 +184,7 @@ export class ChannelEngine {
private subtitleSliceEndpoint: string;
private useVTTSubtitles: boolean;
private alwaysNewSegments: boolean;
private partialStoreHLSVod: boolean;
private alwaysMapBandwidthByNearest: boolean;
private defaultSlateUri?: string;
private slateDuration?: number;
Expand Down Expand Up @@ -223,6 +225,10 @@ export class ChannelEngine {
if (options && options.alwaysNewSegments) {
this.alwaysNewSegments = true;
}
this.partialStoreHLSVod = false;
if (options && options.partialStoreHLSVod) {
this.partialStoreHLSVod = true;
}
this.alwaysMapBandwidthByNearest = false;
if (options && options.alwaysMapBandwidthByNearest) {
this.alwaysMapBandwidthByNearest = true;
Expand Down Expand Up @@ -460,6 +466,7 @@ export class ChannelEngine {
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
useVTTSubtitles: this.useVTTSubtitles,
alwaysNewSegments: options.alwaysNewSegments,
partialStoreHLSVod: options.partialStoreHLSVod,
alwaysMapBandwidthByNearest: options.alwaysMapBandwidthByNearest,
noSessionDataTags: options.noSessionDataTags,
playheadDiffThreshold: channel.options && channel.options.playheadDiffThreshold ? channel.options.playheadDiffThreshold : this.streamerOpts.defaultPlayheadDiffThreshold,
Expand Down
50 changes: 26 additions & 24 deletions engine/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,17 @@ class Session {
this.isAllowedToClearVodCache = null;
this.alwaysNewSegments = null;
this.alwaysMapBandwidthByNearest = null;
this.partialStoreHLSVod = null;
this.currentPlayheadRef = null;
if (config) {
if (config.alwaysNewSegments) {
this.alwaysNewSegments = config.alwaysNewSegments;
}

if (config.partialStoreHLSVod) {
this.partialStoreHLSVod = config.partialStoreHLSVod;
}

if (config.alwaysMapBandwidthByNearest) {
this.alwaysMapBandwidthByNearest = config.alwaysMapBandwidthByNearest;
}
Expand Down Expand Up @@ -637,7 +643,7 @@ class Session {
}
let currentVod = null;
const sessionState = await this._sessionState.getValues(["discSeqAudio", "vodMediaSeqAudio"]);
let playheadState = await this._playheadState.getValues(["mediaSeqAudio", "vodMediaSeqAudio"]);
let playheadState = await this._playheadState.getValues(["mediaSeqAudio", "vodMediaSeqAudio", "playheadRef"]);

if (playheadState.vodMediaSeqAudio > sessionState.vodMediaSeqAudio || (playheadState.vodMediaSeqAudio < sessionState.vodMediaSeqAudio && playheadState.mediaSeqAudio === this.prevMediaSeqOffset.audio)) {
const state = await this._sessionState.get("state");
Expand All @@ -658,14 +664,10 @@ class Session {
if (currentVod) {
// condition suggesting that a new vod should exist
if (playheadState.vodMediaSeqAudio < 2 || playheadState.mediaSeqAudio !== this.prevMediaSeqOffset.audio) {
const AGE_THRESH = this.averageSegmentDuration * 2;
let cacheAge = null;
if (this._sessionState.cache && this._sessionState.cache.currentVod.ts) {
cacheAge = Date.now() - this._sessionState.cache.currentVod.ts;
}
if (cacheAge !== null && cacheAge > AGE_THRESH) {
if (playheadState.playheadRef > this.currentPlayheadRef) {
await timer(500);
debug(`[${this._sessionId}]: While requesting audio manifest for ${audioGroupId}-${audioLanguage}, (mseq=${playheadState.vodMediaSeqAudio})(vod cache age=${cacheAge})`);
this.currentPlayheadRef = playheadState.playheadRef;
debug(`[${this._sessionId}]: While requesting audio manifest for ${audioGroupId}-${audioLanguage}, (mseq=${playheadState.vodMediaSeqAudio})`);
await this._sessionState.clearCurrentVodCache(); // force reading up from shared store
currentVod = await this._sessionState.getCurrentVod();
}
Expand Down Expand Up @@ -712,7 +714,7 @@ class Session {
}
let currentVod = null;
const sessionState = await this._sessionState.getValues(["discSeqSubtitle", "vodMediaSeqSubtitle"]);
let playheadState = await this._playheadState.getValues(["mediaSeqSubtitle", "vodMediaSeqSubtitle"]);
let playheadState = await this._playheadState.getValues(["mediaSeqSubtitle", "vodMediaSeqSubtitle", "playheadRef"]);

if (playheadState.vodMediaSeqSubtitle > sessionState.vodMediaSeqSubtitle || (playheadState.vodMediaSeqSubtitle < sessionState.vodMediaSeqSubtitle && playheadState.mediaSeqSubtitle === this.prevMediaSeqOffset.subtitle)) {
const state = await this._sessionState.get("state");
Expand All @@ -733,14 +735,10 @@ class Session {
if (currentVod) {
// condition suggesting that a new vod should exist
if (playheadState.vodMediaSeqSubtitle < 2 || playheadState.mediaSeqSubtitle !== this.prevMediaSeqOffset.subtitle) {
const AGE_THRESH = this.averageSegmentDuration * 2;
let cacheAge = null;
if (this._sessionState.cache && this._sessionState.cache.currentVod.ts) {
cacheAge = Date.now() - this._sessionState.cache.currentVod.ts;
}
if (cacheAge !== null && cacheAge > AGE_THRESH) {
if (playheadState.playheadRef > this.currentPlayheadRef) {
await timer(500);
debug(`[${this._sessionId}]: While requesting subtitle manifest for ${subtitleGroupId}-${subtitleLanguage}, (mseq=${playheadState.vodMediaSeqSubtitle})(vod cache age=${cacheAge})`);
this.currentPlayheadRef = playheadState.playheadRef;
debug(`[${this._sessionId}]: While requesting subtitle manifest for ${subtitleGroupId}-${subtitleLanguage}, (mseq=${playheadState.vodMediaSeqSubtitle})`);
await this._sessionState.clearCurrentVodCache(); // force reading up from shared store
currentVod = await this._sessionState.getCurrentVod();
}
Expand Down Expand Up @@ -1275,7 +1273,7 @@ class Session {
await this._sessionState.set("discSeq", sessionState.discSeq + lastDiscontinuity);
await this._sessionState.set("discSeqAudio", sessionState.discSeqAudio + lastDiscontinuityAudio);
await this._sessionState.set("slateCount", sessionState.slateCount + 1);
await this._playheadState.set("playheadRef", Date.now(), isLeader);
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);

cloudWatchLog(!this.cloudWatchLogging, 'engine-session', { event: 'slateInserted', channel: this._sessionId });

Expand Down Expand Up @@ -1331,7 +1329,8 @@ class Session {
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
shouldContainSubtitles: this.use_vtt_subtitles,
expectedSubtitleTracks: this._subtitleTracks,
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
skipSerializeMediaSequences: this.partialStoreHLSVod
};
newVod = new HLSVod(vodResponse.uri, [], vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
if (vodResponse.timedMetadata) {
Expand Down Expand Up @@ -1375,7 +1374,6 @@ class Session {
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
sessionState.vodMediaSeqSubtitle = await this._sessionState.set("vodMediaSeqSubtitle", 0);
await this._playheadState.set("playheadRef", Date.now(), isLeader);
this.produceEvent({
type: 'NOW_PLAYING',
data: {
Expand All @@ -1385,6 +1383,7 @@ class Session {
});
sessionState.state = await this._sessionState.set("state", SessionState.VOD_PLAYING);
sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
await this._sessionState.remove("nextVod");
return;
} else {
Expand Down Expand Up @@ -1508,7 +1507,8 @@ class Session {
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
shouldContainSubtitles: this.use_vtt_subtitles,
expectedSubtitleTracks: this._subtitleTracks,
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
skipSerializeMediaSequences: this.partialStoreHLSVod
};
newVod = new HLSVod(vodResponse.uri, null, vodResponse.unixTs, vodResponse.offset * 1000, m3u8Header(this._instanceId), hlsOpts);
if (vodResponse.timedMetadata) {
Expand Down Expand Up @@ -1560,7 +1560,7 @@ class Session {
currentVod = newVod;
debug(`[${this._sessionId}]: msequences=${currentVod.getLiveMediaSequencesCount()}; audio msequences=${currentVod.getLiveMediaSequencesCount("audio")}; subtitle msequences=${currentVod.getLiveMediaSequencesCount("subtitle")}`);
sessionState.currentVod = await this._sessionState.setCurrentVod(currentVod, { ttl: currentVod.getDuration() * 1000 });
await this._playheadState.set("playheadRef", Date.now(), isLeader);
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
sessionState.vodMediaSeqVideo = await this._sessionState.set("vodMediaSeqVideo", 0);
sessionState.vodMediaSeqAudio = await this._sessionState.set("vodMediaSeqAudio", 0);
sessionState.vodMediaSeqSubtitle = await this._sessionState.set("vodMediaSeqSubtitle", 0);
Expand Down Expand Up @@ -1675,7 +1675,7 @@ class Session {
await this._playheadState.set("vodMediaSeqVideo", 0, isLeader);
await this._playheadState.set("vodMediaSeqAudio", 0, isLeader);
await this._playheadState.set("vodMediaSeqSubtitle", 0, isLeader);
await this._playheadState.set("playheadRef", Date.now(), isLeader);
this.currentPlayheadRef = await this._playheadState.set("playheadRef", Date.now(), isLeader);
// 4) Log to debug and cloudwatch
debug(`[${this._sessionId}]: LEADER: Set new Reloaded VOD and vodMediaSeq counts in store.`);
debug(`[${this._sessionId}]: next VOD Reloaded (${currentVod.getDeltaTimes()})`);
Expand Down Expand Up @@ -1756,7 +1756,8 @@ class Session {
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
shouldContainSubtitles: this.use_vtt_subtitles,
expectedSubtitleTracks: this._subtitleTracks,
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
skipSerializeMediaSequences: this.partialStoreHLSVod
};
const timestamp = Date.now();
hlsVod = new HLSVod(this.slateUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
Expand Down Expand Up @@ -1860,7 +1861,8 @@ class Session {
subtitleSliceEndpoint: this.subtitleSliceEndpoint,
shouldContainSubtitles: this.use_vtt_subtitles,
expectedSubtitleTracks: this._subtitleTracks,
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest
alwaysMapBandwidthByNearest: this.alwaysMapBandwidthByNearest,
skipSerializeMediaSequences: this.partialStoreHLSVod
};
const timestamp = Date.now();
hlsVod = new HLSVod(nexVodUri, null, timestamp, null, m3u8Header(this._instanceId), hlsOpts);
Expand Down
14 changes: 13 additions & 1 deletion engine/session_state.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,18 @@ class SharedSessionState {
let hlsVod = null;
if (currentVod) {
if (this.store.isShared()) {
debug(`[${this.sessionId}]: reading ${currentVod.length} characters from shared store (${Date.now()} < ${this.cache.currentVod.ts + this.cache.currentVod.ttl})`);
const strToMB = (str) => {
const bytesPerCharacter = 2; // Assuming each character takes around 2 bytes in memory
const stringSizeBytes = str.length * bytesPerCharacter;
const sizeInMegabytes = stringSizeBytes / (1024 * 1024);
return sizeInMegabytes.toFixed(1);
}
debug(`[${this.sessionId}]: reading ${currentVod.length} characters or (${strToMB(currentVod)} MB) from shared store (${Date.now()} < ${this.cache.currentVod.ts + this.cache.currentVod.ttl})`);
hlsVod = new HLSVod();
hlsVod.fromJSON(currentVod);
if (hlsVod.skipSerializeMediaSequences) {
await hlsVod.generateMediaSequences();
}
} else {
hlsVod = currentVod;
}
Expand All @@ -83,6 +92,9 @@ class SharedSessionState {
const currentVod = await this.get("currentVod");
hlsVod = new HLSVod();
hlsVod.fromJSON(currentVod);
if (hlsVod.skipSerializeMediaSequences) {
await hlsVod.generateMediaSequences();
}
}
} else {
await this.set("currentVod", hlsVod);
Expand Down

0 comments on commit 03bc945

Please sign in to comment.