feat(HLS): Add support for QUERYPARAM variable type in #EXT-X-DEFINE (#5801)

Closes https://github.com/shaka-project/shaka-player/issues/5333
This commit is contained in:
Álvaro Velad Galván
2023-10-24 19:55:42 +02:00
committed by GitHub
parent 51870e9d7c
commit fda3189fd2
3 changed files with 59 additions and 21 deletions
+25 -4
View File
@@ -408,7 +408,8 @@ shaka.hls.HlsParser = class {
const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
'EXT-X-DEFINE');
const mediaVariables = this.parseMediaVariables_(variablesTags);
const mediaVariables = this.parseMediaVariables_(
variablesTags, response.uri);
const stream = streamInfo.stream;
@@ -949,31 +950,50 @@ shaka.hls.HlsParser = class {
* @private
*/
parseMasterVariables_(tags) {
const queryParams = new goog.Uri(this.masterPlaylistUri_).getQueryData();
for (const variableTag of tags) {
const name = variableTag.getAttributeValue('NAME');
const value = variableTag.getAttributeValue('VALUE');
const queryParam = variableTag.getAttributeValue('QUERYPARAM');
if (name && value) {
if (!this.globalVariables_.has(name)) {
this.globalVariables_.set(name, value);
}
}
if (queryParam) {
const queryParamValue = queryParams.get(queryParam)[0];
if (queryParamValue && !this.globalVariables_.has(queryParamValue)) {
this.globalVariables_.set(queryParam, queryParamValue);
}
}
}
}
/**
* Get the variables of each variant tag, and store in a map.
* @param {!Array.<!shaka.hls.Tag>} tags Variant tags from the playlist.
* @param {string} uri Media playlist URI.
* @return {!Map.<string, string>}
* @private
*/
parseMediaVariables_(tags) {
parseMediaVariables_(tags, uri) {
const queryParams = new goog.Uri(uri).getQueryData();
const mediaVariables = new Map();
for (const variableTag of tags) {
const name = variableTag.getAttributeValue('NAME');
const value = variableTag.getAttributeValue('VALUE');
const queryParam = variableTag.getAttributeValue('QUERYPARAM');
const mediaImport = variableTag.getAttributeValue('IMPORT');
if (name && value) {
mediaVariables.set(name, value);
if (!mediaVariables.has(name)) {
mediaVariables.set(name, value);
}
}
if (queryParam) {
const queryParamValue = queryParams.get(queryParam)[0];
if (queryParamValue && !mediaVariables.has(queryParamValue)) {
mediaVariables.set(queryParam, queryParamValue);
}
}
if (mediaImport) {
const globalValue = this.globalVariables_.get(mediaImport);
@@ -2151,7 +2171,8 @@ shaka.hls.HlsParser = class {
const variablesTags = shaka.hls.Utils.filterTagsByName(playlist.tags,
'EXT-X-DEFINE');
const mediaVariables = this.parseMediaVariables_(variablesTags);
const mediaVariables = this.parseMediaVariables_(
variablesTags, absoluteMediaPlaylistUri);
goog.asserts.assert(playlist.segments != null,
'Media playlist should have segments!');
+22 -17
View File
@@ -4292,11 +4292,14 @@ describe('HlsParser', () => {
it('parse variables master playlist', async () => {
const master = [
'#EXTM3U\n',
'#EXT-X-DEFINE:NAME="auth",VALUE="?token=1"\n',
'#EXT-X-DEFINE:NAME="auth",VALUE="token=1"\n',
'#EXT-X-DEFINE:QUERYPARAM="a"\n',
'#EXT-X-DEFINE:QUERYPARAM="b"\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,VIDEO="vid"\n',
'audio.m3u8{$auth}\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",URI="video.m3u8{$auth}"',
'audio.m3u8?{$auth}&a={$a}&b={$b}\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",',
'URI="video.m3u8?{$auth}&a={$a}&b={$b}"',
].join('');
const media = [
@@ -4308,14 +4311,14 @@ describe('HlsParser', () => {
].join('');
fakeNetEngine
.setResponseText('test:/host/master.m3u8', master)
.setResponseText('test:/host/audio.m3u8?token=1', media)
.setResponseText('test:/host/video.m3u8?token=1', media)
.setResponseText('test:/host/master.m3u8?a=1&b=2', master)
.setResponseText('test:/host/audio.m3u8?token=1&a=1&b=2', media)
.setResponseText('test:/host/video.m3u8?token=1&a=1&b=2', media)
.setResponseValue('test:/host/init.mp4', initSegmentData)
.setResponseValue('test:/host/segment.mp4', segmentData);
const actual =
await parser.start('test:/host/master.m3u8', playerInterface);
const actual = await parser.start(
'test:/host/master.m3u8?a=1&b=2', playerInterface);
await loadAllStreamsFor(actual);
const video = actual.variants[0].video;
const audio = actual.variants[0].audio;
@@ -4341,26 +4344,28 @@ describe('HlsParser', () => {
'#EXTM3U\n',
'#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
'RESOLUTION=960x540,FRAME-RATE=60,VIDEO="vid"\n',
'audio.m3u8\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",URI="video.m3u8"',
'audio.m3u8?fooParam=1\n',
'#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="vid",URI="video.m3u8?fooParam=1"',
].join('');
const media = [
'#EXTM3U\n',
'#EXT-X-DEFINE:NAME="auth",VALUE="?token=1"\n',
'#EXT-X-DEFINE:NAME="auth",VALUE="token=1"\n',
'#EXT-X-DEFINE:NAME="path",VALUE="test/"\n',
'#EXT-X-DEFINE:QUERYPARAM="fooParam"\n',
'#EXT-X-PLAYLIST-TYPE:VOD\n',
'#EXT-X-MAP:URI="{$path}init.mp4"\n',
'#EXTINF:5,\n',
'{$path}segment.mp4{$auth}',
'{$path}segment.mp4?{$auth}&fooParam={$fooParam}',
].join('');
fakeNetEngine
.setResponseText('test:/host/master.m3u8', master)
.setResponseText('test:/host/audio.m3u8', media)
.setResponseText('test:/host/video.m3u8', media)
.setResponseText('test:/host/audio.m3u8?fooParam=1', media)
.setResponseText('test:/host/video.m3u8?fooParam=1', media)
.setResponseValue('test:/host/test/init.mp4', initSegmentData)
.setResponseValue('test:/host/test/segment.mp4?token=1', segmentData);
.setResponseValue('test:/host/test/segment.mp4?token=1&fooParam=1',
segmentData);
const actual =
await parser.start('test:/host/master.m3u8', playerInterface);
@@ -4377,11 +4382,11 @@ describe('HlsParser', () => {
// flow has gone well.
const videoReference = Array.from(video.segmentIndex)[0];
expect(videoReference.getUris())
.toEqual(['test:/host/test/segment.mp4?token=1']);
.toEqual(['test:/host/test/segment.mp4?token=1&fooParam=1']);
const audioReference = Array.from(audio.segmentIndex)[0];
expect(audioReference.getUris())
.toEqual(['test:/host/test/segment.mp4?token=1']);
.toEqual(['test:/host/test/segment.mp4?token=1&fooParam=1']);
});
it('import variables in media from master playlist', async () => {
+12
View File
@@ -830,6 +830,18 @@ goog.Uri.QueryData.prototype.add = function(key, value) {
};
/**
* Get the values from a key.
*
* @param {string} key Name.
* @return {Array.<string>}
*/
goog.Uri.QueryData.prototype.get = function(key) {
this.ensureKeyMapInitialized_();
return this.keyMap_[key] || [];
};
/**
* @return {string} Encoded query string.
* @override