Source: lib/hls/hls_classes.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.hls.Attribute');
  7. goog.provide('shaka.hls.Playlist');
  8. goog.provide('shaka.hls.PlaylistType');
  9. goog.provide('shaka.hls.Segment');
  10. goog.provide('shaka.hls.Tag');
  11. goog.require('goog.asserts');
  12. goog.require('shaka.util.Error');
  13. /**
  14. * HLS playlist class.
  15. */
  16. shaka.hls.Playlist = class {
  17. /**
  18. * @param {string} absoluteUri An absolute, final URI after redirects.
  19. * @param {!shaka.hls.PlaylistType} type
  20. * @param {!Array.<shaka.hls.Tag>} tags
  21. * @param {!Array.<shaka.hls.Segment>=} segments
  22. */
  23. constructor(absoluteUri, type, tags, segments) {
  24. /**
  25. * An absolute, final URI after redirects.
  26. *
  27. * @const {string}
  28. */
  29. this.absoluteUri = absoluteUri;
  30. /** @const {shaka.hls.PlaylistType} */
  31. this.type = type;
  32. /** @const {!Array.<!shaka.hls.Tag>} */
  33. this.tags = tags;
  34. /** @const {Array.<!shaka.hls.Segment>} */
  35. this.segments = segments || null;
  36. }
  37. };
  38. /**
  39. * @enum {number}
  40. */
  41. shaka.hls.PlaylistType = {
  42. MASTER: 0,
  43. MEDIA: 1,
  44. };
  45. /**
  46. * HLS tag class.
  47. */
  48. shaka.hls.Tag = class {
  49. /**
  50. * @param {number} id
  51. * @param {string} name
  52. * @param {!Array.<shaka.hls.Attribute>} attributes
  53. * @param {?string=} value
  54. */
  55. constructor(id, name, attributes, value = null) {
  56. /** @const {number} */
  57. this.id = id;
  58. /** @type {string} */
  59. this.name = name;
  60. /** @const {!Array.<shaka.hls.Attribute>} */
  61. this.attributes = attributes;
  62. /** @const {?string} */
  63. this.value = value;
  64. }
  65. /**
  66. * Create the string representation of the tag.
  67. *
  68. * For the DRM system - the full tag needs to be passed down to the CDM.
  69. * There are two ways of doing this (1) save the original tag or (2) recreate
  70. * the tag.
  71. * As in some cases (like in tests) the tag never existed in string form, it
  72. * is far easier to recreate the tag from the parsed form.
  73. *
  74. * @return {string}
  75. * @override
  76. */
  77. toString() {
  78. /**
  79. * @param {shaka.hls.Attribute} attr
  80. * @return {string}
  81. */
  82. const attrToStr = (attr) => {
  83. const isNumericAttr = !isNaN(Number(attr.value));
  84. const value = (isNumericAttr ? attr.value : '"' + attr.value + '"');
  85. return attr.name + '=' + value;
  86. };
  87. // A valid tag can only follow 1 of 4 patterns.
  88. // 1) <NAME>:<VALUE>
  89. // 2) <NAME>:<ATTRIBUTE LIST>
  90. // 3) <NAME>
  91. // 4) <NAME>:<VALUE>,<ATTRIBUTE_LIST>
  92. let tagStr = '#' + this.name;
  93. const appendages = this.attributes ? this.attributes.map(attrToStr) : [];
  94. if (this.value) {
  95. appendages.unshift(this.value);
  96. }
  97. if (appendages.length > 0) {
  98. tagStr += ':' + appendages.join(',');
  99. }
  100. return tagStr;
  101. }
  102. /**
  103. * Adds an attribute to an HLS Tag.
  104. *
  105. * @param {!shaka.hls.Attribute} attribute
  106. */
  107. addAttribute(attribute) {
  108. this.attributes.push(attribute);
  109. }
  110. /**
  111. * Gets the first attribute of the tag with a specified name.
  112. *
  113. * @param {string} name
  114. * @return {?shaka.hls.Attribute} attribute
  115. */
  116. getAttribute(name) {
  117. const attributes = this.attributes.filter((attr) => {
  118. return attr.name == name;
  119. });
  120. goog.asserts.assert(attributes.length < 2,
  121. 'A tag should not have multiple attributes ' +
  122. 'with the same name!');
  123. if (attributes.length) {
  124. return attributes[0];
  125. } else {
  126. return null;
  127. }
  128. }
  129. /**
  130. * Gets the value of the first attribute of the tag with a specified name.
  131. * If not found, returns an optional default value.
  132. *
  133. * @param {string} name
  134. * @param {string=} defaultValue
  135. * @return {?string}
  136. */
  137. getAttributeValue(name, defaultValue) {
  138. const attribute = this.getAttribute(name);
  139. return attribute ? attribute.value : (defaultValue || null);
  140. }
  141. /**
  142. * Finds the attribute and returns its value.
  143. * Throws an error if attribute was not found.
  144. *
  145. * @param {string} name
  146. * @return {string}
  147. */
  148. getRequiredAttrValue(name) {
  149. const attribute = this.getAttribute(name);
  150. if (!attribute) {
  151. throw new shaka.util.Error(
  152. shaka.util.Error.Severity.CRITICAL,
  153. shaka.util.Error.Category.MANIFEST,
  154. shaka.util.Error.Code.HLS_REQUIRED_ATTRIBUTE_MISSING,
  155. name);
  156. }
  157. return attribute.value;
  158. }
  159. /**
  160. * Set the name of the tag. Used only for Preload hinted MAP tag.
  161. * @param {string} name
  162. */
  163. setName(name) {
  164. this.name = name;
  165. }
  166. };
  167. /**
  168. * HLS segment class.
  169. */
  170. shaka.hls.Segment = class {
  171. /**
  172. * Creates an HLS segment object.
  173. *
  174. * @param {string} absoluteUri An absolute URI.
  175. * @param {!Array.<shaka.hls.Tag>} tags
  176. * @param {!Array.<shaka.hls.Tag>=} partialSegments
  177. */
  178. constructor(absoluteUri, tags, partialSegments=[]) {
  179. /** @const {!Array.<shaka.hls.Tag>} */
  180. this.tags = tags;
  181. /**
  182. * An absolute URI.
  183. *
  184. * @const {string}
  185. */
  186. this.absoluteUri = absoluteUri;
  187. /** @type {!Array.<shaka.hls.Tag>} */
  188. this.partialSegments = partialSegments;
  189. }
  190. };
  191. /**
  192. * HLS Attribute class.
  193. */
  194. shaka.hls.Attribute = class {
  195. /**
  196. * Creates an HLS attribute object.
  197. *
  198. * @param {string} name
  199. * @param {string} value
  200. */
  201. constructor(name, value) {
  202. /** @const {string} */
  203. this.name = name;
  204. /** @const {string} */
  205. this.value = value;
  206. }
  207. };