Home Reference Source

src/hls.ts

  1. import * as URLToolkit from 'url-toolkit';
  2. import PlaylistLoader from './loader/playlist-loader';
  3. import ID3TrackController from './controller/id3-track-controller';
  4. import LatencyController from './controller/latency-controller';
  5. import LevelController from './controller/level-controller';
  6. import { FragmentTracker } from './controller/fragment-tracker';
  7. import KeyLoader from './loader/key-loader';
  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 { HdcpLevel, HdcpLevels, Level } from './types/level';
  27. import type { Fragment } from './loader/fragment';
  28. import { BufferInfo } from './utils/buffer-helper';
  29.  
  30. /**
  31. * @module Hls
  32. * @class
  33. * @constructor
  34. */
  35. export default class Hls implements HlsEventEmitter {
  36. private static defaultConfig?: HlsConfig;
  37.  
  38. public readonly config: HlsConfig;
  39. public readonly userConfig: Partial<HlsConfig>;
  40.  
  41. private coreComponents: ComponentAPI[];
  42. private networkControllers: NetworkComponentAPI[];
  43.  
  44. private _emitter: HlsEventEmitter = new EventEmitter();
  45. private _autoLevelCapping: number;
  46. private _maxHdcpLevel: HdcpLevel = null;
  47. private abrController: AbrController;
  48. private bufferController: BufferController;
  49. private capLevelController: CapLevelController;
  50. private latencyController: LatencyController;
  51. private levelController: LevelController;
  52. private streamController: StreamController;
  53. private audioTrackController: AudioTrackController;
  54. private subtitleTrackController: SubtitleTrackController;
  55. private emeController: EMEController;
  56. private cmcdController: CMCDController;
  57.  
  58. private _media: HTMLMediaElement | null = null;
  59. private url: string | null = null;
  60.  
  61. static get version(): string {
  62. return __VERSION__;
  63. }
  64.  
  65. static isSupported(): boolean {
  66. return isSupported();
  67. }
  68.  
  69. static get Events() {
  70. return Events;
  71. }
  72.  
  73. static get ErrorTypes() {
  74. return ErrorTypes;
  75. }
  76.  
  77. static get ErrorDetails() {
  78. return ErrorDetails;
  79. }
  80.  
  81. static get DefaultConfig(): HlsConfig {
  82. if (!Hls.defaultConfig) {
  83. return hlsDefaultConfig;
  84. }
  85.  
  86. return Hls.defaultConfig;
  87. }
  88.  
  89. /**
  90. * @type {HlsConfig}
  91. */
  92. static set DefaultConfig(defaultConfig: HlsConfig) {
  93. Hls.defaultConfig = defaultConfig;
  94. }
  95.  
  96. /**
  97. * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`.
  98. *
  99. * @constructs Hls
  100. * @param {HlsConfig} config
  101. */
  102. constructor(userConfig: Partial<HlsConfig> = {}) {
  103. const config = (this.config = mergeConfig(Hls.DefaultConfig, userConfig));
  104. this.userConfig = userConfig;
  105. enableLogs(config.debug, 'Hls instance');
  106.  
  107. this._autoLevelCapping = -1;
  108.  
  109. if (config.progressive) {
  110. enableStreamingMode(config);
  111. }
  112.  
  113. // core controllers and network loaders
  114. const {
  115. abrController: ConfigAbrController,
  116. bufferController: ConfigBufferController,
  117. capLevelController: ConfigCapLevelController,
  118. fpsController: ConfigFpsController,
  119. } = config;
  120. const abrController = (this.abrController = new ConfigAbrController(this));
  121. const bufferController = (this.bufferController =
  122. new ConfigBufferController(this));
  123. const capLevelController = (this.capLevelController =
  124. new ConfigCapLevelController(this));
  125.  
  126. const fpsController = new ConfigFpsController(this);
  127. const playListLoader = new PlaylistLoader(this);
  128. const id3TrackController = new ID3TrackController(this);
  129.  
  130. // network controllers
  131. const levelController = (this.levelController = new LevelController(this));
  132. // FragmentTracker must be defined before StreamController because the order of event handling is important
  133. const fragmentTracker = new FragmentTracker(this);
  134. const keyLoader = new KeyLoader(this.config);
  135. const streamController = (this.streamController = new StreamController(
  136. this,
  137. fragmentTracker,
  138. keyLoader
  139. ));
  140.  
  141. // Cap level controller uses streamController to flush the buffer
  142. capLevelController.setStreamController(streamController);
  143. // fpsController uses streamController to switch when frames are being dropped
  144. fpsController.setStreamController(streamController);
  145.  
  146. const networkControllers: NetworkComponentAPI[] = [
  147. playListLoader,
  148. levelController,
  149. streamController,
  150. ];
  151.  
  152. this.networkControllers = networkControllers;
  153. const coreComponents: ComponentAPI[] = [
  154. abrController,
  155. bufferController,
  156. capLevelController,
  157. fpsController,
  158. id3TrackController,
  159. fragmentTracker,
  160. ];
  161.  
  162. this.audioTrackController = this.createController(
  163. config.audioTrackController,
  164. networkControllers
  165. );
  166. const AudioStreamControllerClass = config.audioStreamController;
  167. if (AudioStreamControllerClass) {
  168. networkControllers.push(
  169. new AudioStreamControllerClass(this, fragmentTracker, keyLoader)
  170. );
  171. }
  172. // subtitleTrackController must be defined before subtitleStreamController because the order of event handling is important
  173. this.subtitleTrackController = this.createController(
  174. config.subtitleTrackController,
  175. networkControllers
  176. );
  177. const SubtitleStreamControllerClass = config.subtitleStreamController;
  178. if (SubtitleStreamControllerClass) {
  179. networkControllers.push(
  180. new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader)
  181. );
  182. }
  183. this.createController(config.timelineController, coreComponents);
  184. keyLoader.emeController = this.emeController = this.createController(
  185. config.emeController,
  186. coreComponents
  187. );
  188. this.cmcdController = this.createController(
  189. config.cmcdController,
  190. coreComponents
  191. );
  192. this.latencyController = this.createController(
  193. LatencyController,
  194. coreComponents
  195. );
  196.  
  197. this.coreComponents = coreComponents;
  198. }
  199.  
  200. createController(ControllerClass, components) {
  201. if (ControllerClass) {
  202. const controllerInstance = 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. get maxHdcpLevel(): HdcpLevel {
  597. return this._maxHdcpLevel;
  598. }
  599.  
  600. set maxHdcpLevel(value: HdcpLevel) {
  601. if (HdcpLevels.indexOf(value) > -1) {
  602. this._maxHdcpLevel = value;
  603. }
  604. }
  605.  
  606. /**
  607. * True when automatic level selection enabled
  608. * @type {boolean}
  609. */
  610. get autoLevelEnabled(): boolean {
  611. return this.levelController.manualLevel === -1;
  612. }
  613.  
  614. /**
  615. * Level set manually (if any)
  616. * @type {number}
  617. */
  618. get manualLevel(): number {
  619. return this.levelController.manualLevel;
  620. }
  621.  
  622. /**
  623. * min level selectable in auto mode according to config.minAutoBitrate
  624. * @type {number}
  625. */
  626. get minAutoLevel(): number {
  627. const {
  628. levels,
  629. config: { minAutoBitrate },
  630. } = this;
  631. if (!levels) return 0;
  632.  
  633. const len = levels.length;
  634. for (let i = 0; i < len; i++) {
  635. if (levels[i].maxBitrate >= minAutoBitrate) {
  636. return i;
  637. }
  638. }
  639.  
  640. return 0;
  641. }
  642.  
  643. /**
  644. * max level selectable in auto mode according to autoLevelCapping
  645. * @type {number}
  646. */
  647. get maxAutoLevel(): number {
  648. const { levels, autoLevelCapping, maxHdcpLevel } = this;
  649.  
  650. let maxAutoLevel;
  651. if (autoLevelCapping === -1 && levels && levels.length) {
  652. maxAutoLevel = levels.length - 1;
  653. } else {
  654. maxAutoLevel = autoLevelCapping;
  655. }
  656.  
  657. if (maxHdcpLevel) {
  658. for (let i = maxAutoLevel; i--; ) {
  659. const hdcpLevel = levels[i].attrs['HDCP-LEVEL'];
  660. if (hdcpLevel && hdcpLevel <= maxHdcpLevel) {
  661. return i;
  662. }
  663. }
  664. }
  665.  
  666. return maxAutoLevel;
  667. }
  668.  
  669. /**
  670. * next automatically selected quality level
  671. * @type {number}
  672. */
  673. get nextAutoLevel(): number {
  674. // ensure next auto level is between min and max auto level
  675. return Math.min(
  676. Math.max(this.abrController.nextAutoLevel, this.minAutoLevel),
  677. this.maxAutoLevel
  678. );
  679. }
  680.  
  681. /**
  682. * this setter is used to force next auto level.
  683. * this is useful to force a switch down in auto mode:
  684. * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example)
  685. * forced value is valid for one fragment. upon successful frag loading at forced level,
  686. * this value will be resetted to -1 by ABR controller.
  687. * @type {number}
  688. */
  689. set nextAutoLevel(nextLevel: number) {
  690. this.abrController.nextAutoLevel = Math.max(this.minAutoLevel, nextLevel);
  691. }
  692.  
  693. /**
  694. * get the datetime value relative to media.currentTime for the active level Program Date Time if present
  695. * @type {Date}
  696. */
  697. public get playingDate(): Date | null {
  698. return this.streamController.currentProgramDateTime;
  699. }
  700.  
  701. public get mainForwardBufferInfo(): BufferInfo | null {
  702. return this.streamController.getMainFwdBufferInfo();
  703. }
  704.  
  705. /**
  706. * @type {AudioTrack[]}
  707. */
  708. get audioTracks(): Array<MediaPlaylist> {
  709. const audioTrackController = this.audioTrackController;
  710. return audioTrackController ? audioTrackController.audioTracks : [];
  711. }
  712.  
  713. /**
  714. * index of the selected audio track (index in audio track lists)
  715. * @type {number}
  716. */
  717. get audioTrack(): number {
  718. const audioTrackController = this.audioTrackController;
  719. return audioTrackController ? audioTrackController.audioTrack : -1;
  720. }
  721.  
  722. /**
  723. * selects an audio track, based on its index in audio track lists
  724. * @type {number}
  725. */
  726. set audioTrack(audioTrackId: number) {
  727. const audioTrackController = this.audioTrackController;
  728. if (audioTrackController) {
  729. audioTrackController.audioTrack = audioTrackId;
  730. }
  731. }
  732.  
  733. /**
  734. * get alternate subtitle tracks list from playlist
  735. * @type {MediaPlaylist[]}
  736. */
  737. get subtitleTracks(): Array<MediaPlaylist> {
  738. const subtitleTrackController = this.subtitleTrackController;
  739. return subtitleTrackController
  740. ? subtitleTrackController.subtitleTracks
  741. : [];
  742. }
  743.  
  744. /**
  745. * index of the selected subtitle track (index in subtitle track lists)
  746. * @type {number}
  747. */
  748. get subtitleTrack(): number {
  749. const subtitleTrackController = this.subtitleTrackController;
  750. return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1;
  751. }
  752.  
  753. get media() {
  754. return this._media;
  755. }
  756.  
  757. /**
  758. * select an subtitle track, based on its index in subtitle track lists
  759. * @type {number}
  760. */
  761. set subtitleTrack(subtitleTrackId: number) {
  762. const subtitleTrackController = this.subtitleTrackController;
  763. if (subtitleTrackController) {
  764. subtitleTrackController.subtitleTrack = subtitleTrackId;
  765. }
  766. }
  767.  
  768. /**
  769. * @type {boolean}
  770. */
  771. get subtitleDisplay(): boolean {
  772. const subtitleTrackController = this.subtitleTrackController;
  773. return subtitleTrackController
  774. ? subtitleTrackController.subtitleDisplay
  775. : false;
  776. }
  777.  
  778. /**
  779. * Enable/disable subtitle display rendering
  780. * @type {boolean}
  781. */
  782. set subtitleDisplay(value: boolean) {
  783. const subtitleTrackController = this.subtitleTrackController;
  784. if (subtitleTrackController) {
  785. subtitleTrackController.subtitleDisplay = value;
  786. }
  787. }
  788.  
  789. /**
  790. * get mode for Low-Latency HLS loading
  791. * @type {boolean}
  792. */
  793. get lowLatencyMode() {
  794. return this.config.lowLatencyMode;
  795. }
  796.  
  797. /**
  798. * Enable/disable Low-Latency HLS part playlist and segment loading, and start live streams at playlist PART-HOLD-BACK rather than HOLD-BACK.
  799. * @type {boolean}
  800. */
  801. set lowLatencyMode(mode: boolean) {
  802. this.config.lowLatencyMode = mode;
  803. }
  804.  
  805. /**
  806. * position (in seconds) of live sync point (ie edge of live position minus safety delay defined by ```hls.config.liveSyncDuration```)
  807. * @type {number}
  808. */
  809. get liveSyncPosition(): number | null {
  810. return this.latencyController.liveSyncPosition;
  811. }
  812.  
  813. /**
  814. * estimated position (in seconds) of live edge (ie edge of live playlist plus time sync playlist advanced)
  815. * returns 0 before first playlist is loaded
  816. * @type {number}
  817. */
  818. get latency() {
  819. return this.latencyController.latency;
  820. }
  821.  
  822. /**
  823. * maximum distance from the edge before the player seeks forward to ```hls.liveSyncPosition```
  824. * configured using ```liveMaxLatencyDurationCount``` (multiple of target duration) or ```liveMaxLatencyDuration```
  825. * returns 0 before first playlist is loaded
  826. * @type {number}
  827. */
  828. get maxLatency(): number {
  829. return this.latencyController.maxLatency;
  830. }
  831.  
  832. /**
  833. * target distance from the edge as calculated by the latency controller
  834. * @type {number}
  835. */
  836. get targetLatency(): number | null {
  837. return this.latencyController.targetLatency;
  838. }
  839.  
  840. /**
  841. * the rate at which the edge of the current live playlist is advancing or 1 if there is none
  842. * @type {number}
  843. */
  844. get drift(): number | null {
  845. return this.latencyController.drift;
  846. }
  847.  
  848. /**
  849. * set to true when startLoad is called before MANIFEST_PARSED event
  850. * @type {boolean}
  851. */
  852. get forceStartLoad(): boolean {
  853. return this.streamController.forceStartLoad;
  854. }
  855. }
  856.  
  857. export type {
  858. MediaPlaylist,
  859. ErrorDetails,
  860. ErrorTypes,
  861. Events,
  862. Level,
  863. HlsListeners,
  864. HlsEventEmitter,
  865. HlsConfig,
  866. Fragment,
  867. };
  868.  
  869. export type {
  870. ABRControllerConfig,
  871. BufferControllerConfig,
  872. CapLevelControllerConfig,
  873. CMCDControllerConfig,
  874. EMEControllerConfig,
  875. DRMSystemOptions,
  876. FPSControllerConfig,
  877. FragmentLoaderConfig,
  878. FragmentLoaderConstructor,
  879. LevelControllerConfig,
  880. MP4RemuxerConfig,
  881. PlaylistLoaderConfig,
  882. PlaylistLoaderConstructor,
  883. StreamControllerConfig,
  884. LatencyControllerConfig,
  885. MetadataControllerConfig,
  886. TimelineControllerConfig,
  887. TSDemuxerConfig,
  888. } from './config';
  889. export type { CuesInterface } from './utils/cues';
  890. export type {
  891. MediaKeyFunc,
  892. KeySystems,
  893. KeySystemFormats,
  894. } from './utils/mediakeys-helper';
  895. export type { DateRange } from './loader/date-range';
  896. export type { LoadStats } from './loader/load-stats';
  897. export type { LevelKey } from './loader/level-key';
  898. export type { LevelDetails } from './loader/level-details';
  899. export type { SourceBufferName } from './types/buffer';
  900. export type {
  901. MetadataSample,
  902. MetadataSchema,
  903. UserdataSample,
  904. } from './types/demuxer';
  905. export type {
  906. HdcpLevel,
  907. HdcpLevels,
  908. HlsSkip,
  909. HlsUrlParameters,
  910. LevelAttributes,
  911. LevelParsed,
  912. } from './types/level';
  913. export type {
  914. PlaylistLevelType,
  915. HlsChunkPerformanceTiming,
  916. HlsPerformanceTiming,
  917. PlaylistContextType,
  918. PlaylistLoaderContext,
  919. FragmentLoaderContext,
  920. Loader,
  921. LoaderStats,
  922. LoaderContext,
  923. LoaderResponse,
  924. LoaderConfiguration,
  925. LoaderCallbacks,
  926. LoaderOnProgress,
  927. LoaderOnAbort,
  928. LoaderOnError,
  929. LoaderOnSuccess,
  930. LoaderOnTimeout,
  931. HlsProgressivePerformanceTiming,
  932. } from './types/loader';
  933. export type {
  934. MediaPlaylistType,
  935. MainPlaylistType,
  936. AudioPlaylistType,
  937. SubtitlePlaylistType,
  938. } from './types/media-playlist';
  939. export type { Track, TrackSet } from './types/track';
  940. export type { ChunkMetadata } from './types/transmuxer';
  941. export type {
  942. BaseSegment,
  943. Part,
  944. ElementaryStreams,
  945. ElementaryStreamTypes,
  946. ElementaryStreamInfo,
  947. } from './loader/fragment';
  948. export type {
  949. TrackLoadingData,
  950. TrackLoadedData,
  951. AudioTrackLoadedData,
  952. AudioTracksUpdatedData,
  953. AudioTrackSwitchedData,
  954. AudioTrackSwitchingData,
  955. BackBufferData,
  956. BufferAppendedData,
  957. BufferAppendingData,
  958. BufferCodecsData,
  959. BufferCreatedData,
  960. BufferEOSData,
  961. BufferFlushedData,
  962. BufferFlushingData,
  963. CuesParsedData,
  964. ErrorData,
  965. FPSDropData,
  966. FPSDropLevelCappingData,
  967. FragBufferedData,
  968. FragChangedData,
  969. FragDecryptedData,
  970. FragLoadedData,
  971. FragLoadEmergencyAbortedData,
  972. FragLoadingData,
  973. FragParsedData,
  974. FragParsingInitSegmentData,
  975. FragParsingMetadataData,
  976. FragParsingUserdataData,
  977. InitPTSFoundData,
  978. KeyLoadedData,
  979. KeyLoadingData,
  980. LevelLoadedData,
  981. LevelLoadingData,
  982. LevelPTSUpdatedData,
  983. LevelsUpdatedData,
  984. LevelSwitchedData,
  985. LevelSwitchingData,
  986. LevelUpdatedData,
  987. LiveBackBufferData,
  988. ManifestLoadedData,
  989. ManifestLoadingData,
  990. ManifestParsedData,
  991. MediaAttachedData,
  992. MediaAttachingData,
  993. NonNativeTextTrack,
  994. NonNativeTextTracksData,
  995. SubtitleFragProcessedData,
  996. SubtitleTrackLoadedData,
  997. SubtitleTracksUpdatedData,
  998. SubtitleTrackSwitchData,
  999. } from './types/events';
  1000. export type { AttrList } from './utils/attr-list';