Skip to content

Commit

Permalink
fix: Fix chooseCodecsAndFilterManifest for some HLS manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Oct 24, 2023
1 parent f81a16a commit 179c285
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 30 deletions.
4 changes: 4 additions & 0 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1304,6 +1304,7 @@ shaka.hls.HlsParser = class {
if (!ignoreStream) {
const streamInfo =
this.createStreamInfoFromVariantTag_(tag, allCodecs, type);
streamInfo.stream.groupId = groupId;
res[streamInfo.stream.type] = [streamInfo];
}
return res;
Expand Down Expand Up @@ -1674,6 +1675,9 @@ shaka.hls.HlsParser = class {
verbatimMediaPlaylistUri, codecs, type, language, primary, name,
channelsCount, /* closedCaptions= */ null, characteristics, forced,
sampleRate, spatialAudio);
if (streamInfo.stream) {
streamInfo.stream.groupId = groupId;
}
if (this.groupIdToStreamInfosMap_.has(groupId)) {
this.groupIdToStreamInfosMap_.get(groupId).push(streamInfo);
} else {
Expand Down
33 changes: 20 additions & 13 deletions lib/media/adaptation_set.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,11 @@ shaka.media.AdaptationSet = class {
* Variants that may be compatible with the root and should be added if
* compatible. If a candidate is not compatible, it will not end up in the
* adaptation set.
* @param {boolean=} compareCodecs
* @param {boolean=} enableAudioGroups
*/
constructor(root, candidates) {
constructor(root, candidates, compareCodecs = true,
enableAudioGroups = false) {
/** @private {shaka.extern.Variant} */
this.root_ = root;
/** @private {!Set.<shaka.extern.Variant>} */
Expand All @@ -37,17 +40,18 @@ shaka.media.AdaptationSet = class {
// are not compatible with the root, they will be rejected by |add|.
candidates = candidates || [];
for (const candidate of candidates) {
this.add(candidate);
this.add(candidate, compareCodecs, enableAudioGroups);
}
}

/**
* @param {shaka.extern.Variant} variant
* @param {boolean=} compareCodecs
* @param {boolean} compareCodecs
* @param {boolean} enableAudioGroups
* @return {boolean}
*/
add(variant, compareCodecs = true) {
if (this.canInclude(variant, compareCodecs)) {
add(variant, compareCodecs, enableAudioGroups) {
if (this.canInclude(variant, compareCodecs, enableAudioGroups)) {
this.variants_.add(variant);
return true;
}
Expand All @@ -64,20 +68,22 @@ shaka.media.AdaptationSet = class {
*
* @param {shaka.extern.Variant} variant
* @param {boolean=} compareCodecs
* @param {boolean=} enableAudioGroups
* @return {boolean}
*/
canInclude(variant, compareCodecs = true) {
canInclude(variant, compareCodecs = true, enableAudioGroups = false) {
return shaka.media.AdaptationSet
.areAdaptable(this.root_, variant, compareCodecs);
.areAdaptable(this.root_, variant, compareCodecs, enableAudioGroups);
}

/**
* @param {shaka.extern.Variant} a
* @param {shaka.extern.Variant} b
* @param {boolean=} compareCodecs
* @param {boolean} compareCodecs
* @param {boolean} enableAudioGroups
* @return {boolean}
*/
static areAdaptable(a, b, compareCodecs = true) {
static areAdaptable(a, b, compareCodecs, enableAudioGroups) {
const AdaptationSet = shaka.media.AdaptationSet;

// All variants should have audio or should all not have audio.
Expand All @@ -98,8 +104,8 @@ shaka.media.AdaptationSet = class {
goog.asserts.assert(
!!a.audio == !!b.audio,
'Both should either have audio or not have audio.');
if (a.audio && b.audio &&
!AdaptationSet.areAudiosCompatible_(a.audio, b.audio, compareCodecs)) {
if (a.audio && b.audio && !AdaptationSet.areAudiosCompatible_(
a.audio, b.audio, compareCodecs, enableAudioGroups)) {
return false;
}

Expand Down Expand Up @@ -127,10 +133,11 @@ shaka.media.AdaptationSet = class {
* @param {shaka.extern.Stream} a
* @param {shaka.extern.Stream} b
* @param {boolean} compareCodecs
* @param {boolean} enableAudioGroups
* @return {boolean}
* @private
*/
static areAudiosCompatible_(a, b, compareCodecs) {
static areAudiosCompatible_(a, b, compareCodecs, enableAudioGroups) {
const AdaptationSet = shaka.media.AdaptationSet;

// Don't adapt between channel counts, which could annoy the user
Expand All @@ -154,7 +161,7 @@ shaka.media.AdaptationSet = class {
}

// We can only adapt between the same groupId.
if (a.groupId !== b.groupId) {
if (enableAudioGroups && a.groupId !== b.groupId) {
return false;
}

Expand Down
23 changes: 17 additions & 6 deletions lib/media/adaptation_set_criteria.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,18 @@ shaka.media.ExampleBasedCriteria = class {
/**
* @param {shaka.extern.Variant} example
* @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
* @param {boolean=} enableAudioGroups
*/
constructor(example,
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD) {
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
enableAudioGroups = false) {
/** @private {shaka.extern.Variant} */
this.example_ = example;
/** @private {shaka.config.CodecSwitchingStrategy} */
this.codecSwitchingStrategy_ = codecSwitchingStrategy;
/** @private {boolean} */
this.enableAudioGroups_ = enableAudioGroups;


// We can't know if role and label are really important, so we don't use
// role and label for this.
Expand All @@ -63,7 +68,7 @@ shaka.media.ExampleBasedCriteria = class {
/** @private {!shaka.media.AdaptationSetCriteria} */
this.fallback_ = new shaka.media.PreferenceBasedCriteria(
example.language, role, channelCount, hdrLevel, label,
codecSwitchingStrategy);
codecSwitchingStrategy, enableAudioGroups);
}

/** @override */
Expand All @@ -75,14 +80,15 @@ shaka.media.ExampleBasedCriteria = class {
// actually be from another period.
const shortList = variants.filter((variant) => {
return shaka.media.AdaptationSet.areAdaptable(this.example_, variant,
!supportsSmoothCodecTransitions);
!supportsSmoothCodecTransitions, this.enableAudioGroups_);
});

if (shortList.length) {
// Use the first item in the short list as the root. It should not matter
// which element we use as all items in the short list should already be
// compatible.
return new shaka.media.AdaptationSet(shortList[0], shortList);
return new shaka.media.AdaptationSet(shortList[0], shortList,
!supportsSmoothCodecTransitions, this.enableAudioGroups_);
} else {
return this.fallback_.create(variants);
}
Expand All @@ -102,9 +108,11 @@ shaka.media.PreferenceBasedCriteria = class {
* @param {string} hdrLevel
* @param {string=} label
* @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
* @param {boolean=} enableAudioGroups
*/
constructor(language, role, channelCount, hdrLevel, label = '',
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD) {
codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
enableAudioGroups = false) {
/** @private {string} */
this.language_ = language;
/** @private {string} */
Expand All @@ -117,6 +125,8 @@ shaka.media.PreferenceBasedCriteria = class {
this.label_ = label;
/** @private {shaka.config.CodecSwitchingStrategy} */
this.codecSwitchingStrategy_ = codecSwitchingStrategy;
/** @private {boolean} */
this.enableAudioGroups_ = enableAudioGroups;
}

/** @override */
Expand Down Expand Up @@ -185,7 +195,8 @@ shaka.media.PreferenceBasedCriteria = class {
const set = new shaka.media.AdaptationSet(current[0]);
for (const variant of current) {
// `add` checks combatability by calling `canInclude` internally.
set.add(variant, !supportsSmoothCodecTransitions);
set.add(variant, !supportsSmoothCodecTransitions,
this.enableAudioGroups_);
}

return set;
Expand Down
17 changes: 12 additions & 5 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -619,7 +619,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.config_.preferredVariantRole,
this.config_.preferredAudioChannelCount,
this.config_.preferredVideoHdrLevel,
this.config_.mediaSource.codecSwitchingStrategy);
this.config_.preferredAudioLabel,
this.config_.mediaSource.codecSwitchingStrategy,
this.config_.manifest.dash.enableAudioGroups);

/** @private {string} */
this.currentTextLanguage_ = this.config_.preferredTextLanguage;
Expand Down Expand Up @@ -2151,7 +2153,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.config_.preferredAudioChannelCount,
this.config_.preferredVideoHdrLevel,
this.config_.preferredAudioLabel,
this.config_.mediaSource.codecSwitchingStrategy);
this.config_.mediaSource.codecSwitchingStrategy,
this.config_.manifest.dash.enableAudioGroups);

this.currentTextLanguage_ = this.config_.preferredTextLanguage;
this.currentTextRole_ = this.config_.preferredTextRole;
Expand Down Expand Up @@ -4230,7 +4233,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
// When track is selected, back-propagate the language to
// currentAudioLanguage_.
this.currentAdaptationSetCriteria_ = new shaka.media.ExampleBasedCriteria(
variant);
variant,
this.config_.mediaSource.codecSwitchingStrategy,
this.config_.manifest.dash.enableAudioGroups);

// Update AbrManager variants to match these new settings.
this.updateAbrManagerVariants_();
Expand Down Expand Up @@ -4308,7 +4313,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.currentAdaptationSetCriteria_ =
new shaka.media.PreferenceBasedCriteria(language, role || '',
channelsCount, /* hdrLevel= */ '', /* label= */ '',
this.config_.mediaSource.codecSwitchingStrategy);
this.config_.mediaSource.codecSwitchingStrategy,
this.config_.manifest.dash.enableAudioGroups);

const diff = (a, b) => {
if (!a.video && !b.video) {
Expand Down Expand Up @@ -4426,7 +4432,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
this.currentAdaptationSetCriteria_ =
new shaka.media.PreferenceBasedCriteria(
firstVariantWithLabel.language, '', 0, '', label,
this.config_.mediaSource.codecSwitchingStrategy);
this.config_.mediaSource.codecSwitchingStrategy,
this.config_.manifest.dash.enableAudioGroups);

this.chooseVariantAndSwitch_(clearBuffer, safeMargin);
} else if (this.video_ && this.video_.audioTracks) {
Expand Down
17 changes: 13 additions & 4 deletions lib/util/stream_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,8 @@ shaka.util.StreamUtils = class {
const audioStreamsMap = new Map();
const getAudioId = (stream) => {
return stream.language + (stream.channelsCount || 0) +
stream.roles.join(',') + stream.label;
(stream.audioSamplingRate || 0) + stream.roles.join(',') +
stream.label + stream.groupId;
};
for (const stream of audioStreams) {
const id = getAudioId(stream);
Expand All @@ -124,10 +125,17 @@ shaka.util.StreamUtils = class {
const isChangeTypeSupported =
shaka.media.Capabilities.isChangeTypeSupported();

const validVideoStreams = [];
const validVideoIds = [];
const validVideoStreamsMap = new Map();
const getVideoGroupId = (stream) => {
return (stream.frameRate || 0) + (stream.hdr || '');
};
for (const stream of videoStreams) {
const groupId = getVideoGroupId(stream);
const validVideoStreams = validVideoStreamsMap.get(groupId) || [];
if (!validVideoStreams.length) {
validVideoStreams.push(stream);
validVideoIds.push(stream.id);
} else {
const previousStream = validVideoStreams[validVideoStreams.length - 1];
if (!isChangeTypeSupported) {
Expand All @@ -142,6 +150,7 @@ shaka.util.StreamUtils = class {
if (stream.width > previousStream.width ||
stream.height > previousStream.height) {
validVideoStreams.push(stream);
validVideoIds.push(stream.id);
} else if (stream.width == previousStream.width &&
stream.height == previousStream.height) {
const previousCodec =
Expand All @@ -151,16 +160,16 @@ shaka.util.StreamUtils = class {
if (previousCodec == currentCodec) {
if (stream.bandwidth < previousStream.bandwidth) {
validVideoStreams.push(stream);
validVideoIds.push(stream.id);
}
}
}
}
validVideoStreamsMap.set(groupId, validVideoStreams);
}

const validAudioIds =
validAudioStreams.map((audioStream) => audioStream.id);
const validVideoIds =
validVideoStreams.map((videoStream) => videoStream.id);

// Filter out any variants that don't match, forcing AbrManager to choose
// from a single video codec and a single audio codec possible.
Expand Down
5 changes: 3 additions & 2 deletions test/media/adaptation_set_criteria_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,7 @@ describe('AdaptationSetCriteria', () => {
]);
});

it('filters by audio group if present', () => {
it('filters by audio group if enabled', () => {
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
manifest.addVariant(1, (variant) => {
variant.language = 'en';
Expand All @@ -634,7 +634,8 @@ describe('AdaptationSetCriteria', () => {
});
});

const builder = new shaka.media.PreferenceBasedCriteria('en', '', 0, '');
const builder = new shaka.media.PreferenceBasedCriteria('en', '', 0, '',
'', shaka.config.CodecSwitchingStrategy.RELOAD, true);
const set = builder.create(manifest.variants);

checkSet(set, [
Expand Down

0 comments on commit 179c285

Please sign in to comment.