diff --git a/src/core/Settings.js b/src/core/Settings.js index a34938071e..a9a360220d 100644 --- a/src/core/Settings.js +++ b/src/core/Settings.js @@ -73,7 +73,13 @@ import Events from './events/Events.js'; * enableManifestTimescaleMismatchFix: false, * capabilities: { * filterUnsupportedEssentialProperties: true, - * supportedEssentialProperties: Constants.THUMBNAILS_SCHEME_ID_URIS, + * supportedEssentialProperties: [ + { schemeIdUri: Constants.FONT_DOWNLOAD_DVB_SCHEME }, + { schemeIdUri: Constants.COLOUR_PRIMARIES_SCHEME_ID_URI, value: /5|6/ }, + { schemeIdUri: Constants.MATRIX_COEFFICIENTS_SCHEME_ID_URI, value: /5|6/ }, + { schemeIdUri: Constants.TRANSFER_CHARACTERISTICS_SCHEME_ID_URI, value: '6' }, + ...Constants.THUMBNAILS_SCHEME_ID_URIS.map(ep => { return { 'schemeIdUri': ep }; }) + ], * useMediaCapabilitiesApi: false * }, * timeShiftBuffer: { @@ -650,7 +656,7 @@ import Events from './events/Events.js'; * @typedef {Object} Capabilities * @property {boolean} [filterUnsupportedEssentialProperties=true] * Enable to filter all the AdaptationSets and Representations which contain an unsupported \ element. - * @property {Array.} [supportedEssentialProperties=Constants.THUMBNAILS_SCHEME_ID_URIS] + * @property {Array.} [supportedEssentialProperties] * List of supported \ elements * @property {boolean} [useMediaCapabilitiesApi=false] * Enable to use the MediaCapabilities API to check whether codecs are supported. If disabled MSE.isTypeSupported will be used instead. @@ -1032,7 +1038,13 @@ function Settings() { enableManifestTimescaleMismatchFix: false, capabilities: { filterUnsupportedEssentialProperties: true, - supportedEssentialProperties: [Constants.FONT_DOWNLOAD_DVB_SCHEME, ...Constants.THUMBNAILS_SCHEME_ID_URIS], + supportedEssentialProperties: [ + { schemeIdUri: Constants.FONT_DOWNLOAD_DVB_SCHEME }, + { schemeIdUri: Constants.COLOUR_PRIMARIES_SCHEME_ID_URI, value: /5|6/ }, + { schemeIdUri: Constants.MATRIX_COEFFICIENTS_SCHEME_ID_URI, value: /5|6/ }, + { schemeIdUri: Constants.TRANSFER_CHARACTERISTICS_SCHEME_ID_URI, value: '6' }, + ...Constants.THUMBNAILS_SCHEME_ID_URIS.map(ep => { return { 'schemeIdUri': ep }; }) + ], useMediaCapabilitiesApi: false }, timeShiftBuffer: { @@ -1297,7 +1309,7 @@ function Settings() { for (let n in source) { if (source.hasOwnProperty(n)) { if (dest.hasOwnProperty(n)) { - if (typeof source[n] === 'object' && !(source[n] instanceof Array) && source[n] !== null) { + if (typeof source[n] === 'object' && !(source[n] instanceof RegExp) && !(source[n] instanceof Array) && source[n] !== null) { mixinSettings(source[n], dest[n], path.slice() + n + '.'); } else { dest[n] = Utils.clone(source[n]); diff --git a/src/core/Utils.js b/src/core/Utils.js index 053b65c950..a7e7b7c2ac 100644 --- a/src/core/Utils.js +++ b/src/core/Utils.js @@ -62,6 +62,9 @@ class Utils { if (!src || typeof src !== 'object') { return src; // anything } + if (src instanceof RegExp) { + return new RegExp(src); + } let r; if (src instanceof Array) { // array diff --git a/src/dash/vo/DescriptorType.js b/src/dash/vo/DescriptorType.js index eee9b28fca..98ad2e4961 100644 --- a/src/dash/vo/DescriptorType.js +++ b/src/dash/vo/DescriptorType.js @@ -58,6 +58,21 @@ class DescriptorType { } } } + + inArray(arr) { + if (arr) { + return arr.some((entry) => { + return ( + this.schemeIdUri === entry.schemeIdUri && ( + this.value ? + (this.value.match(entry.value)) : // check if provided value matches RegExp + (''.match(entry.value)) // check if RegExp allows absent value + ) + ); + }) + } + return false; + } } export default DescriptorType; diff --git a/src/streaming/constants/Constants.js b/src/streaming/constants/Constants.js index fc48bf2724..89f5c4d391 100644 --- a/src/streaming/constants/Constants.js +++ b/src/streaming/constants/Constants.js @@ -228,6 +228,9 @@ export default { SUPPLEMENTAL_PROPERTY_DVB_LL_SCHEME: 'urn:dvb:dash:lowlatency:critical:2019', THUMBNAILS_SCHEME_ID_URIS: ['http://dashif.org/thumbnail_tile', 'http://dashif.org/guidelines/thumbnail_tile'], FONT_DOWNLOAD_DVB_SCHEME: 'urn:dvb:dash:fontdownload:2014', + COLOUR_PRIMARIES_SCHEME_ID_URI: 'urn:mpeg:mpegB:cicp:ColourPrimaries', + MATRIX_COEFFICIENTS_SCHEME_ID_URI: 'urn:mpeg:mpegB:cicp:MatrixCoefficients', + TRANSFER_CHARACTERISTICS_SCHEME_ID_URI: 'urn:mpeg:mpegB:cicp:TransferCharacteristics', XML: 'XML', ARRAY_BUFFER: 'ArrayBuffer', DVB_REPORTING_URL: 'dvb:reportingUrl', diff --git a/src/streaming/utils/Capabilities.js b/src/streaming/utils/Capabilities.js index 3379278060..55bfa3312e 100644 --- a/src/streaming/utils/Capabilities.js +++ b/src/streaming/utils/Capabilities.js @@ -187,13 +187,14 @@ function Capabilities() { /** * Check if a specific EssentialProperty is supported - * @param {object} ep + * @param {DescriptorType} ep * @return {boolean} */ function supportsEssentialProperty(ep) { let supportedEssentialProps = settings.get().streaming.capabilities.supportedEssentialProperties; + try { - return supportedEssentialProps.indexOf(ep.schemeIdUri) !== -1; + return ep.inArray(supportedEssentialProps); } catch (e) { return true; } diff --git a/test/unit/streaming.utils.Capabilities.js b/test/unit/streaming.utils.Capabilities.js index 040aa98d08..59f637c813 100644 --- a/test/unit/streaming.utils.Capabilities.js +++ b/test/unit/streaming.utils.Capabilities.js @@ -1,25 +1,64 @@ import Capabilities from '../../src/streaming/utils/Capabilities.js'; import Settings from '../../src/core/Settings.js'; -import {expect} from 'chai'; +import { expect } from 'chai'; +import DescriptorType from '../../src/dash/vo/DescriptorType.js'; let settings; let capabilities; -const EssentialPropertyThumbNail = { +let EssentialPropertyThumbNail = new DescriptorType; +EssentialPropertyThumbNail.init({ schemeIdUri: 'http://dashif.org/thumbnail_tile', - value: 'somevalue' + value: 'myvalue' +}); + +const EssentialPropertyThumbNailnoVal = { + schemeIdUri: 'http://dashif.org/thumbnail_tile' }; -const EssentialPropertyOwn = { + +let EssentialPropertyThumbNailemptyVal = new DescriptorType; +EssentialPropertyThumbNailemptyVal.init({ + schemeIdUri: 'http://dashif.org/thumbnail_tile', + value: '' +}); + +let EssentialPropertyOwn = new DescriptorType; +EssentialPropertyOwn.init({ schemeIdUri: 'tag:dashif.org:myOwnFeature', value: 'somevalue' -}; -const EssentialPropertyOwnSecond = { +}); + +let EssentialPropertyOwnSecond = new DescriptorType; +EssentialPropertyOwnSecond.init({ schemeIdUri: 'tag:dashif.org:mySecondOwnFeature', value: 'somevalue' -}; +}); -describe('CapabilitiesFilter', function () { +let EssentialPropertySDR = new DescriptorType; +EssentialPropertySDR.init({ + schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries', + value: '5' +}); + +let EssentialPropertynoVal = new DescriptorType; +EssentialPropertynoVal.init({ + schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries' +}); + +let EssentialPropertyemptyVal = new DescriptorType; +EssentialPropertyemptyVal.init({ + schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries', + value: '' +}); + +let EssentialPropertyHDR = new DescriptorType; +EssentialPropertyHDR.init({ + schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries', + value: '9' +}); + +describe('Capabilities', function () { beforeEach(function () { settings = Settings({}).getInstance(); capabilities = Capabilities({}).getInstance(); @@ -35,24 +74,44 @@ describe('CapabilitiesFilter', function () { expect(res).to.be.true; }); + it('should return true if EssentialProperty value is absent if supported by scheme', function () { + let res = capabilities.supportsEssentialProperty(EssentialPropertyThumbNailnoVal); + expect(res).to.be.true; + }); + + it('should return true if EssentialProperty value is empty if supported by scheme', function () { + let res = capabilities.supportsEssentialProperty(EssentialPropertyThumbNailemptyVal); + expect(res).to.be.true; + }); + it('should return false if EssentialProperty value is not known', function () { let res = capabilities.supportsEssentialProperty(EssentialPropertyOwn); expect(res).to.be.false; }); + it('should return false if EssentialProperty value is absent for known schemeId+value', function () { + let res = capabilities.supportsEssentialProperty(EssentialPropertynoVal); + expect(res).to.be.false; + }); + + it('should return false if EssentialProperty value is empty for known schemeId+value', function () { + let res = capabilities.supportsEssentialProperty(EssentialPropertyemptyVal); + expect(res).to.be.false; + }); + it('should return false if EssentialProperty value is not known when new values are registered in settings', function () { let props = settings.get().streaming.capabilities.supportedEssentialProperties; - props.push(...[EssentialPropertyOwn.schemeIdUri]); - settings.update({ streaming: { capabilities: { supportedEssentialProperties: props }} }); - + props.push(...[EssentialPropertyOwn]); + settings.update({ streaming: { capabilities: { supportedEssentialProperties: props } } }); + let res = capabilities.supportsEssentialProperty(EssentialPropertyOwnSecond); expect(res).to.be.false; }); it('should return true if EssentialProperty value is registered in settings', function () { let props = settings.get().streaming.capabilities.supportedEssentialProperties; - props.push(...[EssentialPropertyOwn.schemeIdUri]); - settings.update({ streaming: { capabilities: { supportedEssentialProperties: props }} }); + props.push(...[EssentialPropertyOwn]); + settings.update({ streaming: { capabilities: { supportedEssentialProperties: props } } }); let res = capabilities.supportsEssentialProperty(EssentialPropertyOwn); expect(res).to.be.true; @@ -60,11 +119,62 @@ describe('CapabilitiesFilter', function () { it('should return true for internally known EssentialProperties when new values are registered in settings', function () { let props = settings.get().streaming.capabilities.supportedEssentialProperties; - props.push(...[EssentialPropertyOwn.schemeIdUri]); - settings.update({ streaming: { capabilities: { supportedEssentialProperties: props }} }); + props.push(...[EssentialPropertyOwn]); + settings.update({ streaming: { capabilities: { supportedEssentialProperties: props } } }); let res = capabilities.supportsEssentialProperty(EssentialPropertyThumbNail); expect(res).to.be.true; }); + + it('should return true if schemeIdUri+value pair is registered as known feature', function () { + let res = capabilities.supportsEssentialProperty(EssentialPropertySDR); + expect(res).to.be.true; + }); + + it('should return true if schemeIdUri+RegExp-value pair is registered as known feature', function () { + const newFeature = { + schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries', + value: /1|5|9/ + }; + + let props = settings.get().streaming.capabilities.supportedEssentialProperties; + props.push(...[newFeature]); + settings.update({ streaming: { capabilities: { supportedEssentialProperties: props } } }); + + let res = capabilities.supportsEssentialProperty(EssentialPropertyHDR); + expect(res).to.be.true; + }); + + it('should return false if schemeIdUri, but unknown RegExp-value pair is registered as known feature', function () { + const newFeature = { + schemeIdUri: 'urn:mpeg:mpegB:cicp:ColourPrimaries', + value: /1|5|7/ + }; + + let props = settings.get().streaming.capabilities.supportedEssentialProperties; + props.push(...[newFeature]); + settings.update({ streaming: { capabilities: { supportedEssentialProperties: props } } }); + + let res = capabilities.supportsEssentialProperty(EssentialPropertyHDR); + expect(res).to.be.false; + }); + + it('should return false if schemeIdUri, but different value are registered as known feature', function () { + let res = capabilities.supportsEssentialProperty(EssentialPropertyHDR); + expect(res).to.be.false; + }); + + it('should default missing EssentialProp.value in Settings to match-all', function () { + let props = settings.get().streaming.capabilities.supportedEssentialProperties; + props.push(...[{ schemeIdUri: 'tag:dashif.org:scheme:value:test' }]); + settings.update({ streaming: { capabilities: { supportedEssentialProperties: props } } }) + + let res = capabilities.supportsEssentialProperty({ schemeIdUri: 'tag:dashif.org:scheme:value:test', value: '' }); + expect(res).to.be.true; + res = capabilities.supportsEssentialProperty({ schemeIdUri: 'tag:dashif.org:scheme:value:test' }); + expect(res).to.be.true; + res = capabilities.supportsEssentialProperty({ schemeIdUri: 'tag:dashif.org:scheme:value:test', value: '5' }); + expect(res).to.be.true; + }); }); });