Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 38 additions & 7 deletions .escheckrc
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
{
"ecmaVersion": "es5",
"modules": "false",
"files": [
"./dist/**/*.js"
]
}
[
{
"ecmaVersion": "es5",
"module": false,
"files": [
"./dist/hls.js",
"./dist/hls.min.js",
"./dist/hls.light.js",
"./dist/hls.light.min.js",
"./dist/hls.worker.js"
]
},
{
"ecmaVersion": "es2016",
"module": false,
"checkFeatures": true,
"ignore": "globalThis,ErrorCause",
"files": [
"./dist/hls.light.js",
"./dist/hls.light.min.js",
"./dist/hls.worker.js"
]
},
{
"ecmaVersion": "es2017",
"module": false,
"checkFeatures": true,
"ignore": "globalThis,ErrorCause",
"files": ["./dist/hls.js", "./dist/hls.min.js"]
},
{
"ecmaVersion": "es2017",
"module": true,
"checkFeatures": true,
"ignore": "globalThis,ErrorCause",
"files": ["./dist/hls.mjs", "./dist/hls.light.mjs"]
}
]
60 changes: 30 additions & 30 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,23 @@ const asyncKeywordConstraintMsg =
'The async keyword adds a `regenerator` dependency in the hls.js ES5 output not allowed in v1 due to bundle size constraints.';
const selfVsWindowGlobalMsg =
'Use `self` instead of `window` to access the global context everywhere (including workers).';
const arrayFindCompatibilityMsg =
'Usage of Array find method is restricted for compatibility.';
const arrayFindIndexCompatibilityMsg =
'Usage of Array findIndex method is restricted for compatibility.';
const arrayEveryCompatibilityMsg =
'Usage of Array every method is restricted for compatibility. Use a negative Array some check instead.';
const arrayIncludesCompatibilityMsg =
'Usage of Array includes method is restricted for compatibility. Use Array indexOf instead. (For String includes, suppress this with an eslint-disable comment.)';

const baseRestrictedSyntax = [
{
selector: 'FunctionDeclaration[async=true]',
message: asyncKeywordConstraintMsg,
},
{
selector: 'ArrowFunctionExpression[async=true]',
message: asyncKeywordConstraintMsg,
},
{
selector: 'MethodDefinition[value.async=true]',
message: asyncKeywordConstraintMsg,
},
];

module.exports = {
env: { browser: true, commonjs: true, es6: true },
Expand All @@ -31,6 +42,7 @@ module.exports = {
// see https://github.com/standard/eslint-config-standard
// 'prettier' (https://github.com/prettier/eslint-config-prettier) must be last
extends: ['eslint:recommended', 'prettier'],
ignorePatterns: ['dist/'],
parser: '@typescript-eslint/parser',
parserOptions: { sourceType: 'module', project: './tsconfig.json' },
plugins: ['@typescript-eslint', 'import', 'no-for-of-loops'],
Expand Down Expand Up @@ -74,32 +86,11 @@ module.exports = {
'no-use-before-define': 'off',
'no-restricted-syntax': [
'error',
{
selector: 'FunctionDeclaration[async=true]',
message: asyncKeywordConstraintMsg,
},
{
selector: 'ArrowFunctionExpression[async=true]',
message: asyncKeywordConstraintMsg,
},
{
selector: 'MethodDefinition[value.async=true]',
message: asyncKeywordConstraintMsg,
},
...baseRestrictedSyntax,
{
selector:
'MemberExpression[property.name="find"][object.type="Identifier"]',
message: arrayFindCompatibilityMsg,
},
{
selector:
'MemberExpression[property.name="findIndex"][object.type="Identifier"]',
message: arrayFindIndexCompatibilityMsg,
},
{
selector:
'MemberExpression[property.name="every"][object.type="Identifier"]',
message: arrayEveryCompatibilityMsg,
'MemberExpression[property.name="includes"][object.type="Identifier"]',
message: arrayIncludesCompatibilityMsg,
},
],
'import/order': [
Expand Down Expand Up @@ -137,5 +128,14 @@ module.exports = {
'@typescript-eslint/no-misused-promises': 'error',
},
},
{
files: ['tests/**/*.ts'],
rules: {
// Tests run only in modern browsers and aren't bundled, so the
// Array.includes compatibility constraint that applies to src/ doesn't
// apply here.
'no-restricted-syntax': ['error', ...baseRestrictedSyntax],
},
},
],
};
23 changes: 17 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,16 +296,27 @@ HLS.js is only compatible with browsers supporting MediaSource extensions (MSE)

HLS.js is supported on:

- Chrome 39+ for Android
- Chrome 39+ for Desktop
- Firefox 41+ for Android
- Firefox 42+ for Desktop
- Chrome 47+ for Desktop
- Firefox 51+ for Desktop
- Edge for Windows 10+
- Safari 9+ for macOS 10.11+
- Safari 10+ for macOS 10.11+
- Safari for iPadOS 13+
- Safari for iOS 17.1+ since HLS version [1.5.0](https://github.com/video-dev/hls.js/releases/tag/v1.5.0) using Managed Media Source (MMS) [WebKit blog](https://webkit.org/blog/14735/webkit-features-in-safari-17-1/)
- Chrome for Android 5+
- Firefox for Android 5+

A [Promise polyfill](https://github.com/taylorhakes/promise-polyfill) is required in browsers missing native promise support.
These versions are the targets passed to [`@babel/preset-env`](https://babeljs.io/docs/babel-preset-env) when building the UMD bundles in `dist/`. They share an **ES2016 runtime baseline**: ES5-style syntax plus native ES2016 globals (`Map`, `Set`, `Promise`, `Array.from`, `Uint8Array.from`, `Array.prototype.includes`, etc.). To keep bundle size small, no `core-js` polyfills are bundled.

Optional features such as CMCD pull in ES2017 APIs (e.g. `Object.entries`), so the full UMD bundle effectively requires an ES2017-capable runtime. The `light` bundle excludes those features and stays at the ES2016 baseline.

The `dist/` folder ships two distribution variants:

- **UMD** (`dist/hls.js`, `dist/hls.min.js`, `dist/hls.light.js`, `dist/hls.light.min.js`) — embeddable directly via a `<script>` tag (exposes a global `Hls`) or resolved by `require('hls.js')` via `package.json`'s `main` field. Targets the browser list above. The companion `dist/hls.worker.js` is the bundled transmuxer Web Worker.
- **ESM** (`dist/hls.mjs`, `dist/hls.light.mjs`) — resolved by `import 'hls.js'` via the `module` field. Built with `@babel/preset-env`'s `esmodules: true` target (≈ Chrome 61+, Firefox 60+, Safari 10.1+, Edge 16+) and intended to be consumed by a modern bundler. Uses ES2015+ syntax but stays below ES2019 (no `Array.prototype.flatMap`, `Object.fromEntries`, etc.).

If you import from `src/` directly or include any of our runtime dependencies untranspiled in your own build, you bypass this Babel pipeline and become responsible for transpilation; those source modules can reach for ES2019+ APIs that are tree-shaken out of the bundles we publish.

To run on browsers below this baseline, supply your own polyfills for any missing globals before HLS.js loads.

**Please note:**

Expand Down
11 changes: 8 additions & 3 deletions build-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,17 @@ const workerFnFooter = '})(false);';

const extensions = ['.ts', '.js'];

// Browser baseline for the legacy UMD/IIFE bundles. Chosen so that all listed
// targets natively support the ES2015 built-ins (Map, Set, Array.from,
// Uint8Array.from, Promise, etc.) that source and bundled @svta/* deps rely
// on — we intentionally do NOT bundle core-js polyfills, to keep size down.
// Keep this list in sync with the "Compatibility" section in README.md.
const babelPresetEnvTargets = {
chrome: '47',
firefox: '51',
safari: '8',
ios: '8',
android: '4',
safari: '10',
ios: '10',
android: '5',
samsung: '5',
edge: '14',
};
Expand Down
1 change: 1 addition & 0 deletions src/controller/eme-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,7 @@ class EMEController extends Logger implements ComponentAPI {
null,
new Uint16Array(licenseChallenge.buffer),
);
// eslint-disable-next-line no-restricted-syntax
if (!xmlString.includes('PlayReadyKeyMessage')) {
// This does not appear to be a wrapped message as on Edge. Some
// clients do not need this unwrapping, so we will assume this is one of
Expand Down
2 changes: 1 addition & 1 deletion src/controller/id3-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ export class ID3TrackController implements ComponentAPI {
if (id3Track && removeOldCues) {
if (id3Track.track.cues?.length) {
const idsToRemove = Object.keys(dateRangeCuesAppended).filter(
(id) => !ids.includes(id),
(id) => ids.indexOf(id) === -1,
);
for (let i = idsToRemove.length; i--; ) {
const id = idsToRemove[i];
Expand Down
2 changes: 1 addition & 1 deletion src/controller/interstitials-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1789,7 +1789,7 @@ Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timeli

if (
this.isInterstitial(playingItem) &&
removedIds.includes(playingItem.event.identifier)
removedIds.indexOf(playingItem.event.identifier) > -1
) {
this.warn(
`Interstitial "${playingItem.event.identifier}" removed while playing`,
Expand Down
2 changes: 1 addition & 1 deletion src/controller/interstitials-schedule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export class InterstitialsSchedule extends Logger {
const ids = Object.keys(dateRanges);
const removedInterstitials = previousInterstitialEvents
? previousInterstitialEvents.filter(
(event) => !ids.includes(event.identifier),
(event) => ids.indexOf(event.identifier) === -1,
)
: [];
if (interstitialEvents.length) {
Expand Down
2 changes: 1 addition & 1 deletion src/controller/subtitle-track-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ class SubtitleTrackController extends BasePlaylistController {
? this.hls.config.enableIMSC1
: this.hls.config.enableWebVTT
) {
if (!subtitleGroups || subtitleGroups.includes(track.groupId)) {
if (!subtitleGroups || subtitleGroups.indexOf(track.groupId) > -1) {
// track.id should match hls.subtitleTracks index
track.id = subtitleTracks.length;
subtitleTracks.push(track);
Expand Down
5 changes: 3 additions & 2 deletions src/remux/mp4-remuxer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export default class MP4Remuxer extends Logger implements Remuxer {
}

if (this.ISGenerated) {
if (enoughAudioSamples && enoughVideoSamples) {
if (enoughAudioSamples && enoughVideoSamples && timeOffset) {
// timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS)
// if first audio DTS is not aligned with first video DTS then we need to take that into account
// when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small
Expand Down Expand Up @@ -482,7 +482,8 @@ export default class MP4Remuxer extends Logger implements Remuxer {
(timeOffset === 0 && videoInitPTS < _initPTS.baseTime)
) {
initDTS = Math.min(initDTS as number, videoInitDTS);
initPTS = Math.min(initPTS as number, videoInitPTS);
// Bind initPTS to the lowest DTS across tracks to prevent negative timestamps
initPTS = Math.min(initPTS as number, videoInitDTS);
}
}
this.videoTrackConfig = {
Expand Down
Loading