Skip to content

Commit

Permalink
feat(HLS): Add support for SUPPLEMENTAL-CODECS
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Jan 24, 2024
1 parent 4e47acd commit 49f2754
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 33 deletions.
39 changes: 11 additions & 28 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ goog.require('shaka.util.Error');
goog.require('shaka.util.FakeEvent');
goog.require('shaka.util.LanguageUtils');
goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.MimeUtils');
goog.require('shaka.util.OperationManager');
goog.require('shaka.util.Pssh');
goog.require('shaka.media.SegmentUtils');
Expand Down Expand Up @@ -1569,6 +1568,9 @@ shaka.hls.HlsParser = class {
getCodecsForVariantTag_(tag) {
let codecsString = tag.getAttributeValue('CODECS') || '';

const supplementalCodecsString =
tag.getAttributeValue('SUPPLEMENTAL-CODECS');

this.codecInfoInManifest_ = codecsString.length > 0;

if (!this.codecInfoInManifest_ && !this.config_.hls.disableCodecGuessing) {
Expand All @@ -1589,34 +1591,15 @@ shaka.hls.HlsParser = class {
/** @type {!Array.<string>} */
const codecs = codecsString.split(/\s*,\s*/);

return this.filterDuplicateCodecs_(codecs);
}


/**
* @param {!Array.<string>} codecs
* @return {!Array.<string>} codecs
* @private
*/
filterDuplicateCodecs_(codecs) {
// Filter out duplicate codecs.
const seen = new Set();
const ret = [];
for (const codec of codecs) {
// HLS says the CODECS field needs to include all codecs that appear in
// the content. This means that if the content changes profiles, it should
// include both. Since all known browsers support changing profiles
// without any other work, just ignore them. See also:
// https://github.com/shaka-project/shaka-player/issues/1817
const shortCodec = shaka.util.MimeUtils.getCodecBase(codec);
if (!seen.has(shortCodec)) {
ret.push(codec);
seen.add(shortCodec);
} else {
shaka.log.debug('Ignoring duplicate codec');
}
if (supplementalCodecsString) {
const supplementalCodecs = supplementalCodecsString.split(/\s*,\s*/)
.map((codec) => {
return codec.split('/')[0];
});
codecs.push(...supplementalCodecs);
}
return ret;

return shaka.media.SegmentUtils.codecsFiltering(codecs);
}

/**
Expand Down
34 changes: 29 additions & 5 deletions lib/media/segment_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ goog.require('shaka.log');
goog.require('shaka.media.Capabilities');
goog.require('shaka.media.ClosedCaptionParser');
goog.require('shaka.util.BufferUtils');
goog.require('shaka.util.ManifestParserUtils');
goog.require('shaka.util.MimeUtils');
goog.require('shaka.util.Mp4BoxParsers');
goog.require('shaka.util.Mp4Parser');
Expand Down Expand Up @@ -353,12 +354,11 @@ shaka.media.SegmentUtils = class {
}
captionParser.reset();
}
const codecs = audioCodecs.concat(
SegmentUtils.chooseBetterCodecs_(videoCodecs));
const codecs = audioCodecs.concat(videoCodecs);
return {
type: onlyAudio ? 'audio' : 'video',
mimeType: onlyAudio ? 'audio/mp4' : 'video/mp4',
codecs: SegmentUtils.filterDuplicateCodecs_(codecs).join(', '),
codecs: SegmentUtils.codecsFiltering(codecs).join(', '),
language: language,
height: height,
width: width,
Expand All @@ -368,6 +368,30 @@ shaka.media.SegmentUtils = class {
};
}

/**
* @param {!Array.<string>} codecs
* @return {!Array.<string>} codecs
*/
static codecsFiltering(codecs) {
const ContentType = shaka.util.ManifestParserUtils.ContentType;
const ManifestParserUtils = shaka.util.ManifestParserUtils;
const SegmentUtils = shaka.media.SegmentUtils;
const allCodecs = SegmentUtils.filterDuplicateCodecs_(codecs);
const audioCodecs =
ManifestParserUtils.guessAllCodecsSafe(ContentType.AUDIO, allCodecs);
const videoCodecs =
ManifestParserUtils.guessAllCodecsSafe(ContentType.VIDEO, allCodecs);
const textCodecs =
ManifestParserUtils.guessAllCodecsSafe(ContentType.TEXT, allCodecs);
const validVideoCodecs = SegmentUtils.chooseBetterCodecs_(videoCodecs);
const finalCodecs =
audioCodecs.concat(validVideoCodecs).concat(textCodecs);
if (allCodecs.length && !finalCodecs.length) {
return allCodecs;
}
return finalCodecs;
}

/**
* @param {!Array.<string>} codecs
* @return {!Array.<string>} codecs
Expand Down Expand Up @@ -405,13 +429,13 @@ shaka.media.SegmentUtils = class {
return codec.startsWith('dvh1.') || codec.startsWith('dvhe.');
});
if (!dolbyVision) {
return codecs.slice(0, 1);
return codecs;
}
const type = `video/mp4; codecs="${dolbyVision}"'`;
if (shaka.media.Capabilities.isTypeSupported(type)) {
return [dolbyVision];
}
return codecs.slice(0, 1);
return codecs.filter((codec) => codec != dolbyVision);
}
};

Expand Down
24 changes: 24 additions & 0 deletions lib/util/manifest_parser_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,30 @@ shaka.util.ManifestParserUtils = class {

return null;
}


/**
* Attempts to guess which codecs from the codecs list belong to a given
* content.
*
* @param {string} contentType
* @param {!Array.<string>} codecs
* @return {!Array.<string>}
*/
static guessAllCodecsSafe(contentType, codecs) {
const allCodecs = [];
const formats = shaka.util.ManifestParserUtils
.CODEC_REGEXPS_BY_CONTENT_TYPE_[contentType];
for (const format of formats) {
for (const codec of codecs) {
if (format.test(codec.trim())) {
allCodecs.push(codec.trim());
}
}
}

return allCodecs;
}
};


Expand Down

0 comments on commit 49f2754

Please sign in to comment.