Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(HLS): Parse SAMPLE-RATE attribute #5375

Merged
merged 1 commit into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
50 changes: 35 additions & 15 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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