Skip to content

Commit

Permalink
feat(HLS): Parse SAMPLE-RATE attribute (shaka-project#5375)
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Jun 29, 2023
1 parent 3f9eade commit 5af34ad
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 16 deletions.
50 changes: 35 additions & 15 deletions lib/hls/hls_parser.js
Expand Up @@ -686,15 +686,13 @@ shaka.hls.HlsParser = class {
// Make the stream info, with those values.
const streamInfo = await this.convertParsedPlaylistIntoStreamInfo_(
playlist, uri, uri, codecs, type, language, primary, name,
channelsCount, closedCaptions, characteristics, forced, spatialAudio,
mimeType);
channelsCount, closedCaptions, characteristics, forced, sampleRate,
spatialAudio, mimeType);
this.uriToStreamInfosMap_.set(uri, streamInfo);

if (type == 'video') {
this.addVideoAttributes_(streamInfo.stream, width, height,
/* frameRate= */ null, /* videoRange= */ null);
} else if (type == 'audio') {
streamInfo.stream.audioSamplingRate = sampleRate;
}

// Wrap the stream from that stream info with a variant.
Expand Down Expand Up @@ -1547,6 +1545,21 @@ shaka.hls.HlsParser = class {
return count;
}

/**
* Get the sample rate information for an HLS audio track.
*
* @param {!shaka.hls.Tag} tag
* @return {?number}
* @private
*/
getSampleRate_(tag) {
const sampleRate = tag.getAttributeValue('SAMPLE-RATE');
if (!sampleRate) {
return null;
}
return parseInt(sampleRate, 10);
}

/**
* Get the spatial audio information for an HLS audio track.
* In HLS the channels field indicates the number of audio channels that the
Expand Down Expand Up @@ -1801,12 +1814,13 @@ shaka.hls.HlsParser = class {

const forcedAttrValue = tag.getAttributeValue('FORCED');
const forced = forcedAttrValue == 'YES';
const sampleRate = type == 'audio' ? this.getSampleRate_(tag) : null;
// TODO: Should we take into account some of the currently ignored
// attributes: INSTREAM-ID, Attribute descriptions: https://bit.ly/2lpjOhj
const streamInfo = this.createStreamInfo_(
verbatimMediaPlaylistUri, codecs, type, language, primary, name,
channelsCount, /* closedCaptions= */ null, characteristics, forced,
spatialAudio);
sampleRate, spatialAudio);
if (this.groupIdToStreamInfosMap_.has(groupId)) {
this.groupIdToStreamInfosMap_.get(groupId).push(streamInfo);
} else {
Expand Down Expand Up @@ -1853,7 +1867,8 @@ shaka.hls.HlsParser = class {
const streamInfo = this.createStreamInfo_(
verbatimImagePlaylistUri, codecs, type, language, /* primary= */ false,
name, /* channelsCount= */ null, /* closedCaptions= */ null,
characteristics, /* forced= */ false, /* spatialAudio= */ false);
characteristics, /* forced= */ false, /* sampleRate= */ null,
/* spatialAudio= */ false);

// TODO: This check is necessary because of the possibility of multiple
// calls to createStreamInfoFromImageTag_ before either has resolved.
Expand Down Expand Up @@ -1917,7 +1932,7 @@ shaka.hls.HlsParser = class {
codecs, type, /* language= */ 'und', /* primary= */ false,
/* name= */ null, /* channelcount= */ null, closedCaptions,
/* characteristics= */ null, /* forced= */ false,
/* spatialAudio= */ false);
/* sampleRate= */ null, /* spatialAudio= */ false);
// TODO: This check is necessary because of the possibility of multiple
// calls to createStreamInfoFromVariantTag_ before either has resolved.
if (this.uriToStreamInfosMap_.has(verbatimMediaPlaylistUri)) {
Expand All @@ -1940,13 +1955,14 @@ shaka.hls.HlsParser = class {
* @param {Map.<string, string>} closedCaptions
* @param {?string} characteristics
* @param {boolean} forced
* @param {?number} sampleRate
* @param {boolean} spatialAudio
* @return {!shaka.hls.HlsParser.StreamInfo}
* @private
*/
createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, language,
primary, name, channelsCount, closedCaptions, characteristics, forced,
spatialAudio) {
sampleRate, spatialAudio) {
// TODO: Refactor, too many parameters
const initialMediaPlaylistUri = shaka.hls.Utils.constructAbsoluteUri(
this.masterPlaylistUri_, verbatimMediaPlaylistUri);
Expand All @@ -1955,7 +1971,8 @@ shaka.hls.HlsParser = class {
// So we start out with a stream object that does not contain the actual
// segment index, then download when createSegmentIndex is called.
const stream = this.makeStreamObject_(codecs, type, language, primary, name,
channelsCount, closedCaptions, characteristics, forced, spatialAudio);
channelsCount, closedCaptions, characteristics, forced, sampleRate,
spatialAudio);
const streamInfo = {
stream,
type,
Expand Down Expand Up @@ -1996,7 +2013,7 @@ shaka.hls.HlsParser = class {
const realStreamInfo = await this.convertParsedPlaylistIntoStreamInfo_(
playlist, verbatimMediaPlaylistUri, absoluteMediaPlaylistUri, codecs,
type, language, primary, name, channelsCount, closedCaptions,
characteristics, forced, spatialAudio);
characteristics, forced, sampleRate, spatialAudio);
if (abortSignal.aborted) {
return;
}
Expand Down Expand Up @@ -2239,15 +2256,16 @@ shaka.hls.HlsParser = class {
* @param {Map.<string, string>} closedCaptions
* @param {?string} characteristics
* @param {boolean} forced
* @param {?number} sampleRate
* @param {boolean} spatialAudio
* @param {(string|undefined)} mimeType
* @return {!Promise.<!shaka.hls.HlsParser.StreamInfo>}
* @private
*/
async convertParsedPlaylistIntoStreamInfo_(playlist, verbatimMediaPlaylistUri,
absoluteMediaPlaylistUri, codecs, type, language, primary, name,
channelsCount, closedCaptions, characteristics, forced, spatialAudio,
mimeType = undefined) {
channelsCount, closedCaptions, characteristics, forced, sampleRate,
spatialAudio, mimeType = undefined) {
if (playlist.type != shaka.hls.PlaylistType.MEDIA) {
// EXT-X-MEDIA and EXT-X-IMAGE-STREAM-INF tags should point to media
// playlists.
Expand Down Expand Up @@ -2317,7 +2335,8 @@ shaka.hls.HlsParser = class {
this.getNextMediaSequenceAndPart_(mediaSequenceNumber, segments);

const stream = this.makeStreamObject_(codecs, type, language, primary, name,
channelsCount, closedCaptions, characteristics, forced, spatialAudio);
channelsCount, closedCaptions, characteristics, forced, sampleRate,
spatialAudio);
stream.segmentIndex = segmentIndex;
stream.encrypted = encrypted;
stream.drmInfos = drmInfos;
Expand Down Expand Up @@ -2401,12 +2420,13 @@ shaka.hls.HlsParser = class {
* @param {Map.<string, string>} closedCaptions
* @param {?string} characteristics
* @param {boolean} forced
* @param {?number} sampleRate
* @param {boolean} spatialAudio
* @return {!shaka.extern.Stream}
* @private
*/
makeStreamObject_(codecs, type, language, primary, name, channelsCount,
closedCaptions, characteristics, forced, spatialAudio) {
closedCaptions, characteristics, forced, sampleRate, spatialAudio) {
// Fill out a "best-guess" mimeType, for now. It will be replaced once the
// stream is lazy-loaded.
const mimeType = this.guessMimeTypeBeforeLoading_(type, codecs) ||
Expand Down Expand Up @@ -2439,7 +2459,7 @@ shaka.hls.HlsParser = class {
roles: characteristics ? characteristics.split(',') : [],
forced,
channelsCount,
audioSamplingRate: null,
audioSamplingRate: sampleRate,
spatialAudio,
closedCaptions,
hdr: undefined,
Expand Down
3 changes: 2 additions & 1 deletion test/hls/hls_parser_unit.js
Expand Up @@ -150,7 +150,7 @@ describe('HlsParser', () => {
const master = [
'#EXTM3U\n',
'#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
'CHANNELS="16/JOC",URI="audio"\n',
'CHANNELS="16/JOC",SAMPLE-RATE="48000",URI="audio"\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="eng",',
'URI="text"\n',
'#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub2",LANGUAGE="es",',
Expand Down Expand Up @@ -192,6 +192,7 @@ describe('HlsParser', () => {
variant.addPartialStream(ContentType.AUDIO, (stream) => {
stream.language = 'en';
stream.channelsCount = 16;
stream.audioSamplingRate = 48000;
stream.spatialAudio = true;
stream.mime('audio/mp4', 'mp4a');
});
Expand Down

0 comments on commit 5af34ad

Please sign in to comment.