Skip to content

Commit

Permalink
Handle Interstitials with empty asset-lists (skip to resumption offse…
Browse files Browse the repository at this point in the history
…t or abutting interstitial)
  • Loading branch information
robwalch committed Oct 9, 2024
1 parent 83074c9 commit c8ee777
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 21 deletions.
38 changes: 17 additions & 21 deletions src/controller/interstitials-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`,
});
}
const assetListLength = interstitial.assetList.length;
if (assetListLength === 0) {
if (assetListLength === 0 && !interstitial.assetListResponse) {
// Waiting at end of primary content segment
// Expect setSchedulePosition to be called again once ASSET-LIST is loaded
this.log(`Waiting for ASSET-LIST to complete loading ${interstitial}`);
Expand All @@ -1064,29 +1064,25 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`,
// Update schedule and asset list position now that it can start
this.waitingItem = null;
this.playingItem = scheduledItem;
// Start Interstitial Playback

// If asset-list is empty or missing asset index, advance to next item
const assetItem = interstitial.assetList[assetListIndex];
if (!assetItem) {
const error = new Error(
`ASSET-LIST index ${assetListIndex} out of bounds [0-${
assetListLength - 1
}] ${interstitial}`,
);
const errorData: ErrorData = {
fatal: true,
type: ErrorTypes.OTHER_ERROR,
details: ErrorDetails.INTERSTITIAL_ASSET_ITEM_ERROR,
error,
};
this.handleAssetItemError(
errorData,
interstitial,
index,
assetListIndex,
error.message,
);
const nextItem = scheduleItems[index + 1];
const media = this.media;
if (
nextItem &&
media &&
!this.isInterstitial(nextItem) &&
media.currentTime < nextItem.start
) {
media.currentTime = this.timelinePos = nextItem.start;
}
this.advanceAfterAssetEnded(interstitial, index, assetListIndex || 0);
return;
}

// Start Interstitial Playback
if (!player) {
player = this.getAssetPlayer(assetItem.identifier);
}
Expand Down Expand Up @@ -1411,7 +1407,7 @@ MediaSource ${JSON.stringify(attachMediaSourceData)} from ${logFromSource}`,
interstitialEvents.forEach((event) => (event.appendInPlace = false));
}
this.log(
`Interstitial events (${
`INTERSTITIALS_UPDATED (${
interstitialEvents.length
}): ${interstitialEvents}
Schedule: ${scheduleItems.map((seg) => segmentToString(seg))}`,
Expand Down
77 changes: 77 additions & 0 deletions tests/unit/controller/interstitials-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1157,5 +1157,82 @@ fileSequence5.mp4`;
expect(insterstitials.bufferingIndex).to.equal(0, 'bufferingIndex');
expect(insterstitials.playingIndex).to.equal(0, 'playingIndex');
});

it('should handle empty asset-lists', function () {
const playlist = `#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:7
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PROGRAM-DATE-TIME:2024-02-23T15:00:00.000Z
#EXT-X-DATERANGE:ID="start",CLASS="com.apple.hls.interstitial",START-DATE="2024-02-23T15:00:00.000Z",DURATION=5,X-ASSET-LIST="https://example.com/empty.m3u8",X-RESUME-OFFSET=5
#EXT-X-MAP:URI="fileSequence0.mp4"
#EXTINF:5,
fileSequence1.mp4
#EXTINF:5,
fileSequence2.mp4
#EXTINF:5,
fileSequence3.mp4
#EXT-X-ENDLIST`;
const media = new MockMediaElement();
hls.attachMedia(media as unknown as HTMLMediaElement);
(hls as any).bufferController.media = media;
hls.trigger(Events.MEDIA_ATTACHED, {
media: media as unknown as HTMLMediaElement,
mediaSource: {} as any,
});

const details = setLoadedLevelDetails(playlist);
hls.trigger(Events.LEVEL_UPDATED, {
details,
level: 0,
});
const insterstitials = interstitialsController.interstitialsManager;
if (!insterstitials) {
expect(insterstitials, 'interstitialsManager').to.be.an('object');
return;
}
expect(insterstitials.events).is.an('array').which.has.lengthOf(1);
expect(insterstitials.schedule).is.an('array').which.has.lengthOf(2);
if (!insterstitials.events || !insterstitials.schedule) {
return;
}
const callsBeforeAttach = getTriggerCalls();
expect(callsBeforeAttach).to.deep.equal(
[
Events.MEDIA_ATTACHING,
Events.MEDIA_ATTACHED,
Events.LEVEL_UPDATED,
Events.INTERSTITIALS_UPDATED,
Events.ASSET_LIST_LOADING,
Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY,
Events.INTERSTITIAL_STARTED,
],
`Actual events before asset-list: ${callsBeforeAttach.join(', ')}`,
);
hls.trigger.resetHistory();
expect(insterstitials.bufferingIndex).to.equal(0, 'bufferingIndex');
expect(insterstitials.playingIndex).to.equal(0, 'playingIndex');

// Load empty asset-list
const interstitial = insterstitials.events[0];
interstitial.assetListResponse = { ASSETS: [] };
hls.trigger(Events.ASSET_LIST_LOADED, {
event: interstitial,
assetListResponse: interstitial.assetListResponse,
networkDetails: {},
});
const callsAfterAttach = getTriggerCalls();
expect(callsAfterAttach).to.deep.equal(
[
Events.ASSET_LIST_LOADED,
Events.INTERSTITIAL_ENDED,
Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY,
Events.INTERSTITIALS_PRIMARY_RESUMED,
],
`Actual events after asset-list: ${callsAfterAttach.join(', ')}`,
);
expect(insterstitials.bufferingIndex).to.equal(1, 'bufferingIndex');
expect(insterstitials.playingIndex).to.equal(1, 'playingIndex');
});
});
});

0 comments on commit c8ee777

Please sign in to comment.