mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-14 15:56:38 +03:00
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:
committed by
GitHub
parent
51870e9d7c
commit
fda3189fd2
+25
-4
@@ -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
@@ -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 () => {
|
||||
|
||||
Vendored
+12
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user