Source: lib/dash/content_protection.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.dash.ContentProtection');
  7. goog.require('goog.asserts');
  8. goog.require('shaka.log');
  9. goog.require('shaka.drm.DrmUtils');
  10. goog.require('shaka.drm.PlayReady');
  11. goog.require('shaka.util.Error');
  12. goog.require('shaka.util.ManifestParserUtils');
  13. goog.require('shaka.util.Pssh');
  14. goog.require('shaka.util.StringUtils');
  15. goog.require('shaka.util.TXml');
  16. goog.require('shaka.util.Uint8ArrayUtils');
  17. /**
  18. * @summary A set of functions for parsing and interpreting ContentProtection
  19. * elements.
  20. */
  21. shaka.dash.ContentProtection = class {
  22. /**
  23. * Parses info from the ContentProtection elements at the AdaptationSet level.
  24. *
  25. * @param {!Array<!shaka.extern.xml.Node>} elements
  26. * @param {boolean} ignoreDrmInfo
  27. * @param {!Object<string, string>} keySystemsByURI
  28. * @return {shaka.dash.ContentProtection.Context}
  29. */
  30. static parseFromAdaptationSet(elements, ignoreDrmInfo, keySystemsByURI) {
  31. const ContentProtection = shaka.dash.ContentProtection;
  32. const ManifestParserUtils = shaka.util.ManifestParserUtils;
  33. const parsed = ContentProtection.parseElements_(elements);
  34. /** @type {Array<shaka.extern.InitDataOverride>} */
  35. let defaultInit = null;
  36. /** @type {!Array<shaka.extern.DrmInfo>} */
  37. let drmInfos = [];
  38. let parsedNonCenc = [];
  39. /** @type {?shaka.dash.ContentProtection.Aes128Info} */
  40. let aes128Info = null;
  41. // Get the default key ID; if there are multiple, they must all match.
  42. const keyIds = new Set(parsed.map((element) => element.keyId));
  43. // Remove any possible null value (elements may have no key ids).
  44. keyIds.delete(null);
  45. let encryptionScheme = 'cenc';
  46. if (keyIds.size > 1) {
  47. throw new shaka.util.Error(
  48. shaka.util.Error.Severity.CRITICAL,
  49. shaka.util.Error.Category.MANIFEST,
  50. shaka.util.Error.Code.DASH_CONFLICTING_KEY_IDS);
  51. }
  52. if (!ignoreDrmInfo) {
  53. const aes128Elements = parsed.filter((elem) => {
  54. return elem.schemeUri == ContentProtection.Aes128Protection_;
  55. });
  56. if (aes128Elements.length > 1) {
  57. throw new shaka.util.Error(
  58. shaka.util.Error.Severity.CRITICAL,
  59. shaka.util.Error.Category.MANIFEST,
  60. shaka.util.Error.Code.DASH_CONFLICTING_AES_128);
  61. }
  62. if (aes128Elements.length) {
  63. aes128Info = ContentProtection.parseAes128_(aes128Elements[0]);
  64. }
  65. const mp4ProtectionParsed = parsed.find((elem) => {
  66. return elem.schemeUri == ContentProtection.MP4Protection_;
  67. });
  68. if (mp4ProtectionParsed && mp4ProtectionParsed.encryptionScheme) {
  69. encryptionScheme = mp4ProtectionParsed.encryptionScheme;
  70. }
  71. // Find the default key ID and init data. Create a new array of all the
  72. // non-CENC elements.
  73. parsedNonCenc = parsed.filter((elem) => {
  74. if (elem.schemeUri == ContentProtection.MP4Protection_) {
  75. goog.asserts.assert(!elem.init || elem.init.length,
  76. 'Init data must be null or non-empty.');
  77. defaultInit = elem.init || defaultInit;
  78. return false;
  79. } else {
  80. return elem.schemeUri != ContentProtection.Aes128Protection_;
  81. }
  82. });
  83. if (parsedNonCenc.length) {
  84. drmInfos = ContentProtection.convertElements_(defaultInit,
  85. encryptionScheme, parsedNonCenc, keySystemsByURI, keyIds);
  86. // If there are no drmInfos after parsing, then add a dummy entry.
  87. // This may be removed in parseKeyIds.
  88. if (drmInfos.length == 0) {
  89. drmInfos = [ManifestParserUtils.createDrmInfo(
  90. '', encryptionScheme, defaultInit)];
  91. }
  92. }
  93. }
  94. // If there are only CENC element(s) or ignoreDrmInfo flag is set, assume
  95. // all key-systems are supported.
  96. if (parsed.length && !aes128Info &&
  97. (ignoreDrmInfo || !parsedNonCenc.length)) {
  98. drmInfos = [];
  99. for (const keySystem of Object.values(keySystemsByURI)) {
  100. // If the manifest doesn't specify any key systems, we shouldn't
  101. // put clearkey in this list. Otherwise, it may be triggered when
  102. // a real key system should be used instead.
  103. if (keySystem != 'org.w3.clearkey') {
  104. const info = ManifestParserUtils.createDrmInfo(
  105. keySystem, encryptionScheme, defaultInit);
  106. drmInfos.push(info);
  107. }
  108. }
  109. }
  110. // If we have a default key id, apply it to every initData.
  111. const defaultKeyId = Array.from(keyIds)[0] || null;
  112. if (defaultKeyId) {
  113. for (const info of drmInfos) {
  114. for (const initData of info.initData) {
  115. initData.keyId = defaultKeyId;
  116. }
  117. }
  118. }
  119. return {
  120. defaultKeyId: defaultKeyId,
  121. defaultInit: defaultInit,
  122. drmInfos: drmInfos,
  123. aes128Info: aes128Info,
  124. firstRepresentation: true,
  125. };
  126. }
  127. /**
  128. * Parses the given ContentProtection elements found at the Representation
  129. * level. This may update the |context|.
  130. *
  131. * @param {!Array<!shaka.extern.xml.Node>} elements
  132. * @param {shaka.dash.ContentProtection.Context} context
  133. * @param {boolean} ignoreDrmInfo
  134. * @param {!Object<string, string>} keySystemsByURI
  135. * @return {?string} The parsed key ID
  136. */
  137. static parseFromRepresentation(
  138. elements, context, ignoreDrmInfo, keySystemsByURI) {
  139. const ContentProtection = shaka.dash.ContentProtection;
  140. const repContext = ContentProtection.parseFromAdaptationSet(
  141. elements, ignoreDrmInfo, keySystemsByURI);
  142. if (context.firstRepresentation) {
  143. const asUnknown = context.drmInfos.length == 1 &&
  144. !context.drmInfos[0].keySystem;
  145. const asUnencrypted = context.drmInfos.length == 0;
  146. const repUnencrypted = repContext.drmInfos.length == 0;
  147. // There are two cases where we need to replace the |drmInfos| in the
  148. // context with those in the Representation:
  149. // 1. The AdaptationSet does not list any ContentProtection.
  150. // 2. The AdaptationSet only lists unknown key-systems.
  151. if (asUnencrypted || (asUnknown && !repUnencrypted)) {
  152. context.drmInfos = repContext.drmInfos;
  153. }
  154. context.firstRepresentation = false;
  155. } else if (repContext.drmInfos.length > 0) {
  156. // If this is not the first Representation, then we need to remove entries
  157. // from the context that do not appear in this Representation.
  158. context.drmInfos = context.drmInfos.filter((asInfo) => {
  159. return repContext.drmInfos.some((repInfo) => {
  160. return repInfo.keySystem == asInfo.keySystem;
  161. });
  162. });
  163. // If we have filtered out all key-systems, throw an error.
  164. if (context.drmInfos.length == 0) {
  165. throw new shaka.util.Error(
  166. shaka.util.Error.Severity.CRITICAL,
  167. shaka.util.Error.Category.MANIFEST,
  168. shaka.util.Error.Code.DASH_NO_COMMON_KEY_SYSTEM);
  169. }
  170. }
  171. return repContext.defaultKeyId || context.defaultKeyId;
  172. }
  173. /**
  174. * Gets a FairPlay license URL from a content protection element
  175. * containing a 'dashif:Laurl' element
  176. *
  177. * @param {shaka.dash.ContentProtection.Element} element
  178. * @return {string}
  179. */
  180. static getFairPlayLicenseUrl(element) {
  181. if (shaka.drm.DrmUtils.isMediaKeysPolyfilled('apple')) {
  182. throw new shaka.util.Error(
  183. shaka.util.Error.Severity.CRITICAL,
  184. shaka.util.Error.Category.MANIFEST,
  185. shaka.util.Error.Code
  186. .DASH_MSE_ENCRYPTED_LEGACY_APPLE_MEDIA_KEYS_NOT_SUPPORTED);
  187. }
  188. const dashIfLaurlNode = shaka.util.TXml.findChildNS(
  189. element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
  190. 'Laurl',
  191. );
  192. if (dashIfLaurlNode) {
  193. const textContents = shaka.util.TXml.getTextContents(dashIfLaurlNode);
  194. if (textContents) {
  195. return textContents;
  196. }
  197. }
  198. return '';
  199. }
  200. /**
  201. * Gets a Widevine license URL from a content protection element
  202. * containing a custom `ms:laurl` or 'dashif:Laurl' elements
  203. *
  204. * @param {shaka.dash.ContentProtection.Element} element
  205. * @return {string}
  206. */
  207. static getWidevineLicenseUrl(element) {
  208. const StringUtils = shaka.util.StringUtils;
  209. const dashIfLaurlNode = shaka.util.TXml.findChildNS(
  210. element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
  211. 'Laurl',
  212. );
  213. if (dashIfLaurlNode) {
  214. const textContents = shaka.util.TXml.getTextContents(dashIfLaurlNode);
  215. if (textContents) {
  216. return textContents;
  217. }
  218. }
  219. const msLaUrlNode = shaka.util.TXml.findChildNS(
  220. element.node, 'urn:microsoft', 'laurl');
  221. if (msLaUrlNode) {
  222. return StringUtils.htmlUnescape(
  223. msLaUrlNode.attributes['licenseUrl']) || '';
  224. }
  225. return '';
  226. }
  227. /**
  228. * Gets a ClearKey license URL from a content protection element
  229. * containing a custom `clearkey::Laurl` or 'dashif:Laurl' elements
  230. *
  231. * @param {shaka.dash.ContentProtection.Element} element
  232. * @return {string}
  233. */
  234. static getClearKeyLicenseUrl(element) {
  235. const dashIfLaurlNode = shaka.util.TXml.findChildNS(
  236. element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
  237. 'Laurl',
  238. );
  239. if (dashIfLaurlNode) {
  240. const textContents = shaka.util.TXml.getTextContents(dashIfLaurlNode);
  241. if (textContents) {
  242. return textContents;
  243. }
  244. }
  245. const clearKeyLaurlNode = shaka.util.TXml.findChildNS(
  246. element.node, shaka.dash.ContentProtection.ClearKeyNamespaceUri_,
  247. 'Laurl',
  248. );
  249. if (clearKeyLaurlNode &&
  250. clearKeyLaurlNode.attributes['Lic_type'] === 'EME-1.0') {
  251. if (clearKeyLaurlNode) {
  252. const textContents = shaka.util.TXml.getTextContents(clearKeyLaurlNode);
  253. if (textContents) {
  254. return textContents;
  255. }
  256. }
  257. }
  258. return '';
  259. }
  260. /**
  261. * Gets a PlayReady license URL from a content protection element
  262. * containing a PlayReady Header Object
  263. *
  264. * @param {shaka.dash.ContentProtection.Element} element
  265. * @return {string}
  266. */
  267. static getPlayReadyLicenseUrl(element) {
  268. const TXml = shaka.util.TXml;
  269. const dashIfLaurlNode = TXml.findChildNS(
  270. element.node, shaka.dash.ContentProtection.DashIfNamespaceUri_,
  271. 'Laurl',
  272. );
  273. if (dashIfLaurlNode) {
  274. const textContents = TXml.getTextContents(dashIfLaurlNode);
  275. if (textContents) {
  276. return textContents;
  277. }
  278. }
  279. const proNode = TXml.findChildNS(
  280. element.node, 'urn:microsoft:playready', 'pro');
  281. if (proNode) {
  282. const textContents = TXml.getTextContents(proNode);
  283. if (textContents) {
  284. return shaka.drm.PlayReady.getLicenseUrl(proNode);
  285. }
  286. }
  287. const psshNode = TXml.findChildNS(
  288. element.node, shaka.dash.ContentProtection.CencNamespaceUri_, 'pssh');
  289. if (psshNode) {
  290. const textContents = TXml.getTextContents(psshNode);
  291. if (textContents) {
  292. const proData = shaka.util.Pssh.getPsshData(
  293. shaka.util.Uint8ArrayUtils.fromBase64(textContents));
  294. const proString = shaka.util.Uint8ArrayUtils.toStandardBase64(proData);
  295. const reBuildProNode =
  296. TXml.parseXmlString('<pro>' + proString + '</pro>');
  297. goog.asserts.assert(reBuildProNode, 'Must have pro node');
  298. return shaka.drm.PlayReady.getLicenseUrl(reBuildProNode);
  299. }
  300. }
  301. return '';
  302. }
  303. /**
  304. * Gets a PlayReady initData from a content protection element
  305. * containing a PlayReady Pro Object
  306. *
  307. * @param {shaka.dash.ContentProtection.Element} element
  308. * @return {?Array<shaka.extern.InitDataOverride>}
  309. * @private
  310. */
  311. static getInitDataFromPro_(element) {
  312. const proNode = shaka.util.TXml.findChildNS(
  313. element.node, 'urn:microsoft:playready', 'pro');
  314. if (!proNode || !shaka.util.TXml.getTextContents(proNode)) {
  315. return null;
  316. }
  317. const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
  318. const textContent =
  319. /** @type {string} */ (shaka.util.TXml.getTextContents(proNode));
  320. const data = Uint8ArrayUtils.fromBase64(textContent);
  321. const systemId = new Uint8Array([
  322. 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86,
  323. 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95,
  324. ]);
  325. const keyIds = new Set();
  326. const psshVersion = 0;
  327. const pssh =
  328. shaka.util.Pssh.createPssh(data, systemId, keyIds, psshVersion);
  329. return [
  330. {
  331. initData: pssh,
  332. initDataType: 'cenc',
  333. keyId: element.keyId,
  334. },
  335. ];
  336. }
  337. /**
  338. * Creates ClearKey initData from Default_KID value retrieved from previously
  339. * parsed ContentProtection tag.
  340. * @param {shaka.dash.ContentProtection.Element} element
  341. * @param {!Set<string>} keyIds
  342. * @return {?Array<shaka.extern.InitDataOverride>}
  343. * @private
  344. */
  345. static getInitDataClearKey_(element, keyIds) {
  346. if (keyIds.size == 0) {
  347. return null;
  348. }
  349. const systemId = new Uint8Array([
  350. 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
  351. 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
  352. ]);
  353. const data = new Uint8Array([]);
  354. const psshVersion = 1;
  355. const pssh =
  356. shaka.util.Pssh.createPssh(data, systemId, keyIds, psshVersion);
  357. return [
  358. {
  359. initData: pssh,
  360. initDataType: 'cenc',
  361. keyId: element.keyId,
  362. },
  363. ];
  364. }
  365. /**
  366. * Creates DrmInfo objects from the given element.
  367. *
  368. * @param {Array<shaka.extern.InitDataOverride>} defaultInit
  369. * @param {string} encryptionScheme
  370. * @param {!Array<shaka.dash.ContentProtection.Element>} elements
  371. * @param {!Object<string, string>} keySystemsByURI
  372. * @param {!Set<string>} keyIds
  373. * @return {!Array<shaka.extern.DrmInfo>}
  374. * @private
  375. */
  376. static convertElements_(defaultInit, encryptionScheme, elements,
  377. keySystemsByURI, keyIds) {
  378. const ContentProtection = shaka.dash.ContentProtection;
  379. const ManifestParserUtils = shaka.util.ManifestParserUtils;
  380. const licenseUrlParsers = ContentProtection.licenseUrlParsers_;
  381. /** @type {!Array<shaka.extern.DrmInfo>} */
  382. const out = [];
  383. for (const element of elements) {
  384. const keySystem = keySystemsByURI[element.schemeUri];
  385. if (keySystem) {
  386. goog.asserts.assert(
  387. !element.init || element.init.length,
  388. 'Init data must be null or non-empty.');
  389. const proInitData = ContentProtection.getInitDataFromPro_(element);
  390. let clearKeyInitData = null;
  391. if (element.schemeUri ===
  392. shaka.dash.ContentProtection.ClearKeySchemeUri_) {
  393. clearKeyInitData =
  394. ContentProtection.getInitDataClearKey_(element, keyIds);
  395. }
  396. const initData = element.init || defaultInit || proInitData ||
  397. clearKeyInitData;
  398. const info = ManifestParserUtils.createDrmInfo(
  399. keySystem, encryptionScheme, initData);
  400. const licenseParser = licenseUrlParsers.get(keySystem);
  401. if (licenseParser) {
  402. info.licenseServerUri = licenseParser(element);
  403. }
  404. out.push(info);
  405. }
  406. }
  407. return out;
  408. }
  409. /**
  410. * Parses the given ContentProtection elements. If there is an error, it
  411. * removes those elements.
  412. *
  413. * @param {!Array<!shaka.extern.xml.Node>} elements
  414. * @return {!Array<shaka.dash.ContentProtection.Element>}
  415. * @private
  416. */
  417. static parseElements_(elements) {
  418. /** @type {!Array<shaka.dash.ContentProtection.Element>} */
  419. const out = [];
  420. for (const element of elements) {
  421. const parsed = shaka.dash.ContentProtection.parseElement_(element);
  422. if (parsed) {
  423. out.push(parsed);
  424. }
  425. }
  426. return out;
  427. }
  428. /**
  429. * Parses the given ContentProtection element.
  430. *
  431. * @param {!shaka.extern.xml.Node} elem
  432. * @return {?shaka.dash.ContentProtection.Element}
  433. * @private
  434. */
  435. static parseElement_(elem) {
  436. const NS = shaka.dash.ContentProtection.CencNamespaceUri_;
  437. const TXml = shaka.util.TXml;
  438. /** @type {?string} */
  439. let schemeUri = elem.attributes['schemeIdUri'];
  440. /** @type {?string} */
  441. let keyId = TXml.getAttributeNS(elem, NS, 'default_KID');
  442. /** @type {!Array<string>} */
  443. const psshs = TXml.findChildrenNS(elem, NS, 'pssh')
  444. .map(TXml.getContents);
  445. const encryptionScheme = elem.attributes['value'];
  446. if (!schemeUri) {
  447. shaka.log.error('Missing required schemeIdUri attribute on',
  448. 'ContentProtection element', elem);
  449. return null;
  450. }
  451. schemeUri = schemeUri.toLowerCase();
  452. if (keyId) {
  453. keyId = keyId.replace(/-/g, '').toLowerCase();
  454. if (keyId.includes(' ')) {
  455. throw new shaka.util.Error(
  456. shaka.util.Error.Severity.CRITICAL,
  457. shaka.util.Error.Category.MANIFEST,
  458. shaka.util.Error.Code.DASH_MULTIPLE_KEY_IDS_NOT_SUPPORTED);
  459. }
  460. }
  461. /** @type {!Array<shaka.extern.InitDataOverride>} */
  462. let init = [];
  463. try {
  464. // Try parsing PSSH data.
  465. init = psshs.map((pssh) => {
  466. return {
  467. initDataType: 'cenc',
  468. initData: shaka.util.Uint8ArrayUtils.fromBase64(pssh),
  469. keyId: null,
  470. };
  471. });
  472. } catch (e) {
  473. throw new shaka.util.Error(
  474. shaka.util.Error.Severity.CRITICAL,
  475. shaka.util.Error.Category.MANIFEST,
  476. shaka.util.Error.Code.DASH_PSSH_BAD_ENCODING);
  477. }
  478. return {
  479. node: elem,
  480. schemeUri,
  481. keyId,
  482. init: (init.length > 0 ? init : null),
  483. encryptionScheme,
  484. };
  485. }
  486. /**
  487. * Parses the given AES-128 ContentProtection element.
  488. *
  489. * @param {shaka.dash.ContentProtection.Element} element
  490. * @return {?shaka.dash.ContentProtection.Aes128Info}
  491. * @private
  492. */
  493. static parseAes128_(element) {
  494. // Check if the Web Crypto API is available.
  495. if (!window.crypto || !window.crypto.subtle) {
  496. shaka.log.alwaysWarn('Web Crypto API is not available to decrypt ' +
  497. 'AES-128. (Web Crypto only exists in secure origins like https)');
  498. throw new shaka.util.Error(
  499. shaka.util.Error.Severity.CRITICAL,
  500. shaka.util.Error.Category.MANIFEST,
  501. shaka.util.Error.Code.NO_WEB_CRYPTO_API);
  502. }
  503. const namespace = 'urn:mpeg:dash:schema:sea:2012';
  504. const segmentEncryption = shaka.util.TXml.findChildNS(
  505. element.node, namespace, 'SegmentEncryption');
  506. if (!segmentEncryption) {
  507. throw new shaka.util.Error(
  508. shaka.util.Error.Severity.CRITICAL,
  509. shaka.util.Error.Category.MANIFEST,
  510. shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
  511. }
  512. const aesSchemeIdUri = 'urn:mpeg:dash:sea:aes128-cbc:2013';
  513. const segmentEncryptionSchemeIdUri =
  514. segmentEncryption.attributes['schemeIdUri'];
  515. if (segmentEncryptionSchemeIdUri != aesSchemeIdUri) {
  516. throw new shaka.util.Error(
  517. shaka.util.Error.Severity.CRITICAL,
  518. shaka.util.Error.Category.MANIFEST,
  519. shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
  520. }
  521. const cryptoPeriod = shaka.util.TXml.findChildNS(
  522. element.node, namespace, 'CryptoPeriod');
  523. if (!cryptoPeriod) {
  524. throw new shaka.util.Error(
  525. shaka.util.Error.Severity.CRITICAL,
  526. shaka.util.Error.Category.MANIFEST,
  527. shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
  528. }
  529. const ivHex = cryptoPeriod.attributes['IV'];
  530. const keyUri = shaka.util.StringUtils.htmlUnescape(
  531. cryptoPeriod.attributes['keyUriTemplate']);
  532. if (!ivHex || !keyUri) {
  533. throw new shaka.util.Error(
  534. shaka.util.Error.Severity.CRITICAL,
  535. shaka.util.Error.Category.MANIFEST,
  536. shaka.util.Error.Code.DASH_UNSUPPORTED_AES_128);
  537. }
  538. // Exclude 0x at the start of string.
  539. const iv = shaka.util.Uint8ArrayUtils.fromHex(ivHex.substr(2));
  540. if (iv.byteLength != 16) {
  541. throw new shaka.util.Error(
  542. shaka.util.Error.Severity.CRITICAL,
  543. shaka.util.Error.Category.MANIFEST,
  544. shaka.util.Error.Code.AES_128_INVALID_IV_LENGTH);
  545. }
  546. return {
  547. keyUri,
  548. iv,
  549. };
  550. }
  551. };
  552. /**
  553. * @typedef {{
  554. * defaultKeyId: ?string,
  555. * defaultInit: Array<shaka.extern.InitDataOverride>,
  556. * drmInfos: !Array<shaka.extern.DrmInfo>,
  557. * aes128Info: ?shaka.dash.ContentProtection.Aes128Info,
  558. * firstRepresentation: boolean
  559. * }}
  560. *
  561. * @description
  562. * Contains information about the ContentProtection elements found at the
  563. * AdaptationSet level.
  564. *
  565. * @property {?string} defaultKeyId
  566. * The default key ID to use. This is used by parseKeyIds as a default. This
  567. * can be null to indicate that there is no default.
  568. * @property {Array<shaka.extern.InitDataOverride>} defaultInit
  569. * The default init data override. This can be null to indicate that there
  570. * is no default.
  571. * @property {!Array<shaka.extern.DrmInfo>} drmInfos
  572. * The DrmInfo objects.
  573. * @property {?shaka.dash.ContentProtection.Aes128Info} aes128Info
  574. * The AES-128 key info.
  575. * @property {boolean} firstRepresentation
  576. * True when first parsed; changed to false after the first call to
  577. * parseKeyIds. This is used to determine if a dummy key-system should be
  578. * overwritten; namely that the first representation can replace the dummy
  579. * from the AdaptationSet.
  580. */
  581. shaka.dash.ContentProtection.Context;
  582. /**
  583. * @typedef {{
  584. * keyUri: string,
  585. * iv: !Uint8Array
  586. * }}
  587. *
  588. * @description
  589. * Contains information about the AES-128 keyUri and IV found at the
  590. * AdaptationSet level.
  591. *
  592. * @property {string} method
  593. * The keyUri in the manifest.
  594. * @property {!Uint8Array} iv
  595. * The IV in the manifest.
  596. */
  597. shaka.dash.ContentProtection.Aes128Info;
  598. /**
  599. * @typedef {{
  600. * node: !shaka.extern.xml.Node,
  601. * schemeUri: string,
  602. * keyId: ?string,
  603. * init: Array<shaka.extern.InitDataOverride>,
  604. * encryptionScheme: ?string
  605. * }}
  606. *
  607. * @description
  608. * The parsed result of a single ContentProtection element.
  609. *
  610. * @property {!shaka.extern.xml.Node} node
  611. * The ContentProtection XML element.
  612. * @property {string} schemeUri
  613. * The scheme URI.
  614. * @property {?string} keyId
  615. * The default key ID, if present.
  616. * @property {Array<shaka.extern.InitDataOverride>} init
  617. * The init data, if present. If there is no init data, it will be null. If
  618. * this is non-null, there is at least one element.
  619. * @property {?string} encryptionScheme
  620. * The encryption scheme, if present.
  621. */
  622. shaka.dash.ContentProtection.Element;
  623. /**
  624. * A map of key system name to license server url parser.
  625. *
  626. * @const {!Map<string, function(shaka.dash.ContentProtection.Element)>}
  627. * @private
  628. */
  629. shaka.dash.ContentProtection.licenseUrlParsers_ = new Map()
  630. .set('com.apple.fps',
  631. shaka.dash.ContentProtection.getFairPlayLicenseUrl)
  632. .set('com.widevine.alpha',
  633. shaka.dash.ContentProtection.getWidevineLicenseUrl)
  634. .set('com.microsoft.playready',
  635. shaka.dash.ContentProtection.getPlayReadyLicenseUrl)
  636. .set('com.microsoft.playready.recommendation',
  637. shaka.dash.ContentProtection.getPlayReadyLicenseUrl)
  638. .set('com.microsoft.playready.software',
  639. shaka.dash.ContentProtection.getPlayReadyLicenseUrl)
  640. .set('com.microsoft.playready.hardware',
  641. shaka.dash.ContentProtection.getPlayReadyLicenseUrl)
  642. .set('org.w3.clearkey',
  643. shaka.dash.ContentProtection.getClearKeyLicenseUrl);
  644. /**
  645. * @const {string}
  646. * @private
  647. */
  648. shaka.dash.ContentProtection.MP4Protection_ =
  649. 'urn:mpeg:dash:mp4protection:2011';
  650. /**
  651. * @const {string}
  652. * @private
  653. */
  654. shaka.dash.ContentProtection.Aes128Protection_ =
  655. 'urn:mpeg:dash:sea:2012';
  656. /**
  657. * @const {string}
  658. * @private
  659. */
  660. shaka.dash.ContentProtection.CencNamespaceUri_ = 'urn:mpeg:cenc:2013';
  661. /**
  662. * @const {string}
  663. * @private
  664. */
  665. shaka.dash.ContentProtection.ClearKeyNamespaceUri_ =
  666. 'http://dashif.org/guidelines/clearKey';
  667. /**
  668. * @const {string}
  669. * @private
  670. */
  671. shaka.dash.ContentProtection.ClearKeySchemeUri_ =
  672. 'urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e';
  673. /**
  674. * @const {string}
  675. * @private
  676. */
  677. shaka.dash.ContentProtection.DashIfNamespaceUri_ =
  678. 'https://dashif.org/CPS';