Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6fa577f
Allow the channels to be query by MediaCapabilities.
gmcgarry Sep 19, 2025
b5d6fe7
From feedback to preserve the existing behaviour to ignore the LFE ch…
gmcgarry Sep 26, 2025
ce4e2b5
Fixed botched merge
gmcgarry Nov 11, 2025
f29ba6e
Add tests for filtering on number of channels.
gmcgarry Nov 11, 2025
d72102f
Add debug symbols.
gmcgarry Nov 11, 2025
c290b75
add sampling rate to test query
gmcgarry Nov 11, 2025
7f762f4
Fix filter of Preselections.
gmcgarry Nov 12, 2025
6190c6e
Support AudioChannelConfiguration on Preselections. Add tests.
gmcgarry Nov 12, 2025
01021da
Fix lint failures.
gmcgarry Nov 12, 2025
b27fc70
rename variable for better readability
stschr Nov 27, 2025
fb58b41
process HDR properties from CommonRepresentation with Preselections
stschr Nov 27, 2025
5c062f2
Merge branch 'sschr/20251127_prsl_isCodecSupported_fix' into dolby/fi…
gmcgarry Nov 28, 2025
518882d
Fix merge conflicts.
gmcgarry Nov 28, 2025
f0af8d1
Fix incorrect test for missing AudioChannelConfiguration on Preselection
gmcgarry Nov 28, 2025
3aa0415
Remove test for component-based preselections
gmcgarry Nov 28, 2025
82c41eb
Add generic preselection test. Add additional test if AudioChannelCo…
gmcgarry Nov 28, 2025
04e8a17
proposed fixes to unit test #1
stschr Nov 28, 2025
3c4016d
proposed fixes to unit test #2
stschr Nov 28, 2025
8ab7004
proposed new unit test #3, where a supported Preselectionrefers to an…
stschr Nov 28, 2025
1751297
enhancing new unit test #3, Preselection now gets removed if it refer…
stschr Nov 28, 2025
18a4310
bugfixes as identified by new test case #3
stschr Nov 28, 2025
23d482f
Merge remote-tracking branch 'origin/development' into dolby/filter-c…
gmcgarry Dec 1, 2025
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
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1682,7 +1682,8 @@ export class MediaPlayerSettingClass {
],
useMediaCapabilitiesApi?: boolean,
filterHDRMetadataFormatEssentialProperties?: boolean,
filterVideoColorimetryEssentialProperties?: boolean
filterVideoColorimetryEssentialProperties?: boolean,
filterAudioChannelConfiguration?: boolean
},
events?: {
eventControllerRefreshDelay?: number,
Expand Down
8 changes: 6 additions & 2 deletions src/core/Settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,8 @@ import SwitchRequest from '../streaming/rules/SwitchRequest.js';
* ],
* useMediaCapabilitiesApi: true,
* filterVideoColorimetryEssentialProperties: false,
* filterHDRMetadataFormatEssentialProperties: false
* filterHDRMetadataFormatEssentialProperties: false,
* filterAudioChannelConfiguration: false
* },
* events: {
* eventControllerRefreshDelay: 100,
Expand Down Expand Up @@ -727,6 +728,8 @@ import SwitchRequest from '../streaming/rules/SwitchRequest.js';
* If disabled, registered properties per supportedEssentialProperties will be allowed without any further checking (including 'urn:mpeg:mpegB:cicp:MatrixCoefficients').
* @property {boolean} [filterHDRMetadataFormatEssentialProperties=false]
* Enable dash.js to query MediaCapabilities API for signalled HDR-MetadataFormat EssentialProperty (per schemeIdUri:'urn:dvb:dash:hdr-dmi').
* @property {boolean} [filterAudioChannelConfiguration=false]
* Enable dash.js to query MediaCapabilities API for signalled AudioChannelConfiguration.
*/

/**
Expand Down Expand Up @@ -1156,7 +1159,8 @@ function Settings() {
],
useMediaCapabilitiesApi: true,
filterVideoColorimetryEssentialProperties: false,
filterHDRMetadataFormatEssentialProperties: false
filterHDRMetadataFormatEssentialProperties: false,
filterAudioChannelConfiguration: false
},
events: {
eventControllerRefreshDelay: 100,
Expand Down
72 changes: 39 additions & 33 deletions src/streaming/utils/AudioChannelConfiguration.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,26 @@
// derived from ISO/IEC 23091-3
const _mapping_CICP = {
'0': undefined,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 5,
'7': 7,
'8': 2,
'9': 3,
'10': 4,
'11': 6,
'12': 7,
'13': 22,
'14': 7,
'15': 10,
'16': 9,
'17': 11,
'18': 13,
'19': 11,
'20': 13
'1': { channels: 1, lfe: 0 },
'2': { channels: 2, lfe: 0 },
'3': { channels: 3, lfe: 0 },
'4': { channels: 4, lfe: 0 },
'5': { channels: 5, lfe: 0 },
'6': { channels: 5, lfe: 1 },
'7': { channels: 7, lfe: 1 },
'8': { channels: 2, lfe: 0 },
'9': { channels: 3, lfe: 0 },
'10': { channels: 4, lfe: 0 },
'11': { channels: 6, lfe: 1 },
'12': { channels: 7, lfe: 1 },
'13': { channels: 22, lfe: 2 },
'14': { channels: 7, lfe: 1 },
'15': { channels: 10, lfe: 2 },
'16': { channels: 9, lfe: 1 },
'17': { channels: 11, lfe: 1 },
'18': { channels: 13, lfe: 1 },
'19': { channels: 11, lfe: 1 },
'20': { channels: 13, lfe: 1 },
};

function _countBits(n) {
Expand All @@ -71,18 +71,21 @@ function _getNChanFromBitMask(value, masks) {
return nChan;
}

function _getNChanDolby2011(value) {
function _getNChanDolby2011(value, includeLFE) {
if ( value.length !== 4 ) {
return undefined;
}

// see ETSI TS 103190-1, table F.1:
// 0b1111100110001000: single channel flags
// 0b0000011001110000: channel pair flags
return _getNChanFromBitMask(value, [0b1111100110001000, 0b0000011001110000]);
// 0b0000000000000110: LFE channels
const single_channel_flags = 0b1111100110001000 + (includeLFE ? 0b0000000000000110 : 0);
const channel_pair_flags = 0b0000011001110000;
return _getNChanFromBitMask(value, [single_channel_flags, channel_pair_flags]);
}

function _getNChanDolby2015(value) {
function _getNChanDolby2015(value, includeLFE) {
if ( value.length !== 6 ) {
return undefined;
}
Expand All @@ -95,21 +98,24 @@ function _getNChanDolby2015(value) {
// see ETSI TS 103190-2, table A.27
// 0b001100111000000010: single channel flags
// 0b110010000110111101: channel pair flags
// 0b000001000001000000: LFE - excluded
return _getNChanFromBitMask(value, [0b001100111000000010, 0b110010000110111101]);
// 0b000001000001000000: LFE channels
const single_channel_flags = 0b001101111000000010 + (includeLFE ? 0b000001000001000000 : 0);
const channel_pair_flags = 0b110010000110111101;
return _getNChanFromBitMask(value, [single_channel_flags, channel_pair_flags]);
}

function _getNChanDTSUHD(value) {
function _getNChanDTSUHD(value, includeLFE) {
if ( value.length > 8 ) {
return undefined;
}

// see ETSI TS 103491, table B-5
// LFE to exclude: 0x00010000 + 0x00000020
return _getNChanFromBitMask(value, [0xFFFEFFDF, 0x00000000]);
// LFE: 0x00010000 + 0x00000020
const mask = 0xFFFEFFDF + (includeLFE ? 0x00010020 : 0)
return _getNChanFromBitMask(value, [mask, 0x00000000]);
}

function getNChanFromAudioChannelConfig(audioChannelConfiguration) {
function getNChanFromAudioChannelConfig(audioChannelConfiguration, includeLFE = false) {
let nChan = undefined;

if ( !audioChannelConfiguration || !audioChannelConfiguration.schemeIdUri || !audioChannelConfiguration.value ) {
Expand All @@ -121,15 +127,15 @@ function getNChanFromAudioChannelConfig(audioChannelConfiguration) {

if (scheme === 'urn:mpeg:dash:23003:3:audio_channel_configuration:2011' || scheme === 'urn:mpeg:mpegB:cicp:ChannelConfiguration') {
// see ISO/IEC 23091-3
nChan = _mapping_CICP[value];
nChan = _mapping_CICP[value] && (_mapping_CICP[value].channels + (includeLFE ? _mapping_CICP[value].lfe : 0));
} else if (scheme === 'tag:dolby.com,2014:dash:audio_channel_configuration:2011') {
nChan = _getNChanDolby2011(value);
nChan = _getNChanDolby2011(value, includeLFE);
} else if (scheme === 'tag:dolby.com,2015:dash:audio_channel_configuration:2015') {
nChan = _getNChanDolby2015(value);
nChan = _getNChanDolby2015(value, includeLFE);
} else if (scheme === 'tag:dts.com,2014:dash:audio_channel_configuration:2012') {
nChan = parseInt(value); // per ETSI TS 102 114,table G.2, this includes LFE
} else if (scheme === 'tag:dts.com,2018:uhd:audio_channel_configuration') {
nChan = _getNChanDTSUHD(value);
nChan = _getNChanDTSUHD(value, includeLFE);
}
return nChan;
}
Expand Down
3 changes: 3 additions & 0 deletions src/streaming/utils/Capabilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,9 @@ function Capabilities() {
if (inputConfig.samplerate) {
configuration.audio.samplerate = inputConfig.samplerate;
}
if (inputConfig.channels) {
configuration.audio.channels = inputConfig.channels;
}

return configuration
}
Expand Down
50 changes: 38 additions & 12 deletions src/streaming/utils/CapabilitiesFilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import EventBus from '../../core/EventBus.js';
import Events from '../../core/events/Events.js';
import DashConstants from '../../dash/constants/DashConstants.js';

import getNChanFromAudioChannelConfig from './AudioChannelConfiguration.js';

function CapabilitiesFilter() {

const context = this.context;
Expand Down Expand Up @@ -135,7 +137,7 @@ function CapabilitiesFilter() {
if (codec) {
let repr = adapter.getCommonRepresentationForPreselection(prsl, period.AdaptationSet);

isPrslCodecSupported = _isCodecSupported(type, repr, codec);
isPrslCodecSupported = _isCodecSupported(type, prsl, codec, repr);
}

if (!isPrslCodecSupported) {
Expand Down Expand Up @@ -366,24 +368,48 @@ function CapabilitiesFilter() {
}

function _createAudioConfiguration(rep, codec, prslRep) {
var samplerate = rep ? rep.audioSamplingRate || null : null;
var bitrate = rep ? rep.bandwidth || null : null;
let cfg = {
codec,
samplerate: rep ? rep.audioSamplingRate || null : null,
bitrate: rep ? rep.bandwidth || null : null,
isSupported: true,
};

if (rep.tagName === DashConstants.PRESELECTION && prslRep) {
if (!samplerate) {
samplerate = prslRep.audioSamplingRate || null;
if (!cfg.samplerate) {
cfg.samplerate = prslRep.audioSamplingRate || null;
}
if (!bitrate) {
bitrate = prslRep.bandwidth || null;
if (!cfg.bitrate) {
cfg.bitrate = prslRep.bandwidth || null;
}
}

if (settings.get().streaming.capabilities.filterAudioChannelConfiguration) {
Object.assign(cfg, _convertAudioChannelConfigurationToConfig(rep, prslRep))
}

return cfg;
}

function _convertAudioChannelConfigurationToConfig(representation, prsl) {

let audioChannelConfigs = representation[DashConstants.AUDIO_CHANNEL_CONFIGURATION] || [];
let channels = null;

if (!audioChannelConfigs && prsl) {
audioChannelConfigs = prsl[DashConstants.AUDIO_CHANNEL_CONFIGURATION];
}

const channelCounts = audioChannelConfigs.map(channelConfig => getNChanFromAudioChannelConfig(channelConfig, true));

// ensure that all AudioChannelConfiguration elements are the same value, otherwise ignore
if (channelCounts.every(e => e == channelCounts[0])) {
channels = channelCounts[0];
}

return {
codec,
bitrate,
samplerate,
isSupported: true
};
channels
}
}

function _addGenericAttributesToConfig(rep, config) {
Expand Down
25 changes: 25 additions & 0 deletions test/unit/mocks/AdapterMock.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,31 @@ function AdapterMock() {
return true
}

this.getPreselectionIsTypeOf = function (preselection, adaptations, type) {
if (preselection.mimeType) {
return preselection.mimeType.startsWith(type)
}
if (adaptations[0].mimeType) {
return adaptations[0].mimeType.startsWith(type)
}
return adaptations[0].Representation[0].mimeType.startsWith(type)
}

this.getCodecForPreselection = function (preselection, adaptations) {
if (preselection.codecs) {
return 'audio/mp4;codecs="' + preselection.codecs + '\"'
}
if (adaptations[0].codec) {
return 'audio/mp4;codecs="' + adaptations.codecs + '"'
}
return 'audio/mp4;codecs="' + adaptations[0].Representation[0].codecs + '"'
}

this.getCommonRepresentationForPreselection = function (preselection, adaptations) {
const id = preselection.preselectionComponents.split(' ')[0]
return adaptations.find((as) => as.id == id).Representation[0]
}

}

export default AdapterMock;
Loading
Loading