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