Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(DASH): MPD Alternate support #7055

Merged
merged 8 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions externs/shaka/ads.js
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,13 @@ shaka.extern.IAdManager = class extends EventTarget {
*/
onHLSInterstitialMetadata(basePlayer, baseVideo, interstitial) {}

/**
* @param {!shaka.Player} basePlayer
* @param {!HTMLMediaElement} baseVideo
* @param {shaka.extern.TimelineRegionInfo} region
*/
onDASHInterstitialMetadata(basePlayer, baseVideo, region) {}

/**
* @param {shaka.extern.AdInterstitial} interstitial
*/
Expand Down
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ module.exports = (config) => {
{pattern: 'test/test/assets/6339/*', included: false},
{pattern: 'test/test/assets/dash-aes-128/*', included: false},
{pattern: 'test/test/assets/dash-clearkey/*', included: false},
{pattern: 'test/test/assets/dash-mpd-alternate/*', included: false},
{pattern: 'test/test/assets/dash-vr/*', included: false},
{pattern: 'test/test/assets/dv-p8-hevc/*', included: false},
{pattern: 'test/test/assets/dv-p10-av1/*', included: false},
Expand Down
16 changes: 16 additions & 0 deletions lib/ads/ad_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,22 @@ shaka.ads.AdManager = class extends shaka.util.FakeEventTarget {
}
}

/**
* @override
* @export
*/
onDASHInterstitialMetadata(basePlayer, baseVideo, region) {
if (region.schemeIdUri != 'urn:mpeg:dash:event:alternativeMPD:2022') {
return;
}
if (!this.interstitialAdManager_) {
this.initInterstitial(/* adContainer= */ null, basePlayer, baseVideo);
}
if (this.interstitialAdManager_) {
this.interstitialAdManager_.addRegion(region);
}
}

/**
* @override
* @export
Expand Down
45 changes: 45 additions & 0 deletions lib/ads/interstitial_ad_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,51 @@ shaka.ads.InterstitialAdManager = class {
}
}

/**
* @param {shaka.extern.TimelineRegionInfo} region
*/
addRegion(region) {
let alternativeMPDUri;
let alternativeMPDmode;
for (const node of region.eventNode.children) {
if (node.tagName == 'AlternativeMPD') {
const uri = node.attributes['uri'];
const mode = node.attributes['mode'];
if (uri) {
alternativeMPDUri = uri;
alternativeMPDmode = mode;
break;
}
}
}
if (!alternativeMPDUri) {
shaka.log.alwaysWarn('Unsupported MPD alternate', region);
return;
}
const isReplace = alternativeMPDmode == 'replace';
const isInsert = alternativeMPDmode == 'insert';
if (!isReplace && !isInsert) {
shaka.log.warning('Unsupported MPD alternate', region);
return;
}
const interstitial = {
id: null,
startTime: region.startTime,
endTime: isReplace ? region.endTime : null,
uri: alternativeMPDUri,
isSkippable: false,
skipOffset: null,
canJump: true,
resumeOffset: isInsert ? 0 : null,
playoutLimit: null,
once: false,
pre: false,
post: false,
timelineRange: isReplace && !isInsert,
};
this.addInterstitials([interstitial]);
}

/**
* @param {string} url
* @return {!Promise}
Expand Down
3 changes: 3 additions & 0 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -2156,6 +2156,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
preloadManager.addQueuedOperation(false, () => {
if (this.adManager_) {
this.adManager_.onDashTimedMetadata(region);
goog.asserts.assert(this.video_, 'Must have video');
this.adManager_.onDASHInterstitialMetadata(
this, this.video_, region);
}
});
});
Expand Down
27 changes: 27 additions & 0 deletions test/ads_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -367,4 +367,31 @@ describe('Ads', () => {
await player.unload();
});
});

describe('support MPD Alternate', () => {
/** @type {string} */
const streamUri = '/base/test/test/assets/dash-mpd-alternate/dash.mpd';

it('without support for multiple media elements', async () => {
player.configure('ads.supportsMultipleMediaElements', false);

adManager.initInterstitial(adContainer, player, video);

await player.load(streamUri);
await video.play();
expect(player.isLive()).toBe(false);

// Wait a maximum of 10 seconds before the ad starts playing.
await waiter.timeoutAfter(10)
.waitForEvent(adManager, shaka.ads.Utils.AD_STARTED);
await waiter.timeoutAfter(20)
.waitForEvent(adManager, shaka.ads.Utils.AD_STOPPED);

// Play for 5 seconds, but stop early if the video ends. If it takes
// longer than 30 seconds, fail the test.
await waiter.waitUntilPlayheadReachesOrFailOnTimeout(video, 5, 30);

await player.unload();
});
});
});
Binary file added test/test/assets/dash-mpd-alternate/0001.m4s
Binary file not shown.
Binary file added test/test/assets/dash-mpd-alternate/0002.m4s
Binary file not shown.
Binary file added test/test/assets/dash-mpd-alternate/0003.m4s
Binary file not shown.
Binary file added test/test/assets/dash-mpd-alternate/0004.m4s
Binary file not shown.
Binary file added test/test/assets/dash-mpd-alternate/0005.m4s
Binary file not shown.
31 changes: 31 additions & 0 deletions test/test/assets/dash-mpd-alternate/dash.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Version information:
Axinom.MediaProcessing v3.0.0 targeting General Purpose Media Format specification v7
ffmpeg version N-81423-g61fac0e Copyright (c) 2000-2016 the FFmpeg developers
x265 [info]: HEVC encoder version 2.0+12-49a0d1176aef5bc6
x264 0.148.2705 3f5ed56
MP4Box - GPAC version 0.6.2-DEV-rev683-g7b29fbe-master
MediaInfoLib - v0.7.87

For more info about this video, see https://github.com/Axinom/dash-test-vectors
-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" type="static" mediaPresentationDuration="PT0H12M14.000S" maxSegmentDuration="PT0H0M4.000S" profiles="urn:mpeg:dash:profile:isoff-live:2011,http://dashif.org/guidelines/dash264" >
<Period duration="PT0H00M04.000S">
<AdaptationSet segmentAlignment="true" group="1" maxWidth="1920" maxHeight="1080" maxFrameRate="24" par="16:9" lang="und">
<SegmentTemplate timescale="24" media="$Number%04d$.m4s" startNumber="1" duration="96" initialization="init.mp4" />
<Representation id="1" mimeType="video/mp4" codecs="avc1.64001f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="389802"></Representation>
</AdaptationSet>
</Period>
<Period duration="PT0H00M16.000S">
<EventStream schemeIdUri="urn:mpeg:dash:event:alternativeMPD:2022" timescale="10000000">
<Event presentationTime="0" duration="40000000" id="1">
<AlternativeMPD uri="/base/test/test/assets/dash-aes-128/dash.mpd" mode="replace" />
</Event>
</EventStream>
<AdaptationSet segmentAlignment="true" group="1" maxWidth="1920" maxHeight="1080" maxFrameRate="24" par="16:9" lang="und">
<SegmentTemplate timescale="24" media="$Number%04d$.m4s" startNumber="1" duration="96" initialization="init.mp4" />
<Representation id="1" mimeType="video/mp4" codecs="avc1.64001f" width="512" height="288" frameRate="24" sar="1:1" startWithSAP="1" bandwidth="389802"></Representation>
</AdaptationSet>
</Period>
</MPD>
Binary file added test/test/assets/dash-mpd-alternate/init.mp4
Binary file not shown.
3 changes: 3 additions & 0 deletions test/test/util/fake_ad_manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ shaka.test.FakeAdManager = class extends shaka.util.FakeEventTarget {
/** @override */
onHLSInterstitialMetadata(basePlayer, baseVideo, interstitial) {}

/** @override */
onDASHInterstitialMetadata(basePlayer, baseVideo, region) {}

/** @override */
addCustomInterstitial(interstitial) {}

Expand Down
Loading