From 0761bd753e74a0ecb3e8ef1ef7bcaf8d482a503a Mon Sep 17 00:00:00 2001 From: Alvaro Velad Date: Tue, 28 Apr 2020 09:22:26 +0200 Subject: [PATCH] Parse HLS CHARACTERISTICS attribute --- lib/hls/hls_parser.js | 16 ++++++++++---- test/hls/hls_parser_unit.js | 43 +++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/lib/hls/hls_parser.js b/lib/hls/hls_parser.js index 50c85f3fed..7eb9bed52c 100644 --- a/lib/hls/hls_parser.js +++ b/lib/hls/hls_parser.js @@ -1046,12 +1046,13 @@ shaka.hls.HlsParser = class { const autoselectAttr = tag.getAttribute('AUTOSELECT'); const primary = !!defaultAttr || !!autoselectAttr; const channelsCount = type == 'audio' ? this.getChannelsCount_(tag) : null; + const characteristics = tag.getAttributeValue('CHARACTERISTICS'); // TODO: Should we take into account some of the currently ignored // attributes: FORCED, INSTREAM-ID, CHARACTERISTICS? Attribute // descriptions: https://bit.ly/2lpjOhj const streamInfo = await this.createStreamInfo_( verbatimMediaPlaylistUri, codecs, type, language, primary, name, - channelsCount, /* closedCaptions= */ null); + channelsCount, /* closedCaptions= */ null, characteristics); if (this.groupIdToStreamInfosMap_.has(groupId)) { this.groupIdToStreamInfosMap_.get(groupId).push(streamInfo); } else { @@ -1093,7 +1094,8 @@ shaka.hls.HlsParser = class { const codecs = this.guessCodecs_(type, allCodecs); const streamInfo = await this.createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, /* language= */ 'und', /* primary= */ false, - /* name= */ null, /* channelcount= */ null, closedCaptions); + /* name= */ null, /* channelcount= */ null, closedCaptions, + /* characteristics= */ null); if (streamInfo == null) { return null; } @@ -1117,11 +1119,12 @@ shaka.hls.HlsParser = class { * @param {?string} name * @param {?number} channelsCount * @param {Map.} closedCaptions + * @param {?string} characteristics * @return {!Promise.} * @private */ async createStreamInfo_(verbatimMediaPlaylistUri, codecs, type, language, - primary, name, channelsCount, closedCaptions) { + primary, name, channelsCount, closedCaptions, characteristics) { // TODO: Refactor, too many parameters let absoluteMediaPlaylistUri = shaka.hls.Utils.constructAbsoluteUri( this.masterPlaylistUri_, verbatimMediaPlaylistUri); @@ -1244,6 +1247,11 @@ shaka.hls.HlsParser = class { const kind = (type == shaka.util.ManifestParserUtils.ContentType.TEXT) ? shaka.util.ManifestParserUtils.TextStreamKind.SUBTITLE : undefined; + const roles = []; + if (characteristics) { + roles.push(characteristics); + } + /** @type {shaka.extern.Stream} */ const stream = { id: this.globalId_++, @@ -1268,7 +1276,7 @@ shaka.hls.HlsParser = class { width: undefined, height: undefined, bandwidth: undefined, - roles: [], + roles: roles, channelsCount, audioSamplingRate: null, closedCaptions, diff --git a/test/hls/hls_parser_unit.js b/test/hls/hls_parser_unit.js index 768fa8ec37..58bcaae302 100644 --- a/test/hls/hls_parser_unit.js +++ b/test/hls/hls_parser_unit.js @@ -770,6 +770,49 @@ describe('HlsParser', () => { await testHlsParser(master, media, manifest); }); + it('parses characteristics from audio tags', async () => { + const master = [ + '#EXTM3U\n', + '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",', + 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n', + 'video\n', + '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",', + 'URI="audio"\n', + '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",', + 'CHARACTERISTICS="public.accessibility.describes-video",URI="audio2"\n', + ].join(''); + + const media = [ + '#EXTM3U\n', + '#EXT-X-PLAYLIST-TYPE:VOD\n', + '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n', + '#EXTINF:5,\n', + '#EXT-X-BYTERANGE:121090@616\n', + 'main.mp4', + ].join(''); + + const manifest = shaka.test.ManifestGenerator.generate((manifest) => { + manifest.anyTimeline(); + manifest.addPartialVariant((variant) => { + variant.language = 'en'; + variant.addPartialStream(ContentType.VIDEO); + variant.addPartialStream(ContentType.AUDIO, (stream) => { + stream.language = 'en'; + }); + }); + manifest.addPartialVariant((variant) => { + variant.language = 'en'; + variant.addPartialStream(ContentType.VIDEO); + variant.addPartialStream(ContentType.AUDIO, (stream) => { + stream.language = 'en'; + stream.roles = ['public.accessibility.describes-video']; + }); + }); + }); + + await testHlsParser(master, media, manifest); + }); + it('should call filter during parsing', async () => { const master = [ '#EXTM3U\n',