Source: lib/util/manifest_parser_utils.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.util.ManifestParserUtils');
  7. goog.require('goog.Uri');
  8. goog.require('shaka.util.Error');
  9. goog.require('shaka.util.Functional');
  10. /**
  11. * @summary Utility functions for manifest parsing.
  12. */
  13. shaka.util.ManifestParserUtils = class {
  14. /**
  15. * Resolves an array of relative URIs to the given base URIs. This will result
  16. * in M*N number of URIs.
  17. *
  18. * @param {!Array.<string>} baseUris
  19. * @param {!Array.<string>} relativeUris
  20. * @return {!Array.<string>}
  21. */
  22. static resolveUris(baseUris, relativeUris) {
  23. const Functional = shaka.util.Functional;
  24. if (relativeUris.length == 0) {
  25. return baseUris;
  26. }
  27. const relativeAsGoog = relativeUris.map((uri) => new goog.Uri(uri));
  28. // Resolve each URI relative to each base URI, creating an Array of Arrays.
  29. // Then flatten the Arrays into a single Array.
  30. return baseUris.map((uri) => new goog.Uri(uri))
  31. .map((base) => relativeAsGoog.map((i) => base.resolve(i)))
  32. .reduce(Functional.collapseArrays, [])
  33. .map((uri) => uri.toString());
  34. }
  35. /**
  36. * Creates a DrmInfo object from the given info.
  37. *
  38. * @param {string} keySystem
  39. * @param {Array.<shaka.extern.InitDataOverride>} initData
  40. * @return {shaka.extern.DrmInfo}
  41. */
  42. static createDrmInfo(keySystem, initData) {
  43. return {
  44. keySystem: keySystem,
  45. licenseServerUri: '',
  46. distinctiveIdentifierRequired: false,
  47. persistentStateRequired: false,
  48. audioRobustness: '',
  49. videoRobustness: '',
  50. serverCertificate: null,
  51. serverCertificateUri: '',
  52. sessionType: '',
  53. initData: initData || [],
  54. keyIds: new Set(),
  55. };
  56. }
  57. /**
  58. * Attempts to guess which codecs from the codecs list belong to a given
  59. * content type.
  60. * Assumes that at least one codec is correct, and throws if none are.
  61. *
  62. * @param {string} contentType
  63. * @param {!Array.<string>} codecs
  64. * @return {string}
  65. */
  66. static guessCodecs(contentType, codecs) {
  67. if (codecs.length == 1) {
  68. return codecs[0];
  69. }
  70. const match = shaka.util.ManifestParserUtils.guessCodecsSafe(
  71. contentType, codecs);
  72. // A failure is specifically denoted by null; an empty string represents a
  73. // valid match of no codec.
  74. if (match != null) {
  75. return match;
  76. }
  77. // Unable to guess codecs.
  78. throw new shaka.util.Error(
  79. shaka.util.Error.Severity.CRITICAL,
  80. shaka.util.Error.Category.MANIFEST,
  81. shaka.util.Error.Code.HLS_COULD_NOT_GUESS_CODECS,
  82. codecs);
  83. }
  84. /**
  85. * Attempts to guess which codecs from the codecs list belong to a given
  86. * content type. Does not assume a single codec is anything special, and does
  87. * not throw if it fails to match.
  88. *
  89. * @param {string} contentType
  90. * @param {!Array.<string>} codecs
  91. * @return {?string} or null if no match is found
  92. */
  93. static guessCodecsSafe(contentType, codecs) {
  94. const formats = shaka.util.ManifestParserUtils
  95. .CODEC_REGEXPS_BY_CONTENT_TYPE_[contentType];
  96. for (const format of formats) {
  97. for (const codec of codecs) {
  98. if (format.test(codec.trim())) {
  99. return codec.trim();
  100. }
  101. }
  102. }
  103. // Text does not require a codec string.
  104. if (contentType == shaka.util.ManifestParserUtils.ContentType.TEXT) {
  105. return '';
  106. }
  107. return null;
  108. }
  109. };
  110. /**
  111. * @enum {string}
  112. */
  113. shaka.util.ManifestParserUtils.ContentType = {
  114. VIDEO: 'video',
  115. AUDIO: 'audio',
  116. TEXT: 'text',
  117. IMAGE: 'image',
  118. APPLICATION: 'application',
  119. };
  120. /**
  121. * @enum {string}
  122. */
  123. shaka.util.ManifestParserUtils.TextStreamKind = {
  124. SUBTITLE: 'subtitle',
  125. CLOSED_CAPTION: 'caption',
  126. };
  127. /**
  128. * Specifies how tolerant the player is of inaccurate segment start times and
  129. * end times within a manifest. For example, gaps or overlaps between segments
  130. * in a SegmentTimeline which are greater than or equal to this value will
  131. * result in a warning message.
  132. *
  133. * @const {number}
  134. */
  135. shaka.util.ManifestParserUtils.GAP_OVERLAP_TOLERANCE_SECONDS = 1 / 15;
  136. /**
  137. * A list of regexps to detect well-known video codecs.
  138. *
  139. * @const {!Array.<!RegExp>}
  140. * @private
  141. */
  142. shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_ = [
  143. /^avc/,
  144. /^hev/,
  145. /^hvc/,
  146. /^vp0?[89]/,
  147. /^av01/,
  148. ];
  149. /**
  150. * A list of regexps to detect well-known audio codecs.
  151. *
  152. * @const {!Array.<!RegExp>}
  153. * @private
  154. */
  155. shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
  156. /^vorbis$/,
  157. /^opus$/,
  158. /^flac$/,
  159. /^mp4a/,
  160. /^[ae]c-3$/,
  161. ];
  162. /**
  163. * A list of regexps to detect well-known text codecs.
  164. *
  165. * @const {!Array.<!RegExp>}
  166. * @private
  167. */
  168. shaka.util.ManifestParserUtils.TEXT_CODEC_REGEXPS_ = [
  169. /^vtt$/,
  170. /^wvtt/,
  171. /^stpp/,
  172. ];
  173. /**
  174. * @const {!Object.<string, !Array.<!RegExp>>}
  175. */
  176. shaka.util.ManifestParserUtils.CODEC_REGEXPS_BY_CONTENT_TYPE_ = {
  177. 'audio': shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_,
  178. 'video': shaka.util.ManifestParserUtils.VIDEO_CODEC_REGEXPS_,
  179. 'text': shaka.util.ManifestParserUtils.TEXT_CODEC_REGEXPS_,
  180. };