Files
shaka-player/lib/hls/hls_classes.js
T
Joey Parrish 69d3193675 Clarify and clean up usage of URIs in HLS
This fixes issues with the interpretation of URIs in HLS and makes
their usage and meaning consistent and clear.

 - Name URI variables as either absolute, final (post-redirect), or
   verbatim (exactly as they appear in the playlist)
 - Identify media playlists by their verbatim URI when testing for
   equality or duplication
 - When a master playlist is redirected, interpret media playlists as
   relative to the redirected location
 - When a media playlist is redirected, request updates from the
   redirected location
 - When updating a media playlist, resolve media segment URIs as
   relative to the latest redirected media playlist URI
 - Resolve absolute segment URIs when parsing the playlist text,
   rather than waiting until we intepret and build the manifest
 - Remove some incidental bind() calls, which exposed compiler errors
 - Avoid refactoring long parameter lists
 - Avoid refactoring for async/await
 - Clean up redirection tests, which were brittle and did not verify
   what they seemed to
 - Use relative segment URIs for all segments in tests
 - Use media playlist URIs within master playlists in tests
 - Add a regression test specifically for #1664

Closes #1664

Change-Id: I45f946790c7d669637c231ae93920a09c18c4222
2018-11-19 12:37:50 -08:00

224 lines
4.9 KiB
JavaScript

/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
goog.provide('shaka.hls.Attribute');
goog.provide('shaka.hls.Playlist');
goog.provide('shaka.hls.PlaylistType');
goog.provide('shaka.hls.Segment');
goog.provide('shaka.hls.Tag');
goog.require('goog.asserts');
/**
* Creates an HLS playlist object.
*
* @param {string} absoluteUri An absolute, final URI after redirects.
* @param {!shaka.hls.PlaylistType} type
* @param {!Array.<shaka.hls.Tag>} tags
* @param {!Array.<shaka.hls.Segment>=} segments
*
* @constructor
* @struct
*/
shaka.hls.Playlist = function(absoluteUri, type, tags, segments) {
/**
* An absolute, final URI after redirects.
*
* @const {string}
*/
this.absoluteUri = absoluteUri;
/** @const {shaka.hls.PlaylistType} */
this.type = type;
/** @const {!Array.<!shaka.hls.Tag>} */
this.tags = tags;
/** @const {Array.<!shaka.hls.Segment>} */
this.segments = segments || null;
};
/**
* @enum {number}
*/
shaka.hls.PlaylistType = {
MASTER: 0,
MEDIA: 1,
};
/**
* Creates an HLS tag object.
*
* @param {number} id
* @param {string} name
* @param {!Array.<shaka.hls.Attribute>} attributes
* @param {?string=} value
*
* @constructor
* @struct
*/
shaka.hls.Tag = function(id, name, attributes, value = null) {
goog.asserts.assert(
(attributes.length == 0 && value) ||
(attributes.length > 0 && !value) ||
(attributes.length == 0 && !value),
'Tags can only take the form ' +
'(1) <NAME>:<VALUE> ' +
'(2) <NAME>:<ATTRIBUTE_LIST> ' +
' (3) <NAME>');
/** @const {number} */
this.id = id;
/** @const {string} */
this.name = name;
/** @const {Array.<shaka.hls.Attribute>} */
this.attributes = attributes;
/** @const {?string} */
this.value = value;
};
/**
* Create the string representation of the tag.
*
* For the DRM system - the full tag needs to be passed down to the CDM. There
* are two ways of doing this (1) save the original tag or (2) recreate the tag.
* As in some cases (like in tests) the tag never existed in string form, it
* is far easier to recreate the tag from the parsed form.
*
* @return {string}
* @override
*/
shaka.hls.Tag.prototype.toString = function() {
/**
* @param {shaka.hls.Attribute} attr
* @return {string}
*/
let attrToStr = function(attr) {
return attr.name + '="' + attr.value + '"';
};
// A valid tag can only follow 1 of 3 patterns.
// 1) <NAME>:<VALUE>
// 2) <NAME>:<ATTRIBUTE LIST>
// 3) <NAME>
if (this.value) {
return '#' + this.name + ':' + this.value;
}
if (this.attributes.length > 0) {
return '#' + this.name + ':' + this.attributes.map(attrToStr).join(',');
}
return '#' + this.name;
};
/**
* Creates an HLS attribute object.
*
* @param {string} name
* @param {string} value
*
* @constructor
* @struct
*/
shaka.hls.Attribute = function(name, value) {
/** @const {string} */
this.name = name;
/** @const {string} */
this.value = value;
};
/**
* Adds an attribute to an HLS Tag.
*
* @param {!shaka.hls.Attribute} attribute
*/
shaka.hls.Tag.prototype.addAttribute = function(attribute) {
this.attributes.push(attribute);
};
/**
* Gets the first attribute of the tag with a specified name.
*
* @param {string} name
* @return {?shaka.hls.Attribute} attribute
*/
shaka.hls.Tag.prototype.getAttribute = function(name) {
let attributes = this.attributes.filter(function(attr) {
return attr.name == name;
});
goog.asserts.assert(attributes.length < 2,
'A tag should not have multiple attributes ' +
'with the same name!');
if (attributes.length) {
return attributes[0];
} else {
return null;
}
};
/**
* Gets the value of the first attribute of the tag with a specified name.
* If not found, returns an optional default value.
*
* @param {string} name
* @param {string=} defaultValue
* @return {?string}
*/
shaka.hls.Tag.prototype.getAttributeValue = function(name, defaultValue) {
let attribute = this.getAttribute(name);
return attribute ? attribute.value : (defaultValue || null);
};
/**
* Creates an HLS segment object.
*
* @param {string} absoluteUri An absolute URI.
* @param {!Array.<shaka.hls.Tag>} tags
*
* @constructor
* @struct
*/
shaka.hls.Segment = function(absoluteUri, tags) {
/** @const {!Array.<shaka.hls.Tag>} */
this.tags = tags;
/**
* An absolute URI.
*
* @const {string}
*/
this.absoluteUri = absoluteUri;
};