From 26439522db5eb3fdb2a781acc2622a58deec8a66 Mon Sep 17 00:00:00 2001 From: stschr <92983372+stschr@users.noreply.github.com> Date: Mon, 24 Apr 2023 10:29:52 +0200 Subject: [PATCH] MediaInfo: introduce DescriptorType (#4158) * resolve rebase conflicts * add secure init function for descriptorType * make use of new DescriptroType class * fix issue with accessibility * change getter to array.map functions * issue #4123: propagate supplProp from Repr to AdaptSet if all equal * take new DescriptorType into account * make supplementalProp avail as DescriptorType * resolve reabse conflicts * fix TS * resolve rebase conflicts * implement first new unit tests * enable mocha grep with karma runner * encapsulate mediaInfo test * a few more tests on Mediainfo from DashAdapter * add test for DashAdapter.AreMediaInfosEqual * bugfix comparing content of object * test supplementalProp on Representations * added test for viewpoint descriptor * adding test for trackEquality with DescriptorType * enhance trackEquality to use DescriptorType * adding unit tests for DashManifestModel * coding style: use single space, no alignment * use strong equality * use () with constructur invokation * use plural + camel case * use DashConstants * fix new names --- index.d.ts | 20 +- karma.unit.conf.js | 3 +- src/dash/DashAdapter.js | 57 ++- src/dash/constants/DashConstants.js | 2 + src/dash/models/DashManifestModel.js | 73 +++- src/dash/vo/DescriptorType.js | 52 +++ src/dash/vo/MediaInfo.js | 5 + src/streaming/controllers/MediaController.js | 6 +- test/unit/dash.DashAdapter.js | 410 ++++++++++++++++-- test/unit/dash.constants.DashConstants.js | 2 + test/unit/dash.models.DashManifestModel.js | 58 ++- .../streaming.controllers.MediaController.js | 52 +++ 12 files changed, 686 insertions(+), 54 deletions(-) create mode 100644 src/dash/vo/DescriptorType.js diff --git a/index.d.ts b/index.d.ts index ba9f5269ab..ed9f7cecc2 100644 --- a/index.d.ts +++ b/index.d.ts @@ -145,15 +145,15 @@ declare namespace dashjs { getLanguageForAdaptation(adaptation: object): string; - getViewpointForAdaptation(adaptation: object): any; + getViewpointForAdaptation(adaptation: object): DescriptorType[]; - getRolesForAdaptation(adaptation: object): any[]; + getRolesForAdaptation(adaptation: object): DescriptorType[]; - getAccessibilityForAdaotation(adaptation: object): any[]; + getAccessibilityForAdaptation(adaptation: object): DescriptorType[]; - getAudioChannelConfigurationForAdaptation(adaptation: object): any[]; + getAudioChannelConfigurationForAdaptation(adaptation: object): DescriptorType[]; - getAudioChannelConfigurationForRepresentation(adaptation: object): any[]; + getAudioChannelConfigurationForRepresentation(adaptation: object): DescriptorType[]; getRepresentationSortFunction(): (a: object, b: object) => number; @@ -426,9 +426,13 @@ declare namespace dashjs { labels: { text: string, lang?: string }[]; lang: string | null; viewpoint: any | undefined | null; + viewpointsWithSchemeIdUri: DescriptorType[] | null; accessibility: any[] | null; + accessibilitiesWithSchemeIdUri: DescriptorType[] | null; audioChannelConfiguration: any[] | null; + audioChannelConfigurationsWithSchemeIdUri: DescriptorType[] | null; roles: string[] | null; + rolesWithSchemeIdUri: DescriptorType[] | null; codec: string | null; mimeType: string | null; contentProtection: any | null; @@ -545,6 +549,12 @@ declare namespace dashjs { value: string; } + export class DescriptorType { + schemeIdUri: string; + value: string; + id: string; + } + /** * Dash **/ diff --git a/karma.unit.conf.js b/karma.unit.conf.js index be3720a25d..d6f17d99b2 100644 --- a/karma.unit.conf.js +++ b/karma.unit.conf.js @@ -35,7 +35,8 @@ module.exports = function (config) { client: { useIframe: false, mocha: { - timeout: 90000 + timeout: 90000, + grep: config.grep } }, diff --git a/src/dash/DashAdapter.js b/src/dash/DashAdapter.js index cb73c6d677..c69f469bc8 100644 --- a/src/dash/DashAdapter.js +++ b/src/dash/DashAdapter.js @@ -202,12 +202,16 @@ function DashAdapter() { const sameId = mInfoOne.id === mInfoTwo.id; const sameCodec = mInfoOne.codec === mInfoTwo.codec; const sameViewpoint = mInfoOne.viewpoint === mInfoTwo.viewpoint; + const sameViewpointWithSchemeIdUri = JSON.stringify(mInfoOne.viewpointsWithSchemeIdUri) === JSON.stringify(mInfoTwo.viewpointsWithSchemeIdUri); const sameLang = mInfoOne.lang === mInfoTwo.lang; const sameRoles = mInfoOne.roles.toString() === mInfoTwo.roles.toString(); + const sameRolesWithSchemeIdUri = JSON.stringify(mInfoOne.rolesWithSchemeIdUri) === JSON.stringify(mInfoTwo.rolesWithSchemeIdUri); const sameAccessibility = mInfoOne.accessibility.toString() === mInfoTwo.accessibility.toString(); + const sameAccessibilityWithSchemeIdUri = JSON.stringify(mInfoOne.accessibilitiesWithSchemeIdUri) === JSON.stringify(mInfoTwo.accessibilitiesWithSchemeIdUri); const sameAudioChannelConfiguration = mInfoOne.audioChannelConfiguration.toString() === mInfoTwo.audioChannelConfiguration.toString(); + const sameAudioChannelConfigurationWithSchemeIdUri = JSON.stringify(mInfoOne.audioChannelConfigurationsWithSchemeIdUri) === JSON.stringify(mInfoTwo.audioChannelConfigurationsWithSchemeIdUri); - return (sameId && sameCodec && sameViewpoint && sameLang && sameRoles && sameAccessibility && sameAudioChannelConfiguration); + return (sameId && sameCodec && sameViewpoint && sameViewpointWithSchemeIdUri && sameLang && sameRoles && sameRolesWithSchemeIdUri && sameAccessibility && sameAccessibilityWithSchemeIdUri && sameAudioChannelConfiguration && sameAudioChannelConfigurationWithSchemeIdUri); } function _getAllMediaInfo(manifest, period, streamInfo, adaptations, type, embeddedText) { @@ -1035,7 +1039,7 @@ function DashAdapter() { let mediaInfo = new MediaInfo(); const realAdaptation = adaptation.period.mpd.manifest.Period_asArray[adaptation.period.index].AdaptationSet_asArray[adaptation.index]; - let viewpoint; + let viewpoint, acc, acc_rep, roles, accessibility; mediaInfo.id = adaptation.id; mediaInfo.index = adaptation.index; @@ -1044,11 +1048,15 @@ function DashAdapter() { mediaInfo.representationCount = dashManifestModel.getRepresentationCount(realAdaptation); mediaInfo.labels = dashManifestModel.getLabelsForAdaptation(realAdaptation); mediaInfo.lang = dashManifestModel.getLanguageForAdaptation(realAdaptation); - viewpoint = dashManifestModel.getViewpointForAdaptation(realAdaptation); - mediaInfo.viewpoint = viewpoint ? viewpoint.value : undefined; mediaInfo.segmentAlignment = dashManifestModel.getSegmentAlignment(realAdaptation); mediaInfo.subSegmentAlignment = dashManifestModel.getSubSegmentAlignment(realAdaptation); - mediaInfo.accessibility = dashManifestModel.getAccessibilityForAdaptation(realAdaptation).map(function (accessibility) { + + viewpoint = dashManifestModel.getViewpointForAdaptation(realAdaptation); + mediaInfo.viewpoint = viewpoint.length ? viewpoint[0].value : undefined; + mediaInfo.viewpointsWithSchemeIdUri = viewpoint; + + accessibility = dashManifestModel.getAccessibilityForAdaptation(realAdaptation); + mediaInfo.accessibility = accessibility.map(function (accessibility) { let accessibilityValue = accessibility.value; let accessibilityData = accessibilityValue; if (accessibility.schemeIdUri && (accessibility.schemeIdUri.search('cea-608') >= 0) && typeof (cea608parser) !== 'undefined') { @@ -1061,19 +1069,28 @@ function DashAdapter() { } return accessibilityData; }); + mediaInfo.accessibilitiesWithSchemeIdUri = accessibility; - mediaInfo.audioChannelConfiguration = dashManifestModel.getAudioChannelConfigurationForAdaptation(realAdaptation).map(function (audioChannelConfiguration) { + acc = dashManifestModel.getAudioChannelConfigurationForAdaptation(realAdaptation); + mediaInfo.audioChannelConfiguration = acc.map(function (audioChannelConfiguration) { return audioChannelConfiguration.value; }); + mediaInfo.audioChannelConfigurationsWithSchemeIdUri = acc; if (mediaInfo.audioChannelConfiguration.length === 0 && Array.isArray(realAdaptation.Representation_asArray) && realAdaptation.Representation_asArray.length > 0) { - mediaInfo.audioChannelConfiguration = dashManifestModel.getAudioChannelConfigurationForRepresentation(realAdaptation.Representation_asArray[0]).map(function (audioChannelConfiguration) { + acc_rep = dashManifestModel.getAudioChannelConfigurationForRepresentation(realAdaptation.Representation_asArray[0]); + mediaInfo.audioChannelConfiguration = acc_rep.map(function (audioChannelConfiguration) { return audioChannelConfiguration.value; }); + mediaInfo.audioChannelConfigurationsWithSchemeIdUri = acc_rep; } - mediaInfo.roles = dashManifestModel.getRolesForAdaptation(realAdaptation).map(function (role) { + + roles = dashManifestModel.getRolesForAdaptation(realAdaptation); + mediaInfo.roles = roles.map(function (role) { return role.value; }); + mediaInfo.rolesWithSchemeIdUri = roles; + mediaInfo.codec = dashManifestModel.getCodec(realAdaptation); mediaInfo.mimeType = dashManifestModel.getMimeType(realAdaptation); mediaInfo.contentProtection = dashManifestModel.getContentProtectionData(realAdaptation); @@ -1092,8 +1109,27 @@ function DashAdapter() { } mediaInfo.isText = dashManifestModel.getIsText(realAdaptation); - mediaInfo.supplementalProperties = dashManifestModel.getSupplementalProperties(realAdaptation); - + mediaInfo.supplementalProperties = dashManifestModel.getSupplementalPropertiesForAdaptation(realAdaptation); + if ( (!mediaInfo.supplementalProperties || Object.keys(mediaInfo.supplementalProperties).length === 0) && Array.isArray(realAdaptation.Representation_asArray) && realAdaptation.Representation_asArray.length > 0) { + let arr = realAdaptation.Representation_asArray.map( repr => { + return dashManifestModel.getSupplementalPropertiesForRepresentation(repr); + }); + if ( arr.every( v => JSON.stringify(v) === JSON.stringify(arr[0]) ) ) { + // only output Representation.supplementalProperties to mediaInfo, if they are present on all Representations + mediaInfo.supplementalProperties = arr[0]; + } + } + mediaInfo.supplementalPropertiesAsArray = dashManifestModel.getSupplementalPropertiesAsArrayForAdaptation(realAdaptation); + if ( (!mediaInfo.supplementalPropertiesAsArray || mediaInfo.supplementalPropertiesAsArray.length === 0) && Array.isArray(realAdaptation.Representation_asArray) && realAdaptation.Representation_asArray.length > 0) { + let arr = realAdaptation.Representation_asArray.map( repr => { + return dashManifestModel.getSupplementalPropertiesAsArrayForRepresentation(repr); + }); + if ( arr.every( v => JSON.stringify(v) === JSON.stringify(arr[0]) ) ) { + // only output Representation.supplementalProperties to mediaInfo, if they are present on all Representations + mediaInfo.supplementalPropertiesAsArray = arr[0]; + } + } + mediaInfo.isFragmented = dashManifestModel.getIsFragmented(realAdaptation); mediaInfo.isEmbedded = false; @@ -1109,6 +1145,7 @@ function DashAdapter() { mediaInfo.isFragmented = false; mediaInfo.lang = bcp47Normalize(lang); mediaInfo.roles = ['caption']; + mediaInfo.rolesWithSchemeIdUri = [{schemeIdUri:'urn:mpeg:dash:role:2011', value:'caption'}]; } function convertVideoInfoToThumbnailInfo(mediaInfo) { diff --git a/src/dash/constants/DashConstants.js b/src/dash/constants/DashConstants.js index 0754376240..4270bb05f5 100644 --- a/src/dash/constants/DashConstants.js +++ b/src/dash/constants/DashConstants.js @@ -89,6 +89,7 @@ class DashConstants { this.CONTENT_PROTECTION = 'ContentProtection'; this.ESSENTIAL_PROPERTY = 'EssentialProperty'; this.SUPPLEMENTAL_PROPERTY = 'SupplementalProperty'; + this.SUPPLEMENTAL_PROPERTY_ASARRAY = 'SupplementalProperty_asArray'; this.INBAND_EVENT_STREAM = 'InbandEventStream'; this.PRODUCER_REFERENCE_TIME = 'ProducerReferenceTime'; this.INBAND = 'inband'; @@ -100,6 +101,7 @@ class DashConstants { this.SUBSET = 'Subset'; this.LANG = 'lang'; this.VIEWPOINT = 'Viewpoint'; + this.VIEWPOINT_ASARRAY = 'Viewpoint_asArray'; this.ROLE_ASARRAY = 'Role_asArray'; this.REPRESENTATION_ASARRAY = 'Representation_asArray'; this.PRODUCERREFERENCETIME_ASARRAY = 'ProducerReferenceTime_asArray'; diff --git a/src/dash/models/DashManifestModel.js b/src/dash/models/DashManifestModel.js index 50a2b3ff0d..f9277acdbc 100644 --- a/src/dash/models/DashManifestModel.js +++ b/src/dash/models/DashManifestModel.js @@ -40,6 +40,7 @@ import BaseURL from '../vo/BaseURL'; import EventStream from '../vo/EventStream'; import ProducerReferenceTime from '../vo/ProducerReferenceTime'; import ContentSteering from '../vo/ContentSteering'; +import DescriptorType from '../vo/DescriptorType'; import ObjectUtils from '../../streaming/utils/ObjectUtils'; import URLUtils from '../../streaming/utils/URLUtils'; import FactoryMaker from '../../core/FactoryMaker'; @@ -230,23 +231,43 @@ function DashManifestModel() { } function getViewpointForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.VIEWPOINT) ? adaptation.Viewpoint : null; + if (!adaptation || !adaptation.hasOwnProperty(DashConstants.VIEWPOINT_ASARRAY) || !adaptation[DashConstants.VIEWPOINT_ASARRAY].length) return []; + return adaptation[DashConstants.VIEWPOINT_ASARRAY].map( viewpoint => { + const vp = new DescriptorType(); + return vp.init(viewpoint); + }); } function getRolesForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.ROLE_ASARRAY) ? adaptation.Role_asArray : []; + if (!adaptation || !adaptation.hasOwnProperty(DashConstants.ROLE_ASARRAY) || !adaptation[DashConstants.ROLE_ASARRAY].length) return []; + return adaptation[DashConstants.ROLE_ASARRAY].map( role => { + const r = new DescriptorType(); + return r.init(role); + }); } function getAccessibilityForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.ACCESSIBILITY_ASARRAY) ? adaptation.Accessibility_asArray : []; + if (!adaptation || !adaptation.hasOwnProperty(DashConstants.ACCESSIBILITY_ASARRAY) || !adaptation[DashConstants.ACCESSIBILITY_ASARRAY].length) return []; + return adaptation[DashConstants.ACCESSIBILITY_ASARRAY].map( accessibility => { + const a = new DescriptorType(); + return a.init(accessibility); + }); } function getAudioChannelConfigurationForAdaptation(adaptation) { - return adaptation && adaptation.hasOwnProperty(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY) ? adaptation.AudioChannelConfiguration_asArray : []; + if (!adaptation || !adaptation.hasOwnProperty(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY) || !adaptation[DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY].length) return []; + return adaptation[DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY].map( audioChanCfg => { + const acc = new DescriptorType(); + return acc.init(audioChanCfg); + }); } function getAudioChannelConfigurationForRepresentation(representation) { - return representation && representation.hasOwnProperty(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY) ? representation.AudioChannelConfiguration_asArray : []; + if (!representation || !representation.hasOwnProperty(DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY) || !representation[DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY].length) return []; + return representation[DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY].map( audioChanCfg => { + const acc = new DescriptorType(); + return acc.init(audioChanCfg); + }); } function getRepresentationSortFunction() { @@ -1263,10 +1284,10 @@ function DashManifestModel() { return serviceDescriptions; } - function getSupplementalProperties(adaptation) { + function getSupplementalPropertiesForAdaptation(adaptation) { const supplementalProperties = {}; - if (adaptation && adaptation.hasOwnProperty(DashConstants.SUPPLEMENTAL_PROPERTY)) { + if (adaptation && adaptation.hasOwnProperty(DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY)) { for (const sp of adaptation.SupplementalProperty_asArray) { if (sp.hasOwnProperty(Constants.SCHEME_ID_URI) && sp.hasOwnProperty(DashConstants.VALUE)) { supplementalProperties[sp[Constants.SCHEME_ID_URI]] = sp[DashConstants.VALUE]; @@ -1276,6 +1297,35 @@ function DashManifestModel() { return supplementalProperties; } + function getSupplementalPropertiesAsArrayForAdaptation(adaptation) { + if (!adaptation || !adaptation.hasOwnProperty(DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY) || !adaptation.SupplementalProperty_asArray.length) return []; + return adaptation.SupplementalProperty_asArray.map( supp => { + const s = new DescriptorType(); + return s.init(supp); + }); + } + + function getSupplementalPropertiesForRepresentation(representation) { + const supplementalProperties = {}; + + if (representation && representation.hasOwnProperty(DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY)) { + for (const sp of representation.SupplementalProperty_asArray) { + if (sp.hasOwnProperty(Constants.SCHEME_ID_URI) && sp.hasOwnProperty(DashConstants.VALUE)) { + supplementalProperties[sp[Constants.SCHEME_ID_URI]] = sp[DashConstants.VALUE]; + } + } + } + return supplementalProperties; + } + + function getSupplementalPropertiesAsArrayForRepresentation(representation) { + if (!representation || !representation.hasOwnProperty(DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY) || !representation.SupplementalProperty_asArray.length) return []; + return representation.SupplementalProperty_asArray.map( supp => { + const s = new DescriptorType(); + return s.init(supp); + }); + } + function setConfig(config) { if (!config) return; @@ -1338,10 +1388,13 @@ function DashManifestModel() { getSuggestedPresentationDelay, getAvailabilityStartTime, getServiceDescriptions, - getSupplementalProperties, - setConfig, getSegmentAlignment, - getSubSegmentAlignment + getSubSegmentAlignment, + getSupplementalPropertiesForAdaptation, + getSupplementalPropertiesAsArrayForAdaptation, + getSupplementalPropertiesForRepresentation, + getSupplementalPropertiesAsArrayForRepresentation, + setConfig }; setup(); diff --git a/src/dash/vo/DescriptorType.js b/src/dash/vo/DescriptorType.js new file mode 100644 index 0000000000..be8a1460a7 --- /dev/null +++ b/src/dash/vo/DescriptorType.js @@ -0,0 +1,52 @@ +/** + * The copyright in this software is being made available under the BSD License, + * included below. This software may be subject to other third party and contributor + * rights, including patent rights, and no such rights are granted under this license. + * + * Copyright (c) 2023, Dash Industry Forum. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation and/or + * other materials provided with the distribution. + * * Neither the name of Dash Industry Forum nor the names of its + * contributors may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * @class + * @ignore + */ +class DescriptorType { + constructor() { + this.schemeIdUri = null; + this.value = null; + this.id = null; + } + + init(data) { + if (data) { + this.schemeIdUri = data.schemeIdUri ? data.schemeIdUri : null; + this.value = data.value ? data.value : null; + this.id = data.id ? data.id : null; + } + return this; + } +} + +export default DescriptorType; diff --git a/src/dash/vo/MediaInfo.js b/src/dash/vo/MediaInfo.js index 9650dce0d6..eef44d33cb 100644 --- a/src/dash/vo/MediaInfo.js +++ b/src/dash/vo/MediaInfo.js @@ -42,9 +42,13 @@ class MediaInfo { this.labels = null; this.lang = null; this.viewpoint = null; + this.viewpointsWithSchemeIdUri = null; this.accessibility = null; + this.accessibilitiesWithSchemeIdUri = null; this.audioChannelConfiguration = null; + this.audioChannelConfigurationsWithSchemeIdUri = null; this.roles = null; + this.rolesWithSchemeIdUri = null; this.codec = null; this.mimeType = null; this.contentProtection = null; @@ -55,6 +59,7 @@ class MediaInfo { this.isEmbedded = null; this.selectionPriority = 1; this.supplementalProperties = {}; + this.supplementalPropertiesAsArray = []; this.segmentAlignment = false; this.subSegmentAlignment = false; } diff --git a/src/streaming/controllers/MediaController.js b/src/streaming/controllers/MediaController.js index a807fe4136..dcec2f8c5c 100644 --- a/src/streaming/controllers/MediaController.js +++ b/src/streaming/controllers/MediaController.js @@ -266,13 +266,17 @@ function MediaController() { const sameId = t1.id === t2.id; const sameViewpoint = t1.viewpoint === t2.viewpoint; + const sameViewpointDescriptors = JSON.stringify(t1.viewpointsWithSchemeIdUri) === JSON.stringify(t2.viewpointsWithSchemeIdUri); const sameLang = t1.lang === t2.lang; const sameCodec = t1.codec === t2.codec; const sameRoles = t1.roles.toString() === t2.roles.toString(); + const sameRoleDescriptors = JSON.stringify(t1.rolesWithSchemeIdUri) === JSON.stringify(t2.rolesWithSchemeIdUri); const sameAccessibility = t1.accessibility.toString() === t2.accessibility.toString(); + const sameAccessibilityDescriptors = JSON.stringify(t1.accessibilitiesWithSchemeIdUri) === JSON.stringify(t2.accessibilitiesWithSchemeIdUri); const sameAudioChannelConfiguration = t1.audioChannelConfiguration.toString() === t2.audioChannelConfiguration.toString(); + const sameAudioChannelConfigurationDescriptors = JSON.stringify(t1.audioChannelConfigurationsWithSchemeIdUri) === JSON.stringify(t2.audioChannelConfigurationsWithSchemeIdUri); - return (sameId && sameCodec && sameViewpoint && sameLang && sameRoles && sameAccessibility && sameAudioChannelConfiguration); + return (sameId && sameCodec && sameViewpoint && sameViewpointDescriptors && sameLang && sameRoles && sameRoleDescriptors && sameAccessibility && sameAccessibilityDescriptors && sameAudioChannelConfiguration && sameAudioChannelConfigurationDescriptors); } function setConfig(config) { diff --git a/test/unit/dash.DashAdapter.js b/test/unit/dash.DashAdapter.js index c451be8479..b883d3ba9c 100644 --- a/test/unit/dash.DashAdapter.js +++ b/test/unit/dash.DashAdapter.js @@ -7,6 +7,7 @@ import cea608parser from '../../externals/cea608-parser'; import VoHelper from './helpers/VOHelper'; import PatchHelper from './helpers/PatchHelper.js'; import ErrorHandlerMock from './mocks/ErrorHandlerMock'; +import DescriptorType from '../../src/dash/vo/DescriptorType'; const expect = require('chai').expect; @@ -18,24 +19,29 @@ const manifest_with_audio = { loadedTime: new Date(), mediaPresentationDuration: 10, Period_asArray: [{ - AdaptationSet_asArray: [{ - id: undefined, - mimeType: Constants.AUDIO, - lang: 'eng', - Role_asArray: [{ value: 'main' }] - }, { id: undefined, mimeType: Constants.AUDIO, lang: 'deu', Role_asArray: [{ value: 'main' }] }] + AdaptationSet_asArray: [ + { + id: undefined, mimeType: Constants.AUDIO, + lang: 'eng', Role_asArray: [{ value: 'main' }] + }, { + id: undefined, mimeType: Constants.AUDIO, + lang: 'deu', Role_asArray: [{ value: 'main' }] + } + ] }] }; const manifest_with_video_with_embedded_subtitles = { loadedTime: new Date(), mediaPresentationDuration: 10, Period_asArray: [{ - AdaptationSet_asArray: [{ - id: 0, - mimeType: Constants.VIDEO, - Accessibility: { schemeIdUri: 'urn:scte:dash:cc:cea-608:2015', value: 'CC1=eng;CC3=swe' }, - Accessibility_asArray: [{ schemeIdUri: 'urn:scte:dash:cc:cea-608:2015', value: 'CC1=eng;CC3=swe' }] - }, { id: 1, mimeType: Constants.VIDEO }] + AdaptationSet_asArray: [ + { + id: 0, mimeType: Constants.VIDEO, + Accessibility_asArray: [{ schemeIdUri: 'urn:scte:dash:cc:cea-608:2015', value: 'CC1=eng;CC3=swe' }] + }, { + id: 1, mimeType: Constants.VIDEO + } + ] }] }; const manifest_with_ll_service_description = { @@ -49,9 +55,7 @@ const manifest_with_ll_service_description = { }], Period_asArray: [{ AdaptationSet_asArray: [{ - id: 0, - mimeType: Constants.VIDEO, - SupplementalProperty: {}, + id: 0, mimeType: Constants.VIDEO, SupplementalProperty_asArray: [{ schemeIdUri: 'urn:dvb:dash:lowlatency:critical:2019', value: 'true' }] }] }] @@ -61,7 +65,112 @@ const manifest_without_supplemental_properties = { mediaPresentationDuration: 10, Period_asArray: [{ AdaptationSet_asArray: [{ id: 0, mimeType: Constants.VIDEO }] }] }; - +const manifest_with_supplemental_properties = { + loadedTime: new Date(), + mediaPresentationDuration: 10, + Period_asArray: [{ + AdaptationSet_asArray: [{ + id: 0, mimeType: Constants.VIDEO, + SupplementalProperty_asArray: [{schemeIdUri: 'test:scheme', value: 'value1'},{schemeIdUri: 'test:scheme', value: 'value2'}] + }] + }] +}; +const manifest_with_supplemental_properties_on_repr = { + loadedTime: new Date(), + mediaPresentationDuration: 10, + Period_asArray: [{ + AdaptationSet_asArray: [{ + id: 0, mimeType: Constants.VIDEO, + // SupplementalProperty_asArray: [{schemeIdUri: 'test:scheme', value: 'value1'},{schemeIdUri: 'test:scheme', value: 'value2'},{schemeIdUri: 'test:scheme', value: 'value3'}], + [DashConstants.REPRESENTATION_ASARRAY]: [ + { + id: 10, bandwidth: 128000, + [DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY]: [ + {schemeIdUri: 'test:scheme', value: 'value1'}, + {schemeIdUri: 'test:scheme', value: 'value2'}, + {schemeIdUri: 'test:scheme', value: 'value3'} + ] + }, + { + id: 11, bandwidth: 160000, + [DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY]: [ + {schemeIdUri: 'test:scheme', value: 'value1'}, + {schemeIdUri: 'test:scheme', value: 'value2'}, + {schemeIdUri: 'test:scheme', value: 'value3'} + ] + } + ] + }] + }] +}; +const manifest_with_supplemental_properties_on_only_one_repr = { + loadedTime: new Date(), + mediaPresentationDuration: 10, + Period_asArray: [{ + AdaptationSet_asArray: [{ + id: 0, mimeType: Constants.VIDEO, + [DashConstants.REPRESENTATION_ASARRAY]: [ + { + id: 10, bandwidth: 128000, + [DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY]: [ + {schemeIdUri: 'test:scheme', value: 'value1'}, + {schemeIdUri: 'test:scheme', value: 'value2'} + ] + }, + { + id: 11, bandwidth: 160000 + }, + { + id: 12, bandwidth: 96000 + } + ] + }] + }] +}; +const manifest_with_audioChanCfg = { + loadedTime: new Date(), + mediaPresentationDuration: 10, + Period_asArray: [{ + AdaptationSet_asArray: [{ + id: 0, mimeType: Constants.AUDIO, + [DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY]: [ + {schemeIdUri: 'urn:mpeg:mpegB:cicp:ChannelConfiguration', value: '6'}, + {schemeIdUri: 'tag:dolby.com,2014:dash:audio_channel_configuration:2011', value: '0xF801'} + ] + }] + }] +}; +const manifest_with_audioChanCfg_Repr = { + loadedTime: new Date(), + mediaPresentationDuration: 10, + Period_asArray: [{ + AdaptationSet_asArray: [{ + id: 0, mimeType: Constants.AUDIO, + [DashConstants.REPRESENTATION_ASARRAY]:[ + { + id: 11, bandwidth: 128000, + [DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY]: [ + {schemeIdUri: 'urn:mpeg:mpegB:cicp:ChannelConfiguration', value: '6'}, + {schemeIdUri: 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011', value: '6'}, + {schemeIdUri: 'tag:dolby.com,2014:dash:audio_channel_configuration:2011', value: '0xF801'} + ] + },{ + id: 12, bandwidth: 96000, + [DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY]: [ + {schemeIdUri: 'urn:mpeg:mpegB:cicp:ChannelConfiguration', value: '21'}, + {schemeIdUri: 'urn:mpeg:mpegB:cicp:ChannelConfiguration', value: '2'}, + {schemeIdUri: 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011', value: '2'}, + {schemeIdUri: 'tag:dolby.com,2014:dash:audio_channel_configuration:2011', value: '0xA000'} + ] + } + ], + [DashConstants.VIEWPOINT_ASARRAY]: [ + {schemeIdUri: 'urn:scheme:viewpoint', value: 'VP1'}, + {schemeIdUri: 'urn:scheme:viewpoint', value: 'VP2'} + ] + }] + }] +}; describe('DashAdapter', function () { describe('SetConfig not previously called', function () { @@ -420,6 +529,7 @@ describe('DashAdapter', function () { track.representationCount = 0; track.lang = 'deu'; track.roles = ['main']; + track.rolesWithSchemeIdUri = [{schemeIdUri:'aScheme', value:'main'}]; track.codec = 'audio/mp4;codecs="mp4a.40.2"'; track.mimeType = 'audio/mp4'; @@ -489,18 +599,214 @@ describe('DashAdapter', function () { expect(streamInfos[0].manifestInfo.serviceDescriptions[0].playbackRate.min).equals(0.5); // jshint ignore:line }); - it('supplemental properties should be empty if not defined', function () { - const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ - id: 'defaultId_0', - index: 0 - }, Constants.VIDEO, manifest_without_supplemental_properties); + describe('mediainfo populated from manifest', function () { + it('supplemental properties should be empty if not defined', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.VIDEO, manifest_without_supplemental_properties); - expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line - expect(mediaInfoArray.length).equals(1); // jshint ignore:line + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].supplementalProperties).not.to.be.null; // jshint ignore:line + expect(Object.keys(mediaInfoArray[0].supplementalProperties).length).equals(0); // jshint ignore:line + + expect(mediaInfoArray[0].supplementalPropertiesAsArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].supplementalPropertiesAsArray.length).equals(0); // jshint ignore:line + }); + + it('supplemental properties should be filled if correctly defined', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.VIDEO, manifest_with_supplemental_properties); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].codec).to.be.null; // jshint ignore:line + + expect(mediaInfoArray[0].supplementalProperties).not.to.be.null; // jshint ignore:line + expect(Object.keys(mediaInfoArray[0].supplementalProperties).length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].supplementalPropertiesAsArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].supplementalPropertiesAsArray.length).equals(2); // jshint ignore:line + }); + + it('supplemental properties should be filled if set on all representations', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.VIDEO, manifest_with_supplemental_properties_on_repr); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].representationCount).equals(2); // jshint ignore:line + expect(mediaInfoArray[0].codec).not.to.be.null; // jshint ignore:line + + expect(mediaInfoArray[0].supplementalProperties).not.to.be.null; // jshint ignore:line + expect(Object.keys(mediaInfoArray[0].supplementalProperties).length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].supplementalPropertiesAsArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].supplementalPropertiesAsArray.length).equals(3); // jshint ignore:line + }); + + it('supplemental properties should not be filled if not set on all representations', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.VIDEO, manifest_with_supplemental_properties_on_only_one_repr); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].representationCount).equals(3); // jshint ignore:line + + expect(mediaInfoArray[0].supplementalProperties).not.to.be.null; // jshint ignore:line + expect(Object.keys(mediaInfoArray[0].supplementalProperties).length).equals(0); // jshint ignore:line + + expect(mediaInfoArray[0].supplementalPropertiesAsArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].supplementalPropertiesAsArray.length).equals(0); // jshint ignore:line + }); + + it('audio channel config should be filled', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.AUDIO, manifest_with_audioChanCfg); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].audioChannelConfiguration).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfiguration.length).equals(2); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfiguration[0]).equals('6'); // jshint ignore:line + + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri.length).equals(2); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri[1].value).equals('0xF801'); // jshint ignore:line + }); + + it('audio channel config should be filled when present on Representation', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.AUDIO, manifest_with_audioChanCfg_Repr); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + // Note: MediaInfo picks those AudioChannelConfig descriptor present on that Representation with lowest bandwidth + expect(mediaInfoArray[0].audioChannelConfiguration).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfiguration.length).equals(4); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfiguration[0]).equals('21'); // jshint ignore:line + + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri.length).equals(4); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(mediaInfoArray[0].audioChannelConfigurationsWithSchemeIdUri[3].value).equals('0xA000'); // jshint ignore:line + }); + + it('role, accessibility and viewpoint should be empty if not defined', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.AUDIO, manifest_with_audioChanCfg); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].roles).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].roles.length).equals(0); // jshint ignore:line + expect(mediaInfoArray[0].accessibility).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].accessibility.length).equals(0); // jshint ignore:line + expect(mediaInfoArray[0].viewpoint).to.be.undefined; // jshint ignore:line + + expect(mediaInfoArray[0].rolesWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].rolesWithSchemeIdUri.length).equals(0); // jshint ignore:line + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri.length).equals(0); // jshint ignore:lineexpect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri.length).equals(0); // jshint ignore:line + }); + + it('role should be filled', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.AUDIO, manifest_with_audio); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(2); // jshint ignore:line + + expect(mediaInfoArray[0].roles).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].roles.length).equals(1); // jshint ignore:line + expect(mediaInfoArray[0].roles[0]).equals('main'); // jshint ignore:line + + expect(mediaInfoArray[0].rolesWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].rolesWithSchemeIdUri.length).equals(1); // jshint ignore:line + expect(mediaInfoArray[0].rolesWithSchemeIdUri[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(mediaInfoArray[0].rolesWithSchemeIdUri[0].value).equals('main'); // jshint ignore:line + }); + + it('accessibility should be filled', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.VIDEO, manifest_with_video_with_embedded_subtitles); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(2); // jshint ignore:line + + expect(mediaInfoArray[0].roles).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].roles.length).equals(0); // jshint ignore:line + + expect(mediaInfoArray[0].accessibility).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].accessibility.length).equals(1); // jshint ignore:line + expect(mediaInfoArray[0].accessibility[0]).equals('cea-608:CC1=eng;CC3=swe'); // jshint ignore:line + expect(mediaInfoArray[1].accessibility.length).equals(0); // jshint ignore:line + + expect(mediaInfoArray[0].rolesWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].rolesWithSchemeIdUri.length).equals(0); // jshint ignore:line + + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri.length).equals(1); // jshint ignore:line + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri[0].schemeIdUri).equals('urn:scte:dash:cc:cea-608:2015'); // jshint ignore:line + expect(mediaInfoArray[0].accessibilitiesWithSchemeIdUri[0].value).equals('CC1=eng;CC3=swe'); // jshint ignore:line + expect(mediaInfoArray[1].accessibilitiesWithSchemeIdUri.length).equals(0); // jshint ignore:line + }); + + it('viewpoint should be filled', function () { + const mediaInfoArray = dashAdapter.getAllMediaInfoForType({ + id: 'defaultId_0', + index: 0 + }, Constants.AUDIO, manifest_with_audioChanCfg_Repr); + + expect(mediaInfoArray).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray.length).equals(1); // jshint ignore:line + + expect(mediaInfoArray[0].viewpoint).equals('VP1'); // jshint ignore:line + + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri).to.be.instanceOf(Array); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri.length).equals(2); // jshint ignore:line + + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[0].schemeIdUri).equals('urn:scheme:viewpoint'); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[0].value).equals('VP1'); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[0].id).to.be.null; // jshint ignore:line + + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[1]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[1].schemeIdUri).equals('urn:scheme:viewpoint'); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[1].value).equals('VP2'); // jshint ignore:line + expect(mediaInfoArray[0].viewpointsWithSchemeIdUri[1].id).to.be.null; // jshint ignore:line + }); - expect(mediaInfoArray[0].supplementalProperties).not.to.be.null; // jshint ignore:line - expect(Object.keys(mediaInfoArray[0].supplementalProperties).length).equals(0); // jshint ignore:line }); + }); describe('getPatchLocation', function () { @@ -1156,4 +1462,58 @@ describe('DashAdapter', function () { }); }); }); + + describe('areMediaInfosEqual', function () { + var mediaInfo1; + + beforeEach(function () { + var manifest_1 = { + loadedTime: new Date(), + mediaPresentationDuration: 10, + Period_asArray: [{ + AdaptationSet_asArray: [ + { + id: 0, mimeType: Constants.VIDEO, + Role_asArray:[], + Accessibility_asArray: [{ schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'description' }], + SupplementalProperty_asArray: [{schemeIdUri: 'test:scheme', value: 'value1'},{schemeIdUri: 'test:scheme', value: 'value2'}], + [DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY]: [ + {schemeIdUri: 'tag:dolby.com,2014:dash:audio_channel_configuration:2011', value: '0xF801'} + ] + }, { + id: 1, mimeType: Constants.VIDEO, + Role_asArray:[{schemeIdUri: 'urn:mpeg:dash:role:2011', value: 'main'}], + Accessibility_asArray: [], + SupplementalProperty_asArray: [{schemeIdUri: 'test:scheme', value: 'value1'},{schemeIdUri: 'test:scheme', value: 'value4'}], + [DashConstants.AUDIOCHANNELCONFIGURATION_ASARRAY]: [ + {schemeIdUri: 'urn:mpeg:mpegB:cicp:ChannelConfiguration', value: '6'} + ] + } + ] + }] + }; + mediaInfo1 = dashAdapter.getAllMediaInfoForType( + {id: 'defaultId_0', index: 0}, Constants.VIDEO, + manifest_1); + }); + + it('should return false if 1st MediaInfo is not set', function () { + var result = dashAdapter.areMediaInfosEqual(null, mediaInfo1[0]); + expect(result).to.be.false; + }); + it('should return false if 2nd MediaInfo is not set', function () { + var result = dashAdapter.areMediaInfosEqual(mediaInfo1[0], null); + expect(result).to.be.false; + }); + + it('should return true if MediaInfos are equal', function () { + var result = dashAdapter.areMediaInfosEqual(mediaInfo1[0], mediaInfo1[0]); + expect(result).to.be.true; + }); + it('should return false if MediaInfos are not equal', function () { + var result = dashAdapter.areMediaInfosEqual(mediaInfo1[0], mediaInfo1[1]); + expect(result).to.be.false; + }); + }); + }); diff --git a/test/unit/dash.constants.DashConstants.js b/test/unit/dash.constants.DashConstants.js index 0c6be60bbf..7313c29558 100644 --- a/test/unit/dash.constants.DashConstants.js +++ b/test/unit/dash.constants.DashConstants.js @@ -57,6 +57,7 @@ describe('DashConstants', function () { expect(DashConstants.CONTENT_PROTECTION).to.equal('ContentProtection'); expect(DashConstants.ESSENTIAL_PROPERTY).to.equal('EssentialProperty'); expect(DashConstants.SUPPLEMENTAL_PROPERTY).to.equal('SupplementalProperty'); + expect(DashConstants.SUPPLEMENTAL_PROPERTY_ASARRAY).to.equal('SupplementalProperty_asArray'); expect(DashConstants.INBAND_EVENT_STREAM).to.equal('InbandEventStream'); expect(DashConstants.PRODUCER_REFERENCE_TIME).to.equal('ProducerReferenceTime'); expect(DashConstants.ACCESSIBILITY).to.equal('Accessibility'); @@ -66,6 +67,7 @@ describe('DashConstants', function () { expect(DashConstants.SUBSET).to.equal('Subset'); expect(DashConstants.LANG).to.equal('lang'); expect(DashConstants.VIEWPOINT).to.equal('Viewpoint'); + expect(DashConstants.VIEWPOINT_ASARRAY).to.equal('Viewpoint_asArray'); expect(DashConstants.ROLE_ASARRAY).to.equal('Role_asArray'); expect(DashConstants.REPRESENTATION_ASARRAY).to.equal('Representation_asArray'); expect(DashConstants.PRODUCERREFERENCETIME_ASARRAY).to.equal('ProducerReferenceTime_asArray'); diff --git a/test/unit/dash.models.DashManifestModel.js b/test/unit/dash.models.DashManifestModel.js index bae9248e80..c568126267 100644 --- a/test/unit/dash.models.DashManifestModel.js +++ b/test/unit/dash.models.DashManifestModel.js @@ -7,6 +7,7 @@ import MpdHelper from './helpers/MPDHelper'; import VoHelper from './helpers/VOHelper'; import ErrorHandlerMock from './mocks/ErrorHandlerMock'; +import DescriptorType from '../../src/dash/vo/DescriptorType'; const expect = require('chai').expect; @@ -112,10 +113,11 @@ describe('DashManifestModel', function () { expect(language).to.equal(EMPTY_STRING); // jshint ignore:line }); - it('should return null when getViewpointForAdaptation is called and adaptation is undefined', () => { + it('should return an empty array when getViewpointForAdaptation is called and adaptation is undefined', () => { const viewPoint = dashManifestModel.getViewpointForAdaptation(); - expect(viewPoint).to.be.null; // jshint ignore:line + expect(viewPoint).to.be.instanceOf(Array); // jshint ignore:line + expect(viewPoint).to.be.empty; // jshint ignore:line }); it('should return an empty array when getAudioChannelConfigurationForAdaptation is called and adaptation is undefined', () => { @@ -139,6 +141,58 @@ describe('DashManifestModel', function () { expect(rolesArray).to.be.empty; // jshint ignore:line }); + it('should return an empty array when getSupplementalPropertiesForAdaptation', () => { + const suppPropArray = dashManifestModel.getSupplementalPropertiesForAdaptation(); + + expect(suppPropArray).to.be.instanceOf(Object); // jshint ignore:line + expect(suppPropArray).to.be.empty; // jshint ignore:line + }); + + it('should return an empty array when getSupplementalPropertiesAsArrayForAdaptation', () => { + const suppPropArray = dashManifestModel.getSupplementalPropertiesAsArrayForAdaptation(); + + expect(suppPropArray).to.be.instanceOf(Array); // jshint ignore:line + expect(suppPropArray).to.be.empty; // jshint ignore:line + }); + + it('should return correct array of DescriptorType when getSupplementalPropertiesAsArrayForAdaptation is called', () => { + const suppPropArray = dashManifestModel.getSupplementalPropertiesAsArrayForAdaptation({ + SupplementalProperty_asArray: [{schemeIdUri: 'test.scheme', value: 'testVal'},{schemeIdUri: 'test.scheme', value: 'test2Val'}] + }); + + expect(suppPropArray).to.be.instanceOf(Array); // jshint ignore:line + expect(suppPropArray[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(suppPropArray[0].schemeIdUri).equals('test.scheme'); // jshint ignore:line + expect(suppPropArray[0].value).equals('testVal'); // jshint ignore:line + expect(suppPropArray[1].schemeIdUri).equals('test.scheme'); // jshint ignore:line + expect(suppPropArray[1].value).equals('test2Val'); // jshint ignore:line + }); + + it('should return an empty array when getSupplementalPropertiesForRepresentation', () => { + const suppPropArray = dashManifestModel.getSupplementalPropertiesForRepresentation(); + + expect(suppPropArray).to.be.instanceOf(Object); // jshint ignore:line + expect(suppPropArray).to.be.empty; // jshint ignore:line + }); + + it('should return an empty array when getSupplementalPropertiesAsArrayForRepresentation', () => { + const suppPropArray = dashManifestModel.getSupplementalPropertiesAsArrayForRepresentation(); + + expect(suppPropArray).to.be.instanceOf(Array); // jshint ignore:line + expect(suppPropArray).to.be.empty; // jshint ignore:line + }); + + it('should return correct array of DescriptorType when getSupplementalPropertiesAsArrayForRepresentation is called', () => { + const suppPropArray = dashManifestModel.getSupplementalPropertiesAsArrayForRepresentation({ + SupplementalProperty_asArray: [{schemeIdUri: 'test.scheme', value: 'testVal'}] + }); + + expect(suppPropArray).to.be.instanceOf(Array); // jshint ignore:line + expect(suppPropArray[0]).to.be.instanceOf(DescriptorType); // jshint ignore:line + expect(suppPropArray[0].schemeIdUri).equals('test.scheme'); // jshint ignore:line + expect(suppPropArray[0].value).equals('testVal'); // jshint ignore:line + }); + it('should return null when getAdaptationForId is called and id, manifest and periodIndex are undefined', () => { const adaptation = dashManifestModel.getAdaptationForId(undefined, undefined, undefined); diff --git a/test/unit/streaming.controllers.MediaController.js b/test/unit/streaming.controllers.MediaController.js index 4b7ad3ec15..a4e5ea5023 100644 --- a/test/unit/streaming.controllers.MediaController.js +++ b/test/unit/streaming.controllers.MediaController.js @@ -96,6 +96,32 @@ describe('MediaController', function () { }); + it('should return false if track are not equals (DescriptorType)', function () { + + let track1 = { + id: 'id', + viewpoint: 'viewpoint', + lang: 'lang', + roles: 1, + accessibility: 'description', + accessibilitiesWithSchemeIdUri: {schemeIdUri: 'urn:scheme:test:1:2023', value: 'description'}, + audioChannelConfiguration: 1 + }; + + let track2 = { + id: 'id', + viewpoint: 'viewpoint', + lang: 'lang', + roles: 1, + accessibility: 'description', + accessibilitiesWithSchemeIdUri: {schemeIdUri: 'urn:scheme:test:2:2023', value: 'description'}, + audioChannelConfiguration: 1 + }; + let equal = mediaController.isTracksEqual(track1, track2); + expect(equal).to.be.false; // jshint ignore:line + + }); + it('should return true if track are equals', function () { let track1 = { @@ -161,6 +187,32 @@ describe('MediaController', function () { expect(equal).to.be.true; // jshint ignore:line }); + it('should return true if track are equals (DescriptorType)', function () { + + let track1 = { + id: 'id', + viewpoint: 'viewpoint', + lang: 'lang', + roles: 1, + accessibility: 'description', + accessibility_withSchemeIdUri: [{schemeIdUri: 'urn:scheme:test:1:2023', value: 'description'}], + audioChannelConfiguration: 1 + }; + + let track2 = { + id: 'id', + viewpoint: 'viewpoint', + lang: 'lang', + roles: 1, + accessibility: 'description', + accessibility_withSchemeIdUri: [{schemeIdUri: 'urn:scheme:test:1:2023', value: 'description'}], + audioChannelConfiguration: 1 + }; + let equal = mediaController.isTracksEqual(track1, track2); + expect(equal).to.be.true; // jshint ignore:line + + }); + }); describe('Track Management', function () {