Skip to content

Commit

Permalink
feat: Allow WebP and AVIF image streams (shaka-project#3856)
Browse files Browse the repository at this point in the history
  • Loading branch information
Álvaro Velad Galván authored Jan 13, 2022
1 parent 8d1b5e6 commit 9f3fb46
Showing 1 changed file with 79 additions and 22 deletions.
101 changes: 79 additions & 22 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ shaka.util.StreamUtils = class {
shaka.util.StreamUtils.filterManifestByCurrentVariant(
currentVariant, manifest);
shaka.util.StreamUtils.filterTextStreams_(manifest);
shaka.util.StreamUtils.filterImageStreams_(manifest);
await shaka.util.StreamUtils.filterImageStreams_(manifest);
}


Expand Down Expand Up @@ -761,37 +761,57 @@ shaka.util.StreamUtils = class {
* @param {shaka.extern.Manifest} manifest
* @private
*/
static filterImageStreams_(manifest) {
// Filter image streams.
manifest.imageStreams = manifest.imageStreams.filter((stream) => {
// TODO: re-examine this and avoid allow-listing the MIME types we can
// accept.
const validMimeTypes = [
'image/svg+xml',
'image/png',
'image/jpeg',
];
const Platform = shaka.util.Platform;
// Add webp support to popular platforms that support it.
const webpSupport = Platform.isWebOS() ||
Platform.isTizen() ||
Platform.isChromecast();
if (webpSupport) {
validMimeTypes.push('image/webp');
static async filterImageStreams_(manifest) {
const imageStreams = [];
for (const stream of manifest.imageStreams) {
const mimeType = stream.mimeType;
if (!shaka.util.StreamUtils.supportedImageMimeTypes_.has(mimeType)) {
const minImage = shaka.util.StreamUtils.minImage_.get(mimeType);
if (minImage) {
// eslint-disable-next-line no-await-in-loop
const res = await shaka.util.StreamUtils.isImageSupported_(minImage);
shaka.util.StreamUtils.supportedImageMimeTypes_.set(mimeType, res);
} else {
shaka.util.StreamUtils.supportedImageMimeTypes_.set(mimeType, false);
}
}
// TODO: add support to image/webp and image/avif
const keep = validMimeTypes.includes(stream.mimeType);

const keep =
shaka.util.StreamUtils.supportedImageMimeTypes_.get(mimeType);

if (!keep) {
shaka.log.debug('Dropping image stream. Is not supported by the ' +
'platform.', stream);
} else {
imageStreams.push(stream);
}
}
manifest.imageStreams = imageStreams;
}

return keep;
/**
* @param {string} minImage
* @return {!Promise.<boolean>}
* @private
*/
static isImageSupported_(minImage) {
return new Promise((resolve) => {
const imageElement = /** @type {HTMLImageElement} */(new Image());
imageElement.src = minImage;
if ('decode' in imageElement) {
imageElement.decode().then(() => {
resolve(true);
}).catch(() => {
resolve(false);
});
} else {
imageElement.onload = imageElement.onerror = () => {
resolve(imageElement.height === 2);
};
}
});
}


/**
* @param {shaka.extern.Stream} s0
* @param {shaka.extern.Stream} s1
Expand Down Expand Up @@ -1445,3 +1465,40 @@ shaka.util.StreamUtils.DecodingAttributes = {
POWER: 'powerEfficient',
BANDWIDTH: 'bandwidth',
};

/**
* @private {!Map.<string, boolean>}
*/
shaka.util.StreamUtils.supportedImageMimeTypes_ = new Map()
.set('image/svg+xml', true)
.set('image/png', true)
.set('image/jpeg', true)
.set('image/jpg', true);

/**
* @const {string}
* @private
*/
shaka.util.StreamUtils.minWebPImage_ = 'data:image/webp;base64,UklGRjoAAABXRU' +
'JQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwY' +
'AAA';

/**
* @const {string}
* @private
*/
shaka.util.StreamUtils.minAvifImage_ = 'data:image/avif;base64,AAAAIGZ0eXBhdm' +
'lmAAAAAGF2aWZtaWYxbWlhZk1BMUIAAADybWV0YQAAAAAAAAAoaGRscgAAAAAAAAAAcGljd' +
'AAAAAAAAAAAAAAAAGxpYmF2aWYAAAAADnBpdG0AAAAAAAEAAAAeaWxvYwAAAABEAAABAAEA' +
'AAABAAABGgAAAB0AAAAoaWluZgAAAAAAAQAAABppbmZlAgAAAAABAABhdjAxQ29sb3IAAAA' +
'AamlwcnAAAABLaXBjbwAAABRpc3BlAAAAAAAAAAIAAAACAAAAEHBpeGkAAAAAAwgICAAAAA' +
'xhdjFDgQ0MAAAAABNjb2xybmNseAACAAIAAYAAAAAXaXBtYQAAAAAAAAABAAEEAQKDBAAAA' +
'CVtZGF0EgAKCBgANogQEAwgMg8f8D///8WfhwB8+ErK42A=';

/**
* @const {!Map.<string, string>}
* @private
*/
shaka.util.StreamUtils.minImage_ = new Map()
.set('image/webp', shaka.util.StreamUtils.minWebPImage_)
.set('image/avif', shaka.util.StreamUtils.minAvifImage_);

0 comments on commit 9f3fb46

Please sign in to comment.