Home Reference Source

src/hls.ts

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