Home Reference Source

src/hls.ts

  1. import * as URLToolkit from 'url-toolkit';
  2. import PlaylistLoader from './loader/playlist-loader';
  3. import KeyLoader from './loader/key-loader';
  4. import ID3TrackController from './controller/id3-track-controller';
  5. import LatencyController from './controller/latency-controller';
  6. import LevelController from './controller/level-controller';
  7. import { FragmentTracker } from './controller/fragment-tracker';
  8. import StreamController from './controller/stream-controller';
  9. import { isSupported } from './is-supported';
  10. import { logger, enableLogs } from './utils/logger';
  11. import { enableStreamingMode, hlsDefaultConfig, mergeConfig } from './config';
  12. import { EventEmitter } from 'eventemitter3';
  13. import { Events } from './events';
  14. import { ErrorTypes, ErrorDetails } from './errors';
  15. import type { HlsEventEmitter, HlsListeners } from './events';
  16. import type AudioTrackController from './controller/audio-track-controller';
  17. import type AbrController from './controller/abr-controller';
  18. import type BufferController from './controller/buffer-controller';
  19. import type CapLevelController from './controller/cap-level-controller';
  20. import type CMCDController from './controller/cmcd-controller';
  21. import type EMEController from './controller/eme-controller';
  22. import type SubtitleTrackController from './controller/subtitle-track-controller';
  23. import type { ComponentAPI, NetworkComponentAPI } from './types/component-api';
  24. import type { MediaPlaylist } from './types/media-playlist';
  25. import type { HlsConfig } from './config';
  26. import type { Level } from './types/level';
  27. import type { Fragment } from './loader/fragment';
  28.  
  29. /**
  30. * @module Hls
  31. * @class
  32. * @constructor
  33. */
  34. export default class Hls implements HlsEventEmitter {
  35. private static defaultConfig?: HlsConfig;
  36.  
  37. public readonly config: HlsConfig;
  38. public readonly userConfig: Partial<HlsConfig>;
  39.  
  40. private coreComponents: ComponentAPI[];
  41. private networkControllers: NetworkComponentAPI[];
  42.  
  43. private _emitter: HlsEventEmitter = new EventEmitter();
  44. private _autoLevelCapping: number;
  45. private abrController: AbrController;
  46. private bufferController: BufferController;
  47. private capLevelController: CapLevelController;
  48. private latencyController: LatencyController;
  49. private levelController: LevelController;
  50. private streamController: StreamController;
  51. private audioTrackController: AudioTrackController;
  52. private subtitleTrackController: SubtitleTrackController;
  53. private emeController: EMEController;
  54. private cmcdController: CMCDController;
  55.  
  56. private _media: HTMLMediaElement | null = null;
  57. private url: string | null = null;
  58.  
  59. static get version(): string {
  60. return __VERSION__;
  61. }
  62.  
  63. static isSupported(): boolean {
  64. return isSupported();
  65. }
  66.  
  67. static get Events() {
  68. return Events;
  69. }
  70.  
  71. static get ErrorTypes() {
  72. return ErrorTypes;
  73. }
  74.  
  75. static get ErrorDetails() {
  76. return ErrorDetails;
  77. }
  78.  
  79. static get DefaultConfig(): HlsConfig {
  80. if (!Hls.defaultConfig) {
  81. return hlsDefaultConfig;
  82. }
  83.  
  84. return Hls.defaultConfig;
  85. }
  86.  
  87. /**
  88. * @type {HlsConfig}
  89. */
  90. static set DefaultConfig(defaultConfig: HlsConfig) {
  91. Hls.defaultConfig = defaultConfig;
  92. }
  93.  
  94. /**
  95. * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`.
  96. *
  97. * @constructs Hls
  98. * @param {HlsConfig} config
  99. */
  100. constructor(userConfig: Partial<HlsConfig> = {}) {
  101. const config = (this.config = mergeConfig(Hls.DefaultConfig, userConfig));
  102. this.userConfig = userConfig;
  103. enableLogs(config.debug);
  104.  
  105. this._autoLevelCapping = -1;
  106.  
  107. if (config.progressive) {
  108. enableStreamingMode(config);
  109. }
  110.  
  111. // core controllers and network loaders
  112. const {
  113. abrController: ConfigAbrController,
  114. bufferController: ConfigBufferController,
  115. capLevelController: ConfigCapLevelController,
  116. fpsController: ConfigFpsController,
  117. } = config;
  118. const abrController = (this.abrController = new ConfigAbrController(this));
  119. const bufferController = (this.bufferController =
  120. new ConfigBufferController(this));
  121. const capLevelController = (this.capLevelController =
  122. new ConfigCapLevelController(this));
  123.  
  124. const fpsController = new ConfigFpsController(this);
  125. const playListLoader = new PlaylistLoader(this);
  126. const keyLoader = new KeyLoader(this);
  127. const id3TrackController = new ID3TrackController(this);
  128.  
  129. // network controllers
  130. const levelController = (this.levelController = new LevelController(this));
  131. // FragmentTracker must be defined before StreamController because the order of event handling is important
  132. const fragmentTracker = new FragmentTracker(this);
  133. const streamController = (this.streamController = new StreamController(
  134. this,
  135. fragmentTracker
  136. ));
  137.  
  138. // Cap level controller uses streamController to flush the buffer
  139. capLevelController.setStreamController(streamController);
  140. // fpsController uses streamController to switch when frames are being dropped
  141. fpsController.setStreamController(streamController);
  142.  
  143. const networkControllers = [levelController, streamController];
  144.  
  145. this.networkControllers = networkControllers;
  146. const coreComponents = [
  147. playListLoader,
  148. keyLoader,
  149. abrController,
  150. bufferController,
  151. capLevelController,
  152. fpsController,
  153. id3TrackController,
  154. fragmentTracker,
  155. ];
  156.  
  157. this.audioTrackController = this.createController(
  158. config.audioTrackController,
  159. null,
  160. networkControllers
  161. );
  162. this.createController(
  163. config.audioStreamController,
  164. fragmentTracker,
  165. networkControllers
  166. );
  167. // subtitleTrackController must be defined before because the order of event handling is important
  168. this.subtitleTrackController = this.createController(
  169. config.subtitleTrackController,
  170. null,
  171. networkControllers
  172. );
  173. this.createController(
  174. config.subtitleStreamController,
  175. fragmentTracker,
  176. networkControllers
  177. );
  178. this.createController(config.timelineController, null, coreComponents);
  179. this.emeController = this.createController(
  180. config.emeController,
  181. null,
  182. coreComponents
  183. );
  184. this.cmcdController = this.createController(
  185. config.cmcdController,
  186. null,
  187. coreComponents
  188. );
  189. this.latencyController = this.createController(
  190. LatencyController,
  191. null,
  192. coreComponents
  193. );
  194.  
  195. this.coreComponents = coreComponents;
  196. }
  197.  
  198. createController(ControllerClass, fragmentTracker, components) {
  199. if (ControllerClass) {
  200. const controllerInstance = fragmentTracker
  201. ? new ControllerClass(this, fragmentTracker)
  202. : new ControllerClass(this);
  203. if (components) {
  204. components.push(controllerInstance);
  205. }
  206. return controllerInstance;
  207. }
  208. return null;
  209. }
  210.  
  211. // Delegate the EventEmitter through the public API of Hls.js
  212. on<E extends keyof HlsListeners, Context = undefined>(
  213. event: E,
  214. listener: HlsListeners[E],
  215. context: Context = this as any
  216. ) {
  217. this._emitter.on(event, listener, context);
  218. }
  219.  
  220. once<E extends keyof HlsListeners, Context = undefined>(
  221. event: E,
  222. listener: HlsListeners[E],
  223. context: Context = this as any
  224. ) {
  225. this._emitter.once(event, listener, context);
  226. }
  227.  
  228. removeAllListeners<E extends keyof HlsListeners>(event?: E | undefined) {
  229. this._emitter.removeAllListeners(event);
  230. }
  231.  
  232. off<E extends keyof HlsListeners, Context = undefined>(
  233. event: E,
  234. listener?: HlsListeners[E] | undefined,
  235. context: Context = this as any,
  236. once?: boolean | undefined
  237. ) {
  238. this._emitter.off(event, listener, context, once);
  239. }
  240.  
  241. listeners<E extends keyof HlsListeners>(event: E): HlsListeners[E][] {
  242. return this._emitter.listeners(event);
  243. }
  244.  
  245. emit<E extends keyof HlsListeners>(
  246. event: E,
  247. name: E,
  248. eventObject: Parameters<HlsListeners[E]>[1]
  249. ): boolean {
  250. return this._emitter.emit(event, name, eventObject);
  251. }
  252.  
  253. trigger<E extends keyof HlsListeners>(
  254. event: E,
  255. eventObject: Parameters<HlsListeners[E]>[1]
  256. ): boolean {
  257. if (this.config.debug) {
  258. return this.emit(event, event, eventObject);
  259. } else {
  260. try {
  261. return this.emit(event, event, eventObject);
  262. } catch (e) {
  263. logger.error(
  264. 'An internal error happened while handling event ' +
  265. event +
  266. '. Error message: "' +
  267. e.message +
  268. '". Here is a stacktrace:',
  269. e
  270. );
  271. this.trigger(Events.ERROR, {
  272. type: ErrorTypes.OTHER_ERROR,
  273. details: ErrorDetails.INTERNAL_EXCEPTION,
  274. fatal: false,
  275. event: event,
  276. error: e,
  277. });
  278. }
  279. }
  280. return false;
  281. }
  282.  
  283. listenerCount<E extends keyof HlsListeners>(event: E): number {
  284. return this._emitter.listenerCount(event);
  285. }
  286.  
  287. /**
  288. * Dispose of the instance
  289. */
  290. destroy() {
  291. logger.log('destroy');
  292. this.trigger(Events.DESTROYING, undefined);
  293. this.detachMedia();
  294. this.removeAllListeners();
  295. this._autoLevelCapping = -1;
  296. this.url = null;
  297.  
  298. this.networkControllers.forEach((component) => component.destroy());
  299. this.networkControllers.length = 0;
  300.  
  301. this.coreComponents.forEach((component) => component.destroy());
  302. this.coreComponents.length = 0;
  303. }
  304.  
  305. /**
  306. * Attaches Hls.js to a media element
  307. * @param {HTMLMediaElement} media
  308. */
  309. attachMedia(media: HTMLMediaElement) {
  310. logger.log('attachMedia');
  311. this._media = media;
  312. this.trigger(Events.MEDIA_ATTACHING, { media: media });
  313. }
  314.  
  315. /**
  316. * Detach Hls.js from the media
  317. */
  318. detachMedia() {
  319. logger.log('detachMedia');
  320. this.trigger(Events.MEDIA_DETACHING, undefined);
  321. this._media = null;
  322. }
  323.  
  324. /**
  325. * Set the source URL. Can be relative or absolute.
  326. * @param {string} url
  327. */
  328. loadSource(url: string) {
  329. this.stopLoad();
  330. const media = this.media;
  331. const loadedSource = this.url;
  332. const loadingSource = (this.url = URLToolkit.buildAbsoluteURL(
  333. self.location.href,
  334. url,
  335. {
  336. alwaysNormalize: true,
  337. }
  338. ));
  339. logger.log(`loadSource:${loadingSource}`);
  340. if (
  341. media &&
  342. loadedSource &&
  343. loadedSource !== loadingSource &&
  344. this.bufferController.hasSourceTypes()
  345. ) {
  346. this.detachMedia();
  347. this.attachMedia(media);
  348. }
  349. // when attaching to a source URL, trigger a playlist load
  350. this.trigger(Events.MANIFEST_LOADING, { url: url });
  351. }
  352.  
  353. /**
  354. * Start loading data from the stream source.
  355. * Depending on default config, client starts loading automatically when a source is set.
  356. *
  357. * @param {number} startPosition Set the start position to stream from
  358. * @default -1 None (from earliest point)
  359. */
  360. startLoad(startPosition: number = -1) {
  361. logger.log(`startLoad(${startPosition})`);
  362. this.networkControllers.forEach((controller) => {
  363. controller.startLoad(startPosition);
  364. });
  365. }
  366.  
  367. /**
  368. * Stop loading of any stream data.
  369. */
  370. stopLoad() {
  371. logger.log('stopLoad');
  372. this.networkControllers.forEach((controller) => {
  373. controller.stopLoad();
  374. });
  375. }
  376.  
  377. /**
  378. * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1)
  379. */
  380. swapAudioCodec() {
  381. logger.log('swapAudioCodec');
  382. this.streamController.swapAudioCodec();
  383. }
  384.  
  385. /**
  386. * When the media-element fails, this allows to detach and then re-attach it
  387. * as one call (convenience method).
  388. *
  389. * Automatic recovery of media-errors by this process is configurable.
  390. */
  391. recoverMediaError() {
  392. logger.log('recoverMediaError');
  393. const media = this._media;
  394. this.detachMedia();
  395. if (media) {
  396. this.attachMedia(media);
  397. }
  398. }
  399.  
  400. removeLevel(levelIndex, urlId = 0) {
  401. this.levelController.removeLevel(levelIndex, urlId);
  402. }
  403.  
  404. /**
  405. * @type {Level[]}
  406. */
  407. get levels(): Array<Level> {
  408. const levels = this.levelController.levels;
  409. return levels ? levels : [];
  410. }
  411.  
  412. /**
  413. * Index of quality level currently played
  414. * @type {number}
  415. */
  416. get currentLevel(): number {
  417. return this.streamController.currentLevel;
  418. }
  419.  
  420. /**
  421. * Set quality level index immediately .
  422. * This will flush the current buffer to replace the quality asap.
  423. * That means playback will interrupt at least shortly to re-buffer and re-sync eventually.
  424. * @type {number} -1 for automatic level selection
  425. */
  426. set currentLevel(newLevel: number) {
  427. logger.log(`set currentLevel:${newLevel}`);
  428. this.loadLevel = newLevel;
  429. this.abrController.clearTimer();
  430. this.streamController.immediateLevelSwitch();
  431. }
  432.  
  433. /**
  434. * Index of next quality level loaded as scheduled by stream controller.
  435. * @type {number}
  436. */
  437. get nextLevel(): number {
  438. return this.streamController.nextLevel;
  439. }
  440.  
  441. /**
  442. * Set quality level index for next loaded data.
  443. * This will switch the video quality asap, without interrupting playback.
  444. * May abort current loading of data, and flush parts of buffer (outside currently played fragment region).
  445. * @type {number} -1 for automatic level selection
  446. */
  447. set nextLevel(newLevel: number) {
  448. logger.log(`set nextLevel:${newLevel}`);
  449. this.levelController.manualLevel = newLevel;
  450. this.streamController.nextLevelSwitch();
  451. }
  452.  
  453. /**
  454. * Return the quality level of the currently or last (of none is loaded currently) segment
  455. * @type {number}
  456. */
  457. get loadLevel(): number {
  458. return this.levelController.level;
  459. }
  460.  
  461. /**
  462. * Set quality level index for next loaded data in a conservative way.
  463. * This will switch the quality without flushing, but interrupt current loading.
  464. * Thus the moment when the quality switch will appear in effect will only be after the already existing buffer.
  465. * @type {number} newLevel -1 for automatic level selection
  466. */
  467. set loadLevel(newLevel: number) {
  468. logger.log(`set loadLevel:${newLevel}`);
  469. this.levelController.manualLevel = newLevel;
  470. }
  471.  
  472. /**
  473. * get next quality level loaded
  474. * @type {number}
  475. */
  476. get nextLoadLevel(): number {
  477. return this.levelController.nextLoadLevel;
  478. }
  479.  
  480. /**
  481. * Set quality level of next loaded segment in a fully "non-destructive" way.
  482. * Same as `loadLevel` but will wait for next switch (until current loading is done).
  483. * @type {number} level
  484. */
  485. set nextLoadLevel(level: number) {
  486. this.levelController.nextLoadLevel = level;
  487. }
  488.  
  489. /**
  490. * Return "first level": like a default level, if not set,
  491. * falls back to index of first level referenced in manifest
  492. * @type {number}
  493. */
  494. get firstLevel(): number {
  495. return Math.max(this.levelController.firstLevel, this.minAutoLevel);
  496. }
  497.  
  498. /**
  499. * Sets "first-level", see getter.
  500. * @type {number}
  501. */
  502. set firstLevel(newLevel: number) {
  503. logger.log(`set firstLevel:${newLevel}`);
  504. this.levelController.firstLevel = newLevel;
  505. }
  506.  
  507. /**
  508. * Return start level (level of first fragment that will be played back)
  509. * if not overrided by user, first level appearing in manifest will be used as start level
  510. * if -1 : automatic start level selection, playback will start from level matching download bandwidth
  511. * (determined from download of first segment)
  512. * @type {number}
  513. */
  514. get startLevel(): number {
  515. return this.levelController.startLevel;
  516. }
  517.  
  518. /**
  519. * set start level (level of first fragment that will be played back)
  520. * if not overrided by user, first level appearing in manifest will be used as start level
  521. * if -1 : automatic start level selection, playback will start from level matching download bandwidth
  522. * (determined from download of first segment)
  523. * @type {number} newLevel
  524. */
  525. set startLevel(newLevel: number) {
  526. logger.log(`set startLevel:${newLevel}`);
  527. // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel
  528. if (newLevel !== -1) {
  529. newLevel = Math.max(newLevel, this.minAutoLevel);
  530. }
  531.  
  532. this.levelController.startLevel = newLevel;
  533. }
  534.  
  535. /**
  536. * Get the current setting for capLevelToPlayerSize
  537. *
  538. * @type {boolean}
  539. */
  540. get capLevelToPlayerSize(): boolean {
  541. return this.config.capLevelToPlayerSize;
  542. }
  543.  
  544. /**
  545. * set dynamically set capLevelToPlayerSize against (`CapLevelController`)
  546. *
  547. * @type {boolean}
  548. */
  549. set capLevelToPlayerSize(shouldStartCapping: boolean) {
  550. const newCapLevelToPlayerSize = !!shouldStartCapping;
  551.  
  552. if (newCapLevelToPlayerSize !== this.config.capLevelToPlayerSize) {
  553. if (newCapLevelToPlayerSize) {
  554. this.capLevelController.startCapping(); // If capping occurs, nextLevelSwitch will happen based on size.
  555. } else {
  556. this.capLevelController.stopCapping();
  557. this.autoLevelCapping = -1;
  558. this.streamController.nextLevelSwitch(); // Now we're uncapped, get the next level asap.
  559. }
  560.  
  561. this.config.capLevelToPlayerSize = newCapLevelToPlayerSize;
  562. }
  563. }
  564.  
  565. /**
  566. * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`)
  567. * @type {number}
  568. */
  569. get autoLevelCapping(): number {
  570. return this._autoLevelCapping;
  571. }
  572.  
  573. /**
  574. * get bandwidth estimate
  575. * @type {number}
  576. */
  577. get bandwidthEstimate(): number {
  578. const { bwEstimator } = this.abrController;
  579. if (!bwEstimator) {
  580. return NaN;
  581. }
  582. return bwEstimator.getEstimate();
  583. }
  584.  
  585. /**
  586. * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`)
  587. * @type {number}
  588. */
  589. set autoLevelCapping(newLevel: number) {
  590. if (this._autoLevelCapping !== newLevel) {
  591. logger.log(`set autoLevelCapping:${newLevel}`);
  592. this._autoLevelCapping = newLevel;
  593. }
  594. }
  595.  
  596. /**
  597. * True when automatic level selection enabled
  598. * @type {boolean}
  599. */
  600. get autoLevelEnabled(): boolean {
  601. return this.levelController.manualLevel === -1;
  602. }
  603.  
  604. /**
  605. * Level set manually (if any)
  606. * @type {number}
  607. */
  608. get manualLevel(): number {
  609. return this.levelController.manualLevel;
  610. }
  611.  
  612. /**
  613. * min level selectable in auto mode according to config.minAutoBitrate
  614. * @type {number}
  615. */
  616. get minAutoLevel(): number {
  617. const {
  618. levels,
  619. config: { minAutoBitrate },
  620. } = this;
  621. if (!levels) return 0;
  622.  
  623. const len = levels.length;
  624. for (let i = 0; i < len; i++) {
  625. if (levels[i].maxBitrate > minAutoBitrate) {
  626. return i;
  627. }
  628. }
  629.  
  630. return 0;
  631. }
  632.  
  633. /**
  634. * max level selectable in auto mode according to autoLevelCapping
  635. * @type {number}
  636. */
  637. get maxAutoLevel(): number {
  638. const { levels, autoLevelCapping } = this;
  639.  
  640. let maxAutoLevel;
  641. if (autoLevelCapping === -1 && levels && levels.length) {
  642. maxAutoLevel = levels.length - 1;
  643. } else {
  644. maxAutoLevel = autoLevelCapping;
  645. }
  646.  
  647. return maxAutoLevel;
  648. }
  649.  
  650. /**
  651. * next automatically selected quality level
  652. * @type {number}
  653. */
  654. get nextAutoLevel(): number {
  655. // ensure next auto level is between min and max auto level
  656. return Math.min(
  657. Math.max(this.abrController.nextAutoLevel, this.minAutoLevel),
  658. this.maxAutoLevel
  659. );
  660. }
  661.  
  662. /**
  663. * this setter is used to force next auto level.
  664. * this is useful to force a switch down in auto mode:
  665. * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example)
  666. * forced value is valid for one fragment. upon successful frag loading at forced level,
  667. * this value will be resetted to -1 by ABR controller.
  668. * @type {number}
  669. */
  670. set nextAutoLevel(nextLevel: number) {
  671. this.abrController.nextAutoLevel = Math.max(this.minAutoLevel, nextLevel);
  672. }
  673.  
  674. /**
  675. * @type {AudioTrack[]}
  676. */
  677. get audioTracks(): Array<MediaPlaylist> {
  678. const audioTrackController = this.audioTrackController;
  679. return audioTrackController ? audioTrackController.audioTracks : [];
  680. }
  681.  
  682. /**
  683. * index of the selected audio track (index in audio track lists)
  684. * @type {number}
  685. */
  686. get audioTrack(): number {
  687. const audioTrackController = this.audioTrackController;
  688. return audioTrackController ? audioTrackController.audioTrack : -1;
  689. }
  690.  
  691. /**
  692. * selects an audio track, based on its index in audio track lists
  693. * @type {number}
  694. */
  695. set audioTrack(audioTrackId: number) {
  696. const audioTrackController = this.audioTrackController;
  697. if (audioTrackController) {
  698. audioTrackController.audioTrack = audioTrackId;
  699. }
  700. }
  701.  
  702. /**
  703. * get alternate subtitle tracks list from playlist
  704. * @type {MediaPlaylist[]}
  705. */
  706. get subtitleTracks(): Array<MediaPlaylist> {
  707. const subtitleTrackController = this.subtitleTrackController;
  708. return subtitleTrackController
  709. ? subtitleTrackController.subtitleTracks
  710. : [];
  711. }
  712.  
  713. /**
  714. * index of the selected subtitle track (index in subtitle track lists)
  715. * @type {number}
  716. */
  717. get subtitleTrack(): number {
  718. const subtitleTrackController = this.subtitleTrackController;
  719. return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1;
  720. }
  721.  
  722. get media() {
  723. return this._media;
  724. }
  725.  
  726. /**
  727. * select an subtitle track, based on its index in subtitle track lists
  728. * @type {number}
  729. */
  730. set subtitleTrack(subtitleTrackId: number) {
  731. const subtitleTrackController = this.subtitleTrackController;
  732. if (subtitleTrackController) {
  733. subtitleTrackController.subtitleTrack = subtitleTrackId;
  734. }
  735. }
  736.  
  737. /**
  738. * @type {boolean}
  739. */
  740. get subtitleDisplay(): boolean {
  741. const subtitleTrackController = this.subtitleTrackController;
  742. return subtitleTrackController
  743. ? subtitleTrackController.subtitleDisplay
  744. : false;
  745. }
  746.  
  747. /**
  748. * Enable/disable subtitle display rendering
  749. * @type {boolean}
  750. */
  751. set subtitleDisplay(value: boolean) {
  752. const subtitleTrackController = this.subtitleTrackController;
  753. if (subtitleTrackController) {
  754. subtitleTrackController.subtitleDisplay = value;
  755. }
  756. }
  757.  
  758. /**
  759. * get mode for Low-Latency HLS loading
  760. * @type {boolean}
  761. */
  762. get lowLatencyMode() {
  763. return this.config.lowLatencyMode;
  764. }
  765.  
  766. /**
  767. * Enable/disable Low-Latency HLS part playlist and segment loading, and start live streams at playlist PART-HOLD-BACK rather than HOLD-BACK.
  768. * @type {boolean}
  769. */
  770. set lowLatencyMode(mode: boolean) {
  771. this.config.lowLatencyMode = mode;
  772. }
  773.  
  774. /**
  775. * position (in seconds) of live sync point (ie edge of live position minus safety delay defined by ```hls.config.liveSyncDuration```)
  776. * @type {number}
  777. */
  778. get liveSyncPosition(): number | null {
  779. return this.latencyController.liveSyncPosition;
  780. }
  781.  
  782. /**
  783. * estimated position (in seconds) of live edge (ie edge of live playlist plus time sync playlist advanced)
  784. * returns 0 before first playlist is loaded
  785. * @type {number}
  786. */
  787. get latency() {
  788. return this.latencyController.latency;
  789. }
  790.  
  791. /**
  792. * maximum distance from the edge before the player seeks forward to ```hls.liveSyncPosition```
  793. * configured using ```liveMaxLatencyDurationCount``` (multiple of target duration) or ```liveMaxLatencyDuration```
  794. * returns 0 before first playlist is loaded
  795. * @type {number}
  796. */
  797. get maxLatency(): number {
  798. return this.latencyController.maxLatency;
  799. }
  800.  
  801. /**
  802. * target distance from the edge as calculated by the latency controller
  803. * @type {number}
  804. */
  805. get targetLatency(): number | null {
  806. return this.latencyController.targetLatency;
  807. }
  808.  
  809. /**
  810. * the rate at which the edge of the current live playlist is advancing or 1 if there is none
  811. * @type {number}
  812. */
  813. get drift(): number | null {
  814. return this.latencyController.drift;
  815. }
  816.  
  817. /**
  818. * set to true when startLoad is called before MANIFEST_PARSED event
  819. * @type {boolean}
  820. */
  821. get forceStartLoad(): boolean {
  822. return this.streamController.forceStartLoad;
  823. }
  824. }
  825.  
  826. export type {
  827. MediaPlaylist,
  828. ErrorDetails,
  829. ErrorTypes,
  830. Events,
  831. Level,
  832. HlsListeners,
  833. HlsEventEmitter,
  834. HlsConfig,
  835. Fragment,
  836. };
  837.  
  838. export type {
  839. ABRControllerConfig,
  840. BufferControllerConfig,
  841. CapLevelControllerConfig,
  842. CMCDControllerConfig,
  843. EMEControllerConfig,
  844. DRMSystemOptions,
  845. FPSControllerConfig,
  846. FragmentLoaderConfig,
  847. FragmentLoaderConstructor,
  848. LevelControllerConfig,
  849. MP4RemuxerConfig,
  850. PlaylistLoaderConfig,
  851. PlaylistLoaderConstructor,
  852. StreamControllerConfig,
  853. LatencyControllerConfig,
  854. TimelineControllerConfig,
  855. TSDemuxerConfig,
  856. } from './config';
  857. export type { CuesInterface } from './utils/cues';
  858. export type { MediaKeyFunc, KeySystems } from './utils/mediakeys-helper';
  859. export type { LoadStats } from './loader/load-stats';
  860. export type { LevelKey } from './loader/level-key';
  861. export type { LevelDetails } from './loader/level-details';
  862. export type { SourceBufferName } from './types/buffer';
  863. export type { MetadataSample, UserdataSample } from './types/demuxer';
  864. export type {
  865. LevelParsed,
  866. LevelAttributes,
  867. HlsUrlParameters,
  868. HlsSkip,
  869. } from './types/level';
  870. export type {
  871. PlaylistLevelType,
  872. HlsChunkPerformanceTiming,
  873. HlsPerformanceTiming,
  874. PlaylistContextType,
  875. PlaylistLoaderContext,
  876. FragmentLoaderContext,
  877. Loader,
  878. LoaderStats,
  879. LoaderContext,
  880. LoaderResponse,
  881. LoaderConfiguration,
  882. LoaderCallbacks,
  883. LoaderOnProgress,
  884. LoaderOnAbort,
  885. LoaderOnError,
  886. LoaderOnSuccess,
  887. LoaderOnTimeout,
  888. HlsProgressivePerformanceTiming,
  889. } from './types/loader';
  890. export type {
  891. MediaPlaylistType,
  892. MainPlaylistType,
  893. AudioPlaylistType,
  894. SubtitlePlaylistType,
  895. } from './types/media-playlist';
  896. export type { Track, TrackSet } from './types/track';
  897. export type { ChunkMetadata } from './types/transmuxer';
  898. export type {
  899. BaseSegment,
  900. Part,
  901. ElementaryStreams,
  902. ElementaryStreamTypes,
  903. ElementaryStreamInfo,
  904. } from './loader/fragment';
  905. export type {
  906. TrackLoadingData,
  907. TrackLoadedData,
  908. AudioTrackLoadedData,
  909. AudioTracksUpdatedData,
  910. AudioTrackSwitchedData,
  911. AudioTrackSwitchingData,
  912. BackBufferData,
  913. BufferAppendedData,
  914. BufferAppendingData,
  915. BufferCodecsData,
  916. BufferCreatedData,
  917. BufferEOSData,
  918. BufferFlushedData,
  919. BufferFlushingData,
  920. CuesParsedData,
  921. ErrorData,
  922. FPSDropData,
  923. FPSDropLevelCappingData,
  924. FragBufferedData,
  925. FragChangedData,
  926. FragDecryptedData,
  927. FragLoadedData,
  928. FragLoadEmergencyAbortedData,
  929. FragLoadingData,
  930. FragParsedData,
  931. FragParsingInitSegmentData,
  932. FragParsingMetadataData,
  933. FragParsingUserdataData,
  934. InitPTSFoundData,
  935. KeyLoadedData,
  936. KeyLoadingData,
  937. LevelLoadedData,
  938. LevelLoadingData,
  939. LevelPTSUpdatedData,
  940. LevelsUpdatedData,
  941. LevelSwitchedData,
  942. LevelSwitchingData,
  943. LevelUpdatedData,
  944. LiveBackBufferData,
  945. ManifestLoadedData,
  946. ManifestLoadingData,
  947. ManifestParsedData,
  948. MediaAttachedData,
  949. MediaAttachingData,
  950. NonNativeTextTrack,
  951. NonNativeTextTracksData,
  952. SubtitleFragProcessedData,
  953. SubtitleTrackLoadedData,
  954. SubtitleTracksUpdatedData,
  955. SubtitleTrackSwitchData,
  956. } from './types/events';
  957. export type { AttrList } from './utils/attr-list';