Skip to content

Commit

Permalink
Merge branch 'main' into ui-console-error
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Oct 25, 2023
2 parents a455901 + 8944ca9 commit 0d5776d
Show file tree
Hide file tree
Showing 19 changed files with 248 additions and 75 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Gil Gonen <gil.gonen@gmail.com>
Giorgio Gamberoni <giorgio.gamberoni@gmail.com>
Giuseppe Samela <giuseppe.samela@gmail.com>
Google Inc. <*@google.com>
Ivan Kohut <i.kohut@yahoo.com>
Itay Kinnrot <Itay.Kinnrot@Kaltura.com>
Jaeseok Lee <devsunb@gmail.com>
Jason Palmer <jason@jason-palmer.com>
Expand Down
1 change: 1 addition & 0 deletions CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Gil Gonen <gil.gonen@gmail.com>
Giorgio Gamberoni <giorgio.gamberoni@gmail.com>
Giuseppe Samela <giuseppe.samela@gmail.com>
Hichem Taoufik <hichem@code-it.fr>
Ivan Kohut <i.kohut@yahoo.com>
Itay Kinnrot <itay.kinnrot@kaltura.com>
Isaac Ramirez <isaac.ramirez.herrera@gmail.com>
Jacob Trimble <modmaker@google.com>
Expand Down
54 changes: 54 additions & 0 deletions docs/tutorials/ad_monetization.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,60 @@ other ad providers in v3.1+.
Please note that the current API is likely to undergo significant
changes as our support extends.

#### AWS Elemental MediaTailor Integration

Shaka Player provides an integration with the [AWS Elemental MediaTailor][].
We support Client Side, Server Side and overlays ad insertion.

[AWS Elemental MediaTailor]: https://aws.amazon.com/mediatailor/

All ad insertion experiences are available through the
{@linksource shaka.extern.IAdManager} object on the Player.

If you're not using Shaka's UI library, you will
also need to create a `<div>` over your video element to serve as an ad
container.

Start by initializing the server side logic.
With Shaka UI:

```js
const video = document.getElementById('video');
const ui = video['ui'];
const controls = video.ui.getControls();
// If you're using a non-UI build, this is the div you'll need to create
// for your layout.
const container = controls.getServerSideAdContainer();
const player = controls.getPlayer();
const netEngine = player.getNetworkingEngine();
const adManager = player.getAdManager();
adManager.initMediaTailor(container, netEngine, video);
```

Requesting a Client Side stream:

```js
const mediaTailorUrl = 'https://d305rncpy6ne2q.cloudfront.net/v1/session/94063eadf7d8c56e9e2edd84fdf897826a70d0df/SFP-MediaTailor-VOD-HLS-DASH/out/v1/b94f3611978f419985a18335bac9d9cb/ddb73bf548a44551a0059c346226445a/eaa5485198bf497284559efb8172425e/index.mpd';
const mediaTailorAdsParams = {
adsParams: {
assetid: 'test2',
podduration: '15',
},
};
const uri = await adManager.requestMediaTailorStream(mediaTailorUrl, mediaTailorAdsParams);
player.load(uri);
```

Requesting a Server Side stream:

```js
const mediaTailorUrl = 'https://ad391cc0d55b44c6a86d232548adc225.mediatailor.us-east-1.amazonaws.com/v1/session/d02fedbbc5a68596164208dd24e9b48aa60dadc7/singssai/master.m3u8';
const uri = await adManager.requestMediaTailorStream(mediaTailorUrl);
player.load(uri);
```

Note: overlays ad insertions is the same as server side.

#### IMA SDK Integration

Shaka Player provides an integration with the [Interactive Media Ads][] SDKs.
Expand Down
33 changes: 29 additions & 4 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,8 @@ shaka.hls.HlsParser = class {
const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
'EXT-X-DEFINE');

const mediaVariables = this.parseMediaVariables_(variablesTags);
const mediaVariables = this.parseMediaVariables_(
variablesTags, response.uri);

const stream = streamInfo.stream;

Expand Down Expand Up @@ -949,31 +950,50 @@ shaka.hls.HlsParser = class {
* @private
*/
parseMasterVariables_(tags) {
const queryParams = new goog.Uri(this.masterPlaylistUri_).getQueryData();
for (const variableTag of tags) {
const name = variableTag.getAttributeValue('NAME');
const value = variableTag.getAttributeValue('VALUE');
const queryParam = variableTag.getAttributeValue('QUERYPARAM');
if (name && value) {
if (!this.globalVariables_.has(name)) {
this.globalVariables_.set(name, value);
}
}
if (queryParam) {
const queryParamValue = queryParams.get(queryParam)[0];
if (queryParamValue && !this.globalVariables_.has(queryParamValue)) {
this.globalVariables_.set(queryParam, queryParamValue);
}
}
}
}

/**
* Get the variables of each variant tag, and store in a map.
* @param {!Array.<!shaka.hls.Tag>} tags Variant tags from the playlist.
* @param {string} uri Media playlist URI.
* @return {!Map.<string, string>}
* @private
*/
parseMediaVariables_(tags) {
parseMediaVariables_(tags, uri) {
const queryParams = new goog.Uri(uri).getQueryData();
const mediaVariables = new Map();
for (const variableTag of tags) {
const name = variableTag.getAttributeValue('NAME');
const value = variableTag.getAttributeValue('VALUE');
const queryParam = variableTag.getAttributeValue('QUERYPARAM');
const mediaImport = variableTag.getAttributeValue('IMPORT');
if (name && value) {
mediaVariables.set(name, value);
if (!mediaVariables.has(name)) {
mediaVariables.set(name, value);
}
}
if (queryParam) {
const queryParamValue = queryParams.get(queryParam)[0];
if (queryParamValue && !mediaVariables.has(queryParamValue)) {
mediaVariables.set(queryParam, queryParamValue);
}
}
if (mediaImport) {
const globalValue = this.globalVariables_.get(mediaImport);
Expand Down Expand Up @@ -1304,6 +1324,7 @@ shaka.hls.HlsParser = class {
if (!ignoreStream) {
const streamInfo =
this.createStreamInfoFromVariantTag_(tag, allCodecs, type);
streamInfo.stream.groupId = groupId;
res[streamInfo.stream.type] = [streamInfo];
}
return res;
Expand Down Expand Up @@ -1674,6 +1695,9 @@ shaka.hls.HlsParser = class {
verbatimMediaPlaylistUri, codecs, type, language, primary, name,
channelsCount, /* closedCaptions= */ null, characteristics, forced,
sampleRate, spatialAudio);
if (streamInfo.stream) {
streamInfo.stream.groupId = groupId;
}
if (this.groupIdToStreamInfosMap_.has(groupId)) {
this.groupIdToStreamInfosMap_.get(groupId).push(streamInfo);
} else {
Expand Down Expand Up @@ -2147,7 +2171,8 @@ shaka.hls.HlsParser = class {
const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
'EXT-X-DEFINE');

const mediaVariables = this.parseMediaVariables_(variablesTags);
const mediaVariables = this.parseMediaVariables_(
variablesTags, absoluteMediaPlaylistUri);

goog.asserts.assert(playlist.segments != null,
'Media playlist should have segments!');
Expand Down
33 changes: 20 additions & 13 deletions lib/media/adaptation_set.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ shaka.media.AdaptationSet = class {
* Variants that may be compatible with the root and should be added if
* compatible. If a candidate is not compatible, it will not end up in the
* adaptation set.
* @param {boolean=} compareCodecs
* @param {boolean=} enableAudioGroups
*/
constructor(root, candidates) {
constructor(root, candidates, compareCodecs = true,
enableAudioGroups = false) {
/** @private {shaka.extern.Variant} */
this.root_ = root;
/** @private {!Set.<shaka.extern.Variant>} */
Expand All @@ -37,17 +40,18 @@ shaka.media.AdaptationSet = class {
// are not compatible with the root, they will be rejected by |add|.
candidates = candidates || [];
for (const candidate of candidates) {
this.add(candidate);
this.add(candidate, compareCodecs, enableAudioGroups);
}
}

/**
* @param {shaka.extern.Variant} variant
* @param {boolean=} compareCodecs
* @param {boolean} compareCodecs
* @param {boolean} enableAudioGroups
* @return {boolean}
*/
add(variant, compareCodecs = true) {
if (this.canInclude(variant, compareCodecs)) {
add(variant, compareCodecs, enableAudioGroups) {
if (this.canInclude(variant, compareCodecs, enableAudioGroups)) {
this.variants_.add(variant);
return true;
}
Expand All @@ -64,20 +68,22 @@ shaka.media.AdaptationSet = class {
*
* @param {shaka.extern.Variant} variant
* @param {boolean=} compareCodecs
* @param {boolean=} enableAudioGroups
* @return {boolean}
*/
canInclude(variant, compareCodecs = true) {
canInclude(variant, compareCodecs = true, enableAudioGroups = false) {
return shaka.media.AdaptationSet
.areAdaptable(this.root_, variant, compareCodecs);
.areAdaptable(this.root_, variant, compareCodecs, enableAudioGroups);
}

/**
* @param {shaka.extern.Variant} a
* @param {shaka.extern.Variant} b
* @param {boolean=} compareCodecs
* @param {boolean} compareCodecs
* @param {boolean} enableAudioGroups
* @return {boolean}
*/
static areAdaptable(a, b, compareCodecs = true) {
static areAdaptable(a, b, compareCodecs, enableAudioGroups) {
const AdaptationSet = shaka.media.AdaptationSet;

// All variants should have audio or should all not have audio.
Expand All @@ -98,8 +104,8 @@ shaka.media.AdaptationSet = class {
goog.asserts.assert(
!!a.audio == !!b.audio,
'Both should either have audio or not have audio.');
if (a.audio && b.audio &&
!AdaptationSet.areAudiosCompatible_(a.audio, b.audio, compareCodecs)) {
if (a.audio && b.audio && !AdaptationSet.areAudiosCompatible_(
a.audio, b.audio, compareCodecs, enableAudioGroups)) {
return false;
}

Expand Down Expand Up @@ -127,10 +133,11 @@ shaka.media.AdaptationSet = class {
* @param {shaka.extern.Stream} a
* @param {shaka.extern.Stream} b
* @param {boolean} compareCodecs
* @param {boolean} enableAudioGroups
* @return {boolean}
* @private
*/
static areAudiosCompatible_(a, b, compareCodecs) {
static areAudiosCompatible_(a, b, compareCodecs, enableAudioGroups) {
const AdaptationSet = shaka.media.AdaptationSet;

// Don't adapt between channel counts, which could annoy the user
Expand All @@ -154,7 +161,7 @@ shaka.media.AdaptationSet = class {
}

// We can only adapt between the same groupId.
if (a.groupId !== b.groupId) {
if (enableAudioGroups && a.groupId !== b.groupId) {
return false;
}

Expand Down
30 changes: 17 additions & 13 deletions lib/media/adaptation_set_criteria.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,18 @@ shaka.media.ExampleBasedCriteria = class {
/**
* @param {shaka.extern.Variant} example
* @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
* @param {boolean=} enableAudioGroups
*/
constructor(example,
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD) {
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
enableAudioGroups = false) {
/** @private {shaka.extern.Variant} */
this.example_ = example;
/** @private {shaka.config.CodecSwitchingStrategy} */
this.codecSwitchingStrategy_ = codecSwitchingStrategy;
/** @private {boolean} */
this.enableAudioGroups_ = enableAudioGroups;


// We can't know if role and label are really important, so we don't use
// role and label for this.
Expand All @@ -63,7 +68,7 @@ shaka.media.ExampleBasedCriteria = class {
/** @private {!shaka.media.AdaptationSetCriteria} */
this.fallback_ = new shaka.media.PreferenceBasedCriteria(
example.language, role, channelCount, hdrLevel, label,
codecSwitchingStrategy);
codecSwitchingStrategy, enableAudioGroups);
}

/** @override */
Expand All @@ -75,14 +80,15 @@ shaka.media.ExampleBasedCriteria = class {
// actually be from another period.
const shortList = variants.filter((variant) => {
return shaka.media.AdaptationSet.areAdaptable(this.example_, variant,
!supportsSmoothCodecTransitions);
!supportsSmoothCodecTransitions, this.enableAudioGroups_);
});

if (shortList.length) {
// Use the first item in the short list as the root. It should not matter
// which element we use as all items in the short list should already be
// compatible.
return new shaka.media.AdaptationSet(shortList[0], shortList);
return new shaka.media.AdaptationSet(shortList[0], shortList,
!supportsSmoothCodecTransitions, this.enableAudioGroups_);
} else {
return this.fallback_.create(variants);
}
Expand All @@ -102,9 +108,11 @@ shaka.media.PreferenceBasedCriteria = class {
* @param {string} hdrLevel
* @param {string=} label
* @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
* @param {boolean=} enableAudioGroups
*/
constructor(language, role, channelCount, hdrLevel, label = '',
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD) {
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
enableAudioGroups = false) {
/** @private {string} */
this.language_ = language;
/** @private {string} */
Expand All @@ -117,6 +125,8 @@ shaka.media.PreferenceBasedCriteria = class {
this.label_ = label;
/** @private {shaka.config.CodecSwitchingStrategy} */
this.codecSwitchingStrategy_ = codecSwitchingStrategy;
/** @private {boolean} */
this.enableAudioGroups_ = enableAudioGroups;
}

/** @override */
Expand Down Expand Up @@ -181,14 +191,8 @@ shaka.media.PreferenceBasedCriteria = class {
shaka.config.CodecSwitchingStrategy.SMOOTH &&
shaka.media.Capabilities.isChangeTypeSupported();

// Make sure we only return a valid adaptation set.
const set = new shaka.media.AdaptationSet(current[0]);
for (const variant of current) {
// `add` checks combatability by calling `canInclude` internally.
set.add(variant, !supportsSmoothCodecTransitions);
}

return set;
return new shaka.media.AdaptationSet(current[0], current,
!supportsSmoothCodecTransitions, this.enableAudioGroups_);
}

/**
Expand Down
Loading

0 comments on commit 0d5776d

Please sign in to comment.