Home Reference Source

src/loader/key-loader.ts

  1. /*
  2. * Decrypt key Loader
  3. */
  4.  
  5. import Event from '../events';
  6. import EventHandler from '../event-handler';
  7. import { ErrorTypes, ErrorDetails } from '../errors';
  8. import { logger } from '../utils/logger';
  9. import Hls from '../hls';
  10. import Fragment from './fragment';
  11. import { LoaderStats, LoaderResponse, LoaderContext, LoaderConfiguration, LoaderCallbacks } from '../types/loader';
  12.  
  13. interface OnKeyLoadingPayload {
  14. frag: Fragment
  15. }
  16.  
  17. interface KeyLoaderContext extends LoaderContext {
  18. frag: Fragment
  19. }
  20.  
  21. class KeyLoader extends EventHandler {
  22. public loaders = {};
  23. public decryptkey: Uint8Array | null = null;
  24. public decrypturl: string | null = null;
  25.  
  26. constructor (hls: Hls) {
  27. super(hls, Event.KEY_LOADING);
  28. }
  29.  
  30. destroy (): void {
  31. for (const loaderName in this.loaders) {
  32. let loader = this.loaders[loaderName];
  33. if (loader) {
  34. loader.destroy();
  35. }
  36. }
  37. this.loaders = {};
  38.  
  39. super.destroy();
  40. }
  41.  
  42. onKeyLoading (data: OnKeyLoadingPayload) {
  43. const { frag } = data;
  44. const type = frag.type;
  45. const loader = this.loaders[type];
  46. if (!frag.decryptdata) {
  47. logger.warn('Missing decryption data on fragment in onKeyLoading');
  48. return;
  49. }
  50.  
  51. // Load the key if the uri is different from previous one, or if the decrypt key has not yet been retrieved
  52. const uri = frag.decryptdata.uri;
  53. if (uri !== this.decrypturl || this.decryptkey === null) {
  54. let config = this.hls.config;
  55. if (loader) {
  56. logger.warn(`abort previous key loader for type:${type}`);
  57. loader.abort();
  58. }
  59. if (!uri) {
  60. logger.warn('key uri is falsy');
  61. return;
  62. }
  63.  
  64. frag.loader = this.loaders[type] = new config.loader(config);
  65. this.decrypturl = uri;
  66. this.decryptkey = null;
  67.  
  68. const loaderContext: KeyLoaderContext = {
  69. url: uri,
  70. frag: frag,
  71. responseType: 'arraybuffer'
  72. };
  73.  
  74. // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times,
  75. // key-loader will trigger an error and rely on stream-controller to handle retry logic.
  76. // this will also align retry logic with fragment-loader
  77. const loaderConfig: LoaderConfiguration = {
  78. timeout: config.fragLoadingTimeOut,
  79. maxRetry: 0,
  80. retryDelay: config.fragLoadingRetryDelay,
  81. maxRetryDelay: config.fragLoadingMaxRetryTimeout
  82. };
  83.  
  84. const loaderCallbacks: LoaderCallbacks<KeyLoaderContext> = {
  85. onSuccess: this.loadsuccess.bind(this),
  86. onError: this.loaderror.bind(this),
  87. onTimeout: this.loadtimeout.bind(this)
  88. };
  89.  
  90. frag.loader.load(loaderContext, loaderConfig, loaderCallbacks);
  91. } else if (this.decryptkey) {
  92. // Return the key if it's already been loaded
  93. frag.decryptdata.key = this.decryptkey;
  94. this.hls.trigger(Event.KEY_LOADED, { frag: frag });
  95. }
  96. }
  97.  
  98. loadsuccess (response: LoaderResponse, stats: LoaderStats, context: KeyLoaderContext) {
  99. let frag = context.frag;
  100. if (!frag.decryptdata) {
  101. logger.error('after key load, decryptdata unset');
  102. return;
  103. }
  104. this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data as ArrayBuffer);
  105.  
  106. // detach fragment loader on load success
  107. frag.loader = undefined;
  108. delete this.loaders[frag.type];
  109. this.hls.trigger(Event.KEY_LOADED, { frag: frag });
  110. }
  111.  
  112. loaderror (response: LoaderResponse, context: KeyLoaderContext) {
  113. let frag = context.frag;
  114. let loader = frag.loader;
  115. if (loader) {
  116. loader.abort();
  117. }
  118.  
  119. delete this.loaders[frag.type];
  120. this.hls.trigger(Event.ERROR, { type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag, response });
  121. }
  122.  
  123. loadtimeout (stats: LoaderStats, context: KeyLoaderContext) {
  124. let frag = context.frag;
  125. let loader = frag.loader;
  126. if (loader) {
  127. loader.abort();
  128. }
  129.  
  130. delete this.loaders[frag.type];
  131. this.hls.trigger(Event.ERROR, { type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.KEY_LOAD_TIMEOUT, fatal: false, frag });
  132. }
  133. }
  134.  
  135. export default KeyLoader;