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(Mcap): Add config for media capabilities cache #6615

Closed
Closed
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
4 changes: 3 additions & 1 deletion demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,9 @@ shakaDemo.Config = class {
'drm.parseInbandPsshEnabled')
.addTextInput_('Min HDCP version', 'drm.minHdcpVersion')
.addBoolInput_('Ignore duplicate init data',
'drm.ignoreDuplicateInitData');
'drm.ignoreDuplicateInitData')
.addBoolInput_('Enable cache for mediacapabilities',
'drm.enableMediaCapabilitiesCache');
const advanced = shakaDemoMain.getConfiguration().drm.advanced || {};
const addDRMAdvancedField = (name, valueName, suggestions) => {
// All advanced fields of a given type are set at once.
Expand Down
7 changes: 6 additions & 1 deletion externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ shaka.extern.PersistentSessionMetadata;
* keySystemsMapping: !Object.<string, string>,
* parseInbandPsshEnabled: boolean,
* minHdcpVersion: string,
* ignoreDuplicateInitData: boolean
* ignoreDuplicateInitData: boolean,
* enableMediaCapabilitiesCache: boolean
* }}
*
* @property {shaka.extern.RetryParameters} retryParameters
Expand Down Expand Up @@ -865,6 +866,10 @@ shaka.extern.PersistentSessionMetadata;
* Note: Tizen 2015 and 2016 models will send multiple webkitneedkey events
* with the same init data. If the duplicates are supressed, playback
* will stall without errors.
* @property {boolean} enableMediaCapabilitiesCache
* Custom optimization, enable aggressive cache on mediaCapabilities call.
* Should only be enabled on platform where polyfill for mediaCapabilities
* is used.
* @exportDoc
*/
shaka.extern.DrmConfiguration;
Expand Down
3 changes: 2 additions & 1 deletion lib/media/drm_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,8 @@ shaka.media.DrmEngine = class {
// in the drm infos, and before queryMediaKeys_().
await shaka.util.StreamUtils.getDecodingInfosForVariants(variants,
this.usePersistentLicenses_, this.srcEquals_,
this.config_.preferredKeySystems);
this.config_.preferredKeySystems,
this.config_.enableMediaCapabilitiesCache);

const hasDrmInfo = hadDrmInfo || Object.keys(this.config_.servers).length;
// An unencrypted content is initialized.
Expand Down
4 changes: 2 additions & 2 deletions lib/media/manifest_filterer.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,11 @@ shaka.media.ManifestFilterer = class {
async filterManifestWithStreamUtils_(manifest) {
goog.asserts.assert(manifest, 'Manifest should exist!');
await shaka.util.StreamUtils.filterManifest(this.drmEngine_, manifest,
this.config_.drm.preferredKeySystems);
this.config_.drm.preferredKeySystems,
this.config_.drm.enableMediaCapabilitiesCache);
this.checkPlayableVariants_(manifest);
}


/**
* @param {?shaka.extern.Manifest} manifest
* @return {boolean} tracksChanged
Expand Down
1 change: 1 addition & 0 deletions lib/util/player_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ shaka.util.PlayerConfiguration = class {
parseInbandPsshEnabled: shaka.util.Platform.isXboxOne(),
minHdcpVersion: '',
ignoreDuplicateInitData: !shaka.util.Platform.isTizen2(),
enableMediaCapabilitiesCache: false,
};

// The Xbox One and PS4 only support the Playready DRM, so they should
Expand Down
136 changes: 113 additions & 23 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,13 @@ shaka.util.StreamUtils = class {
* @param {shaka.media.DrmEngine} drmEngine
* @param {shaka.extern.Manifest} manifest
* @param {!Array<string>=} preferredKeySystems
* @param {boolean} enableMediaCapabilitiesCache
*/
static async filterManifest(drmEngine, manifest, preferredKeySystems = []) {
static async filterManifest(drmEngine, manifest,
preferredKeySystems = [], enableMediaCapabilitiesCache = false) {
await shaka.util.StreamUtils.filterManifestByMediaCapabilities(
drmEngine, manifest, manifest.offlineSessionIds.length > 0,
preferredKeySystems);
preferredKeySystems, enableMediaCapabilitiesCache);
shaka.util.StreamUtils.filterTextStreams_(manifest);
await shaka.util.StreamUtils.filterImageStreams_(manifest);
}
Expand All @@ -366,15 +368,17 @@ shaka.util.StreamUtils = class {
* @param {shaka.extern.Manifest} manifest
* @param {boolean} usePersistentLicenses
* @param {!Array<string>} preferredKeySystems
* @param {boolean} enableMediaCapabilitiesCache
*/
static async filterManifestByMediaCapabilities(
drmEngine, manifest, usePersistentLicenses, preferredKeySystems) {
drmEngine, manifest, usePersistentLicenses,
preferredKeySystems, enableMediaCapabilitiesCache = false) {
goog.asserts.assert(navigator.mediaCapabilities,
'MediaCapabilities should be valid.');

await shaka.util.StreamUtils.getDecodingInfosForVariants(
manifest.variants, usePersistentLicenses, /* srcEquals= */ false,
preferredKeySystems);
preferredKeySystems, enableMediaCapabilitiesCache);

let keySystem = null;
if (drmEngine) {
Expand Down Expand Up @@ -666,10 +670,11 @@ shaka.util.StreamUtils = class {
* @param {boolean} usePersistentLicenses
* @param {boolean} srcEquals
* @param {!Array<string>} preferredKeySystems
* @param {boolean} enableMediaCapabilitiesCache
* @exportDoc
*/
static async getDecodingInfosForVariants(variants, usePersistentLicenses,
srcEquals, preferredKeySystems) {
srcEquals, preferredKeySystems, enableMediaCapabilitiesCache = false) {
const gotDecodingInfo = variants.some((variant) =>
variant.decodingInfos.length);
if (gotDecodingInfo) {
Expand Down Expand Up @@ -710,27 +715,112 @@ shaka.util.StreamUtils = class {
}
} // for (const preferredKeySystem of preferredKeySystems)

for (const variant of variants) {
/** @type {!Array.<!Array.<!MediaDecodingConfiguration>>} */
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
variant, usePersistentLicenses, srcEquals)
.filter((configs) => {
// All configs in a batch will have the same keySystem.
const config = configs[0];
const keySystem = config.keySystemConfiguration &&
config.keySystemConfiguration.keySystem;
// Avoid checking preferred systems twice.
return !keySystem || !preferredKeySystems.includes(keySystem);
});
/**
* For platforms which use the MediaCapabilities polyfill, calls to media
* capabilities are replaced with isTypeSupported and
* requestMediaKeySystemAccess instead.
* By grouping similar variants, we can improve performance for manifests
* with lots of variants.
*/
const enableCache = enableMediaCapabilitiesCache &&
!srcEquals && !shaka.util.Platform.isChromecast();
if (enableCache) {
/** @type {!Object.<string, MediaCapabilitiesDecodingInfo>} */
const decodeInfoCache = {};
const buildCacheKey = (variant) => {
if (variant.audio.fullMimeTypes.size < 1 ||
variant.video.fullMimeTypes.size < 1) {
return undefined;
}
const audio = variant.audio;
const video = variant.video;

// if pure function for calculating the fulltype
const videoDrmInfos = video ? video.drmInfos : [];
const audioDrmInfos = audio ? audio.drmInfos : [];
const allDrmInfos = videoDrmInfos.concat(audioDrmInfos);

const keySystems = new Set(allDrmInfos.map((info) => info.keySystem));
const cacheKeys = [];
for (const keySystem of keySystems) {
for (const videoFullMimeType of video.fullMimeTypes) {
for (const audioFullMimeType of audio.fullMimeTypes) {
cacheKeys.push(
`${videoFullMimeType}#${audioFullMimeType}#${keySystem}`);
}
}
}
return cacheKeys;
};

// sort variants, so that high bandwidth checked first.
// High bandwidth tends to be less likely to be supported, or smooth
const sortedVariants = variants.sort((a, b) => b.bandwidth - a.bandwidth);

for (const variant of sortedVariants) {
const cacheKeys = buildCacheKey(variant);
const useCache = cacheKeys && cacheKeys.length &&
cacheKeys.every((key) => {
return decodeInfoCache[key] && decodeInfoCache[key].supported;
});
if (useCache) {
variant.decodingInfos =
cacheKeys.map((key) => decodeInfoCache[key]);
continue;
}

// The reason we are performing this await in a loop rather than
// batching into a `promise.all` is performance related.
// https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
for (const configs of decodingConfigs) {
// eslint-disable-next-line no-await-in-loop
await shaka.util.StreamUtils.getDecodingInfosForVariant_(
variant, configs);
await shaka.util.StreamUtils.getAndApplyDecodingInfosForVariant_(
variant, usePersistentLicenses, srcEquals, preferredKeySystems);

if (cacheKeys) {
for (let i = 0; i < variant.decodingInfos.length; i++) {
const cacheKey = cacheKeys[i];
decodeInfoCache[cacheKey] = variant.decodingInfos[i];
delete decodeInfoCache[cacheKey]['configuration'];
}
}
}
return;
}

for (const variant of variants) {
// eslint-disable-next-line no-await-in-loop
await shaka.util.StreamUtils.getAndApplyDecodingInfosForVariant_(
variant, usePersistentLicenses, srcEquals, preferredKeySystems);
}
}

/**
* Call mediaCapabilities
* Generate input for querying mediaCapabilities and call the API .
* @param {!shaka.extern.Variant} variant
* @param {boolean} usePersistentLicenses
* @param {boolean} srcEquals
* @param {!Array<string>=} preferredKeySystems
* @private
*/
static async getAndApplyDecodingInfosForVariant_(
variant, usePersistentLicenses, srcEquals, preferredKeySystems = []) {
/** @type {!Array.<!Array.<!MediaDecodingConfiguration>>} */
const decodingConfigs = shaka.util.StreamUtils.getDecodingConfigs_(
variant, usePersistentLicenses, srcEquals)
.filter((configs) => {
// All configs in a batch will have the same keySystem.
const config = configs[0];
const keySystem = config.keySystemConfiguration &&
config.keySystemConfiguration.keySystem;
// Avoid checking preferred systems twice.
return !keySystem || !preferredKeySystems.includes(keySystem);
});

// The reason we are performing this await in a loop rather than
// batching into a `promise.all` is performance related.
// https://github.com/shaka-project/shaka-player/pull/4708#discussion_r1022581178
for (const configs of decodingConfigs) {
// eslint-disable-next-line no-await-in-loop
await shaka.util.StreamUtils.getDecodingInfosForVariant_(
variant, configs);
}
}

Expand Down
Loading