README.md
HLS.js is a JavaScript library that implements an HTTP Live Streaming client. It relies on HTML5 video and MediaSource Extensions for playback.
It works by transmuxing MPEG-2 Transport Stream and AAC/MP3 streams into ISO BMFF (MP4) fragments. Transmuxing is performed asynchronously using a Web Worker when available in the browser. HLS.js also supports HLS + fmp4, as announced during WWDC2016.
HLS.js works directly on top of a standard HTML<video> element.
HLS.js is written in ECMAScript6 (*.js) and TypeScript (*.ts) (strongly typed superset of ES6), and transpiled in ECMAScript5 using Babel and the TypeScript compiler.
Rollup is used to build the distro bundle and serve the local development environment.
SUPPLEMENTAL-CODECS attribute for codec selectionenableEmsgKLVMetadata)mjpg) renditionsFor details on the HLS format and these tags' meanings, see https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis
#EXT-X-STREAM-INF:<attribute-list>
<URI>#EXT-X-I-FRAME-STREAM-INF I-frame Media Playlist files#EXT-X-MEDIA:<attribute-list>#EXT-X-SESSION-DATA:<attribute-list>#EXT-X-SESSION-KEY:<attribute-list> EME Key-System selection and preloading#EXT-X-START:TIME-OFFSET=<n>#EXT-X-CONTENT-STEERING:<attribute-list> Content Steering#EXT-X-DEFINE:<attribute-list> Variable Substitution (NAME,VALUE,QUERYPARAM attributes)#EXTM3U (required format identifier)#EXT-X-VERSION:<n> (value is ignored)#EXT-X-INDEPENDENT-SEGMENTS (ignored)#EXT-X-I-FRAMES-ONLY#EXTINF:<duration>,[<title>]#EXT-X-ENDLIST#EXT-X-PLAYLIST-TYPE:<type-enum> (see "Not Supported" below)#EXT-X-MEDIA-SEQUENCE:<n>#EXT-X-TARGETDURATION:<n>#EXT-X-DISCONTINUITY#EXT-X-DISCONTINUITY-SEQUENCE:<n>#EXT-X-BITRATE:<rate>#EXT-X-BYTERANGE:<n>[@<o>]#EXT-X-MAP:<attribute-list>#EXT-X-KEY:<attribute-list> (KEYFORMAT="identity",METHOD=SAMPLE-AES is only supported with MPEG-2 TS segments)#EXT-X-PROGRAM-DATE-TIME:<date-time-msec>#EXT-X-START:TIME-OFFSET=<n>#EXT-X-SERVER-CONTROL:<attribute-list>#EXT-X-PART-INF:PART-TARGET=<n>#EXT-X-PART:<attribute-list>#EXT-X-SKIP:<attribute-list> Delta Playlists#EXT-X-RENDITION-REPORT:<attribute-list>#EXT-X-DATERANGE:<attribute-list> Metadata
#EXT-X-DEFINE:<attribute-list> Variable Import and Substitution (NAME,VALUE,IMPORT,QUERYPARAM attributes)#EXT-X-GAP (Skips loading GAP segments and parts. Skips playback of unbuffered program containing only GAP content and no suitable alternates. See #2940)Parsed but missing feature support:
#EXT-X-PRELOAD-HINT:<attribute-list> (See #5074)For a complete list of issues, see "Top priorities" in the Release Planning and Backlog project tab. Codec support is dependent on the runtime environment (for example, not all browsers on the same OS support HEVC).
REQ-VIDEO-LAYOUT is not used in variant filtering or selectionSAMPLE-AES method keys with fmp4, aac, mp3, vtt... segments (MPEG-2 TS only)org.w3.clearkey) is incomplete: the key system is recognized, but there is no way to supply key ID/key value pairs to the EME controller, so no license or session path exists (See #2934)light build (see __USE_M2TS_ADVANCED_CODECS__)require from a Node.js runtimeYou can safely require this library in Node and absolutely nothing will happen. A dummy object is exported so that requiring the library does not throw an error. HLS.js is not instantiable in Node.js. See #1841 for more details.
First, checkout the repository and install the required dependencies
git clone https://github.com/video-dev/hls.js.git
cd hls.js
# After cloning or pulling from the repository, make sure all dependencies are up-to-date
npm install ci
# Run dev-server for demo page (recompiles on file-watch, but doesn't write to actual dist fs artifacts)
npm run dev
# After making changes run the sanity-check task to verify all checks before committing changes
npm run sanity-check
The dev server will host files on port 8000. Once started, the demo can be found running at http://localhost:8000/demo/.
Before submitting a PR, please see our contribution guidelines. Join the discussion on Slack via video-dev.org in #hlsjs for updates and questions about development.
Build all flavors (suitable for prod-mode/CI):
npm install ci
npm run build
Only debug-mode artifacts:
npm run build:debug
Build and watch (customized dev setups where you'll want to host through another server - for example in a sub-module/project)
npm run build:watch
Only specific flavors (known configs are: full, fullMin, fullEsm, fullEsmMin, light, lightMin, lightEsm, lightEsmMin, worker, demo):
npm run build -- --configType fullMin # repeat --configType to build more than one
Report the size of the built dist/ files, and check them against the budgets in
dist-size-budget.json (the same check CI runs):
npm run size
npm run size:check
NOTE: hls.light.*.js dist files do not include alternate-audio, subtitles, CMCD, EME (DRM), Variable Substitution, Interstitials, I-frame trick-play, Media Capabilities, or MPEG-2 TS advanced codec (HEVC and AC-3) support. Content Steering is included. In addition, the following types are not available in the light build:
AudioStreamControllerAudioTrackControllerCuesInterfaceEMEControllerSubtitleStreamControllerSubtitleTrackControllerTimelineControllerCMCDControllerInterstitialsControllerInterstitialsManagerIFrameControllerHlsIFramesOnlyHlsImageIFramesOnlyRun linter:
npm run lint
Run linter with auto-fix mode:
npm run lint:fix
Run linter with errors only (no warnings)
npm run lint:quiet
Run prettier to format code
npm run prettier
Run type-check to verify TypeScript types
npm run type-check
Run all tests at once:
npm test
Run unit tests:
npm run test:unit
Run unit tests in watch mode:
npm run test:unit:watch
Run functional (integration) tests:
npm run test:func
An overview of this project's design, it's modules, events, and error handling can be found here.
Note you can access the docs for a particular version using "https://github.com/video-dev/hls.js/tree/deployments"
https://hlsjs.video-dev.org/demo
https://hlsjs-dev.video-dev.org/demo
Find the commit on https://github.com/video-dev/hls.js/tree/deployments.
This project is tested with BrowserStack. This project is tested with SauceLabs.
HLS.js is only compatible with browsers supporting MediaSource extensions (MSE) API with 'video/MP4' mime-type inputs.
HLS.js is supported on:
These versions are the targets passed to @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:
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.dist/hls.mjs, dist/hls.light.mjs, plus the minified dist/hls.min.mjs and dist/hls.light.min.mjs) — import 'hls.js' resolves to the unminified dist/hls.mjs via the module field, which is what you want when a bundler will minify it for you. The .min.mjs files exist for loading straight from a CDN with <script type="module">. 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.).The ESM builds do not bundle the transmuxer Web Worker. The UMD builds inline it, but
dist/hls.mjsanddist/hls.min.mjsdo not, so transmuxing runs on the main thread unless you pointworkerPathat the separately published worker:jsconst hls = new Hls({ workerPath: 'https://cdn.jsdelivr.net/npm/hls.js@1/dist/hls.worker.js', });
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:
Safari browsers (iOS, iPadOS, and macOS) have built-in HLS support through the plain video "tag" source URL. See the example below (Using HLS.js) to run appropriate feature detection and choose between using HLS.js or natively built-in HLS support.
When a platform has neither MediaSource nor native HLS support, the browser cannot play HLS.
Keep in mind that if the intention is to support HLS on multiple platforms, beyond those compatible with HLS.js, the HLS streams need to strictly follow the specifications of RFC8216, especially if apps, smart TVs, and set-top boxes are to be supported.
Find a support matrix of the MediaSource API here: https://developer.mozilla.org/en-US/docs/Web/API/MediaSource
Prepackaged builds are included with each release. Or install the hls.js as a dependency of your project:
npm install --save hls.js
A canary channel is also available if you prefer to work off the development branch (master):
npm install hls.js@canary
Directly include dist/hls.js or dist/hls.min.js in a script tag on the page. This setup prioritizes HLS.js MSE playback over native browser support for HLS playback in HTMLMediaElements:
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<!-- Or if you want the latest version from the main branch -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
<video id="video"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
// HLS.js is not supported on platforms that do not have Media Source
// Extensions (MSE) enabled.
//
// When the browser has built-in HLS support (check using `canPlayType`),
// we can provide an HLS manifest (i.e. .m3u8 URL) directly to the video
// element through the `src` property. This is using the built-in support
// of the plain video element, without using HLS.js.
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
}
</script>
To check for native browser support first and then fallback to HLS.js, swap these conditionals.
Note:
video.canPlayType('application/vnd.apple.mpegurl')returns a non-empty string ("maybe") in Safari, Chrome, and potentially other browsers. However, not all browsers support HLS content equally — for example, Chrome 147 reports support but may fail to play certain streams natively. UsingHls.isSupported()first (the default setup above) is recommended unless you specifically need native playback.
<script src="https://cdn.jsdelivr.net/npm/hls.js@1"></script>
<!-- Or if you want the latest version from the main branch -->
<!-- <script src="https://cdn.jsdelivr.net/npm/hls.js@canary"></script> -->
<video id="video"></video>
<script>
var video = document.getElementById('video');
var videoSrc = 'https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8';
//
// Only use native HLS in browsers with ManagedMediaSource (e.g. modern Safari)
// where native playback is well-supported. Other browsers may report HLS support
// via canPlayType but fail to play certain streams reliably.
//
if (
video.canPlayType('application/vnd.apple.mpegurl') &&
'ManagedMediaSource' in window
) {
video.src = videoSrc;
//
// If not using native HLS, check if HLS.js is supported
//
} else if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
</script>
HLS transcoding of an original video file often pushes the time of the first frame a bit. If you depend on having an exact match of frame times between original video and HLS stream, you need to account for this:
let tOffset = 0;
const getAppendedOffset = (eventName, { frag }) => {
if (frag.type === 'main' && frag.sn !== 'initSegment' && frag.elementaryStreams.video) {
const { start, startDTS, startPTS, maxStartPTS, elementaryStreams } = frag;
tOffset = elementaryStreams.video.startPTS - start;
hls.off(Hls.Events.BUFFER_APPENDED, getAppendedOffset);
console.log('video timestamp offset:', tOffset, { start, startDTS, startPTS, maxStartPTS, elementaryStreams });
}
}
hls.on(Hls.Events.BUFFER_APPENDED, getAppendedOffset);
// and account for this offset, for example like this:
const video = document.querySelector('video');
video.addEventListener('timeupdate', () => setTime(Math.max(0, video.currentTime - tOffset))
const seek = (t) => video.currentTime = t + tOffset;
const getDuration = () => video.duration - tOffset;
For more embed and API examples see docs/API.md.
All HLS resources must be delivered with CORS headers permitting GET requests.
Video is controlled through HTML <video> element HTMLVideoElement methods, events and optional UI controls (<video controls>).
The following players integrate HLS.js for HLS playback:
| cdn77 | |||
made by gramk, plays hls from address bar and m3u8 links
HLS.js is released under Apache 2.0 License