Skip to content

Commit 8425e6a

Browse files
authored
Optimize handling of supplemental codecs (Dash-Industry-Forum#4610)
1 parent e3c7af5 commit 8425e6a

File tree

3 files changed

+166
-37
lines changed

3 files changed

+166
-37
lines changed

src/streaming/utils/CapabilitiesFilter.js

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -123,39 +123,46 @@ function CapabilitiesFilter() {
123123
if (!as.Representation || as.Representation.length === 0) {
124124
return;
125125
}
126-
const configurations = [];
127126

128127
as.Representation = as.Representation.filter((rep, i) => {
129128
const codec = adapter.getCodec(as, i, false);
130-
const config = _createConfiguration(type, rep, codec);
131-
configurations.push(config);
132-
const isCodecSupported = capabilities.isCodecSupportedBasedOnTestedConfigurations(config, type);
129+
const isMainCodecSupported = _isCodecSupported(type, rep, codec);
133130

134-
let isSupplementalCodecSupported = false;
135-
const supplementalCodecs = adapter.getSupplementalCodecs(rep);
136-
if (supplementalCodecs.length > 0) {
137-
if (supplementalCodecs.length > 1) {
138-
logger.warn(`[CapabilitiesFilter] Multiple supplemental codecs not supported; using first in list`);
139-
}
140-
const supplementalCodec = supplementalCodecs[0];
141-
const supplementalCodecConfig = _createConfiguration(type, rep, supplementalCodec);
142-
configurations.push(supplementalCodecConfig);
143-
isSupplementalCodecSupported = capabilities.isCodecSupportedBasedOnTestedConfigurations(supplementalCodecConfig, type);
144-
if (isSupplementalCodecSupported) {
145-
logger.debug(`[CapabilitiesFilter] Codec ${supplementalCodec} supported. Upgrading Representation with ID ${rep.id}`);
146-
// overriding default codec
147-
rep.codecs = rep[DashConstants.SUPPLEMENTAL_CODECS]
148-
}
131+
let isSupplementalCodecSupported = _isSupplementalCodecSupported(rep, type);
132+
if (isSupplementalCodecSupported) {
133+
logger.debug(`[CapabilitiesFilter] Codec supported. Upgrading codecs string of Representation with ID ${rep.id}`);
134+
rep.codecs = rep[DashConstants.SUPPLEMENTAL_CODECS]
149135
}
150136

151-
if (!isCodecSupported && !isSupplementalCodecSupported) {
152-
logger.debug(`[CapabilitiesFilter] Codec ${codec} not supported. Removing Representation with ID ${rep.id}`);
137+
if (!isMainCodecSupported && !isSupplementalCodecSupported) {
138+
logger.warn(`[CapabilitiesFilter] Codec ${codec} not supported. Removing Representation with ID ${rep.id}`);
153139
}
154140

155-
return isCodecSupported || isSupplementalCodecSupported;
141+
return isMainCodecSupported || isSupplementalCodecSupported;
156142
});
157143
}
158144

145+
function _isSupplementalCodecSupported(rep, type) {
146+
let isSupplementalCodecSupported = false;
147+
const supplementalCodecs = adapter.getSupplementalCodecs(rep);
148+
149+
if (supplementalCodecs.length > 0) {
150+
if (supplementalCodecs.length > 1) {
151+
logger.warn(`[CapabilitiesFilter] Multiple supplemental codecs not supported; using the first in list`);
152+
}
153+
const supplementalCodec = supplementalCodecs[0];
154+
isSupplementalCodecSupported = _isCodecSupported(type, rep, supplementalCodec);
155+
}
156+
157+
return isSupplementalCodecSupported
158+
}
159+
160+
function _isCodecSupported(type, rep, codec) {
161+
const config = _createConfiguration(type, rep, codec);
162+
163+
return capabilities.isCodecSupportedBasedOnTestedConfigurations(config, type);
164+
}
165+
159166
function _getConfigurationsToCheck(manifest, type) {
160167
if (!manifest || !manifest.Period || manifest.Period.length === 0) {
161168
return [];
@@ -169,22 +176,11 @@ function CapabilitiesFilter() {
169176
if (adapter.getIsTypeOf(as, type)) {
170177
as.Representation.forEach((rep, i) => {
171178
const codec = adapter.getCodec(as, i, false);
172-
const config = _createConfiguration(type, rep, codec);
173-
const configString = JSON.stringify(config);
174-
175-
if (!configurationsSet.has(configString)) {
176-
configurationsSet.add(configString);
177-
configurations.push(config);
178-
}
179+
_processCodecToCheck(type, rep, codec, configurationsSet, configurations);
179180

180181
const supplementalCodecs = adapter.getSupplementalCodecs(rep)
181182
if (supplementalCodecs.length > 0) {
182-
const config = _createConfiguration(type, rep, supplementalCodecs[0]);
183-
const configString = JSON.stringify(config);
184-
if (!configurationsSet.has(configString)) {
185-
configurationsSet.add(configString);
186-
configurations.push(config);
187-
}
183+
_processCodecToCheck(type, rep, supplementalCodecs[0], configurationsSet, configurations);
188184
}
189185
});
190186
}
@@ -194,6 +190,15 @@ function CapabilitiesFilter() {
194190
return configurations;
195191
}
196192

193+
function _processCodecToCheck(type, rep, codec, configurationsSet, configurations) {
194+
const config = _createConfiguration(type, rep, codec);
195+
const configString = JSON.stringify(config);
196+
197+
if (!configurationsSet.has(configString)) {
198+
configurationsSet.add(configString);
199+
configurations.push(config);
200+
}
201+
}
197202

198203
function _createConfiguration(type, rep, codec) {
199204
let config = null;

test/unit/mocks/AdapterMock.js

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import DashConstants from '../../../src/dash/constants/DashConstants.js';
2+
13
function AdapterMock() {
24
this.metricsList = {
35
BUFFER_STATE: 'BUFFER_STATE'
@@ -161,8 +163,12 @@ function AdapterMock() {
161163
return codec;
162164
};
163165

164-
this.getSupplementalCodecs = function () {
165-
return [];
166+
this.getSupplementalCodecs = function (representation) {
167+
const supplementalCodecs = representation[DashConstants.SUPPLEMENTAL_CODECS];
168+
if (!supplementalCodecs) {
169+
return [];
170+
}
171+
return supplementalCodecs.split(' ').map((codec) => representation.mimeType + ';codecs="' + codec + '"');
166172
}
167173

168174
this.getEssentialPropertiesForRepresentation = function (realRepresentation) {

test/unit/test/streaming/streaming.utils.CapabilitiesFilter.js

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,124 @@ describe('CapabilitiesFilter', function () {
148148
done(e);
149149
});
150150
});
151+
152+
it('should filter Representations with unsupported main codec and unsupported supplemental codec', function (done) {
153+
const manifest = {
154+
Period: [{
155+
AdaptationSet: [{
156+
mimeType: 'audio/mp4',
157+
Representation: [
158+
{
159+
mimeType: 'audio/mp4',
160+
codecs: 'mp4a.40.1',
161+
audioSamplingRate: '48000',
162+
'scte214:supplementalCodecs': 'dvh1.08.01'
163+
},
164+
{
165+
mimeType: 'audio/mp4',
166+
codecs: 'mp4a.40.2',
167+
audioSamplingRate: '48000'
168+
}
169+
]
170+
}]
171+
}]
172+
};
173+
174+
prepareCapabilitiesMock({
175+
name: 'isCodecSupportedBasedOnTestedConfigurations', definition: function (config) {
176+
return config.codec === 'audio/mp4;codecs="mp4a.40.2"';
177+
}
178+
});
179+
180+
capabilitiesFilter.filterUnsupportedFeatures(manifest)
181+
.then(() => {
182+
expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1);
183+
expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(1);
184+
done();
185+
})
186+
.catch((e) => {
187+
done(e);
188+
});
189+
});
190+
191+
it('should keep Representations with unsupported main codec but supported supplemental codec', function (done) {
192+
const manifest = {
193+
Period: [{
194+
AdaptationSet: [{
195+
mimeType: 'audio/mp4',
196+
Representation: [
197+
{
198+
mimeType: 'audio/mp4',
199+
codecs: 'mp4a.40.1',
200+
audioSamplingRate: '48000',
201+
'scte214:supplementalCodecs': 'dvh1.08.01'
202+
},
203+
{
204+
mimeType: 'audio/mp4',
205+
codecs: 'mp4a.40.2',
206+
audioSamplingRate: '48000'
207+
}
208+
]
209+
}]
210+
}]
211+
};
212+
213+
prepareCapabilitiesMock({
214+
name: 'isCodecSupportedBasedOnTestedConfigurations', definition: function (config) {
215+
return config.codec === 'audio/mp4;codecs="mp4a.40.2"' || config.codec === 'audio/mp4;codecs="dvh1.08.01"';
216+
}
217+
});
218+
219+
capabilitiesFilter.filterUnsupportedFeatures(manifest)
220+
.then(() => {
221+
expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1);
222+
expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(2);
223+
expect(manifest.Period[0].AdaptationSet[0].Representation[0].codecs).to.be.equal(manifest.Period[0].AdaptationSet[0].Representation[0]['scte214:supplementalCodecs']);
224+
done();
225+
})
226+
.catch((e) => {
227+
done(e);
228+
});
229+
});
230+
231+
it('should keep Representations with supported main codec but unsupported supplemental codec', function (done) {
232+
const manifest = {
233+
Period: [{
234+
AdaptationSet: [{
235+
mimeType: 'audio/mp4',
236+
Representation: [
237+
{
238+
mimeType: 'audio/mp4',
239+
codecs: 'mp4a.40.1',
240+
audioSamplingRate: '48000',
241+
'scte214:supplementalCodecs': 'dvh1.08.01'
242+
},
243+
{
244+
mimeType: 'audio/mp4',
245+
codecs: 'mp4a.40.2',
246+
audioSamplingRate: '48000'
247+
}
248+
]
249+
}]
250+
}]
251+
};
252+
253+
prepareCapabilitiesMock({
254+
name: 'isCodecSupportedBasedOnTestedConfigurations', definition: function (config) {
255+
return config.codec === 'audio/mp4;codecs="mp4a.40.2"' || config.codec === 'audio/mp4;codecs="mp4a.40.1"';
256+
}
257+
});
258+
259+
capabilitiesFilter.filterUnsupportedFeatures(manifest)
260+
.then(() => {
261+
expect(manifest.Period[0].AdaptationSet).to.have.lengthOf(1);
262+
expect(manifest.Period[0].AdaptationSet[0].Representation).to.have.lengthOf(2);
263+
done();
264+
})
265+
.catch((e) => {
266+
done(e);
267+
});
268+
});
151269
});
152270

153271
describe('filter codecs using essentialProperties', function () {

0 commit comments

Comments
 (0)