Summary
HLS.js v1.7.0 adds I-frame playlist support, improved playback of protected content, and CMCD v2 for standardized streaming analytics. It also delivers smoother audio-track switching, faster startup via parallel init-segment loading, and broad fixes across Low-Latency HLS, subtitles, interstitials, and live streaming.
Highlights to try in this release:
- I-frame playback — call
hls.createIFramePlayer()andloadMediaAt(time)to render frames from a stream's#EXT-X-I-FRAME-STREAM-INFvariants in a video element, or load an#EXT-X-I-FRAMES-ONLYmedia playlist directly. - Smoother audio switching — set
hls.nextAudioTrack(or passflushImmediatetosetAudioOption) to schedule a seamless audio-rendition change that avoids stalls and added live latency. - CMCD v2 — set
cmcd.version: 2to enable it, and configurecmcd.eventTargetsto send standardized event reports to an external analytics endpoint. - Faster startup — alternate audio and video init segments now load in parallel; try a stream with separate audio/video renditions.
- Tune live resilience — new
errorPenaltyExpireMs,abrSwitchInterval, andappendTimeoutconfig options, plus thePLAYLIST_UNCHANGED_ERRORandMEDIA_SOURCE_REQUIRES_RESETevents. - Improved support for looped video — new
loopBackBufferFlushconfig option flushes the back-buffer of lower quality on looped streams
Changes Since The Last Release
Features
- HLS I-FRAME playlist support —
#EXT-X-I-FRAME-STREAM-INFand#EXT-X-I-FRAMES-ONLY(#7757, #7791) @robwalch- Optimized I-frame parsing (#7794)
- "mjpg" image I-frame support (#7862)
- Parse I-frame multivariant tags that end at EOF without a trailing newline (#7851) @rudemateo
- Play single-keyframe fMP4 segments and parse
tfhddefault_sample_sizecorrectly (#7884) @rudemateo
- CMCD v2 — upgrade Common Media Client Data reporting to the CMCD v2 specification (#7725) @littlespex
- Smooth audio track switching via the
hls.nextAudioTrackAPI (#7575) @krseager
New API, events & config options
hls.iframeVariants,hls.createIFramePlayer()→HlsIFramesOnly.loadMediaAt(time): render frames from a stream's#EXT-X-I-FRAME-STREAM-INFvariants in a video element (#7757, #7791) @robwalchhls.createImageIFramePlayer()→HlsImageIFramesOnly.attachImage(image): render "mjpg" image I-frame variants (#7862) @robwalchiframeCacheLimitconfig option (default 2 MB) : cap I-frame data cached for rendering
hls.nextAudioTrackgetter/setter : Smooth audio switching (#7575) @krseagernextAudioTrackBufferFlushForwardOffsetconfig option (default 0.25) : forward buffer retained when scheduling a seamless switch
- Add optional
flushImmediateargument tohls.setAudioOption(#7795) @krseager hls.audioForwardBufferInfogetter : get audio-specific forward buffer info (#7865) @robwalchPLAYLIST_UNCHANGED_ERRORevent : Max Unchanged Live Updates (#7704) @dzianis-dashkevichliveMaxUnchangedPlaylistRefreshconfig option (default Infinity) : unchanged live refreshes allowed before the error fires
MEDIA_SOURCE_REQUIRES_RESETerror event : MediaSource closed while attached or ended on append error (#7699) @christriants- Started with: Track "sourceended" event and handle ManagedMediaSource recovery (#7697) @zalishchuk
- Additional work (#7702, #7707) @robwalch
- Only emit
FRAG_PARSING_USERDATAwhen text samples are found (#7686) @XephyrKenny - Add
subtitleTrackandclosedCaptionsproperties toCUES_PARSEDevent (#7843) @robwalch cmcd.versionandcmcd.eventTargetsconfig (pluscmcd.includeKeysand a customcmcd.loader) : enable CMCD v2 and report events to an external analytics endpoint (#7725) @littlespexabrSwitchIntervalconfig option (#7669) @christriantsappendTimeoutconfig option : SourceBuffer append operation timeout handling (#7670) @dzianis-dashkevicherrorPenaltyExpireMsconfig option : Re-elect penalized level after configurable penalty expiry (#7771) @Harshit661000- Add
xhrSetupcontext arg and loader context types (#7831 @robwalch, #7762 @dzianis-dashkevich) loopBackBufferFlushconfig option : flush the back-buffer on looped streams (#7869) @dzianis-dashkevichhandleMpegTsVideoIntegrityErrorsconfig option ('process'|'skip') : how to handle corrupt MPEG-TS video (#7094) @mstyuraemsgKLVSchemaUriconfig option (defaulturn:misb:KLV:bin:1910.1) : schema URI for MISB KLVemsgmetadata (#7668) @jacorbelloskipBufferHolePaddingconfig option (default 0.1) : padding applied when skipping buffer holes/gaps (#7630) @krseager
Enhancements
Content protection (DRM)
- DRM and multi-key handling improvements for improved protected playback (#7517) @robwalch
- Resolve usable keys from the latest key session before requesting another, and resolve
setMediaKeysonce attached (#7879, #7615) @robwalch - Parse the Widevine key id from the
EXT-X-KEYPSSH (andencryptedevent initData) whenKEYIDis absent (#7775, #7415) @robwalch @yajin2021 - Support playback of elementary (containerless) audio encrypted with Sample-AES (#7479) @Yesterday17
- Load the key for the fragment of a selected LL-HLS part so encrypted parts decrypt correctly (#7874) @robwalch
Audio switching, live & buffering
- Smooth audio track switching (#7575) @krseager
- Error and stop streaming after max unchanged live updates (#7704) @dzianis-dashkevich
- Download init segments in parallel for a faster start (#7867) @krseager
- Flush the back-buffer for looped streams (#7869) @dzianis-dashkevich
- Improve audio/video append synchronization (#7842) @robwalch
- Avoid an audio buffer flush on the first track switch (#7696)
- Reach the "ended" state reliably when a playlist ends with empty or gap segments (#7609)
- Better playlist alignment when PDT is inconsistent across renditions (#7482)
Timed metadata & track management
- Extract MISB KLV timed metadata from MPEG-TS and CMAF streams (#7668) @jacorbello, including when audio is present (#7673) @jacorbello
- Allow removing TextTracks that hls.js created (#7515) @xxoo
- (Uses removable DOM
<track>elements overHTMLMediaElement.addTextTrack()which does not allow removal)
- (Uses removable DOM
Networking & resilience
- fetch-loader retry logic for transient network failures (#7756) @dzianis-dashkevich
- Verify byte-range responses and warn on a size mismatch (#7701) @robwalch
- Detect corrupt MPEG-TS video streams (#7094) @mstyura
Performance, memory & lifecycle
- Cap the selected level to the player size with
ResizeObserver, and makenextLevelSwitchmore responsive (#7108) @robwalch - Clear the resolved fragment/part URL cache when done to reduce memory use (#7731)
- More reliable transmuxer teardown on
destroy()(#7863) - Keep inherited logger methods intact on
destroy()(#7864) - Improve M2TS init-segment handling and append-queue logging (#7695)
- Remove dead code and some unused exports for a smaller bundle (#7882)
Bug fixes
Playback start, seeking & timestamps
- Fix backward seeking that led to stalling or elevated buffering (#7577) @krseager
- Fix certain Tizen devices getting stuck seeking over buffer gaps (#7630) @krseager
- Fix an endless loop in the gap controller on bad streams (zero-duration segments) (#7661) @robwalch
- Restore playback after page restoration from bfcache in Safari (#7683) @christriants
- Restart loading from
recoverMediaError()whenautoStartLoadis disabled (#7693) @robwalch - Roll initPTS forward when needed by earlier segments (#7537) @robwalch
- Correct a
computeInitPtsrollover that caused a segment-0 loop (#7752) @christriants - Adjust initPTS when a level switch drops the opening GoP (#7700) @robwalch
- Do not update initPTS on small drift caused by playlist segment-duration rounding (#7792) @robwalch
Rendering & codecs
- Work around Chrome silently evicting coded frames on PTS overlap (#7807) @robwalch
- Pad the initial PTS of remuxed MPEG-TS to avoid Chrome rendering glitches (#7805) @robwalch
- Drop bad MP4 data instead of failing the append (#7815) @robwalch
- HEVC: set sync samples from IRAP VCL NALs (#7861) @gemxx
- HEVC: correct access-unit sample boundaries in Safari (#7854) @gemxx
- Switch away from containerless EC-3 audio that hls.js cannot demux (Safari) instead of stalling (#7873) @robwalch
- Handle implicit HE-AAC for AAC Main at low sample rates (#7835) @robwalch
Audio tracks
- Fix switching back to main (audio-only) after end-of-stream is buffered (#7645) @robwalch
- Fix AES-128 decryption failure on alternate audio when a shared key load is pending (#7834) @robwalch
- Fix audio TS segment duration calculation (#7646) @robwalch
- Stop
AUDIO_TRACK_LOAD_TIMEOUTfrom following theerrorRetrypolicy inplaylistLoadPolicy(#7506) @CongCong-1228
Content protection (DRM)
- Fix PlayReady on some devices (#7631) @KunXi-Fox
- Fix PlayReady key endianness (#7510) @ShubhamSharma2311
- Fix an eme-controller type error that depended on promise state (#7877) @bwallberg
- Fix
licenseXhrSetupreturn types and aselectKeySystemnested-Promise anti-pattern (#7888) @robwalch
Subtitles & captions
- Decrypt LL-HLS VTT AES parts per-part (#7881) @hongjun-bae
- Fix Low-Latency HLS VTT subtitle part loading (#7626) @robwalch
- Align WebVTT segments missing an
X-TIMESTAMP-MAPheader with the media timeline (#7852) @robwalch - Fix WebVTT loading by disabling progressive loading for WebVTT (#7649) @krseager
- Cache initial cues added to a TextTrack so the browser does not remove them (#7809) @robwalch
- Fix CEA-608 cursor clamping so position stays within the column array bounds (#7666) @chelleccarlyle
Live, interstitials & content steering
- Fix live join following an interstitial preroll (#7846) @robwalch
- Fix interstitial live start with a short sliding window (#7799)
- Do not reset interstitials when seeking between assets (#7650)
- Fix an interstitial-controller exception on legacy engines where
media.play()returns no Promise (#7586) @malickyeu - Fix fallback to primary on live start (#7651) @robwalch
- Content Steering: apply
URI-REPLACEMENTHOSTto the hostname only (#7655) - Pathway Cloning: apply
PARAMSafterPER-VARIANT-URIS(#7710)
Live updates & playlist parsing
- Keep
PLAYLIST_UNCHANGED_ERRORnon-fatal so playback can reach the end of the playlist (#7812) @robwalch - Support an
EXT-X-DISCONTINUITY-SEQUENCEdeclaration after the first media segment (#7750) - Detect stream end after a live-to-VOD transition when
endListFragmentsis stale (#7754) @alchemyyy - Fix the initial reload time of a live playlist loaded with
hls.loadSource()(#7875) @robwalch - Do not request a video buffer flush or EOS when the main playlist is audio-only (#7868)
- Fix "Missing format identifier #EXTM3U" playlist parsing errors (#7533)
- Multivariant playlist parsing fixes (#7523)
Buffering, memory & recovery
- Recover from
QuotaExceededErrorby evicting the back-buffer and retrying (#7749) @alchemyyy - Limit buffering while paused outside the live sliding window (#7788) @robwalch
- Prevent loop-loading of segments whose appends were dropped (#7797)
- Make
part.gapwritable and handle part muxing errors as gaps (#7814) - Forward transmuxer worker errors correctly (#7883)
Build and CI
- Update minimum browser support documentation and build config (#7880) @robwalch
- Update Chrome configuration for macOS in build.yml (#7698)
- Increase minimum Windows test version for saucelabs (#7602)
- Use BrowserStack to run macOS CI tests (#7836)
- Update to TypeScript 5.9 (#7607)
- Add dev container support for contributors (#7758) @tjenkinson
- Correct interstitial env vars and content-steering condition (#7727) @zalishchuk
Documentation and README
- Update README Alternative setup for 2026 browsers (#7844) @robwalch
- Document testing with BrowserStack and SauceLabs (#7780)
- Add missing config options to API.md (#7628) @krseager
- Create SECURITY.md (#7685) @pr4v33n7-sa
- Fix links for API Docs in README (#7637) @daniemo2
- Add Labra-Flex to 'They use HLS.js in production!' section (#7419) @aaskaoui
- Fix HLS.js product name in issue templates (#7828) @karlhorky
Demo
- Fix TypeError on playlist load/reload inside of 3rd party library Chart.js for demo page (#7801) @krseager
- Add an
hlsjsConfigquery param to populate the demo editor (#7473) @quentinms
Demo Page
https://9f9035b8.hls-js-dev.pages.dev/demo/
Special thanks
Beyond the code contributors credited above, a big thank you to the community members whose issue reports drove fixes and improvements in this release:
- @gdplayerto (#4958), @iamsyk (#6374), @Hyddan (#6680), @luwes (#6777), @Tzipi-kaltura (#7524), @twilson90 (#7531), @knuthaug (#7535), @bmarshall (#7536), @AfLosada (#7541), @dchan-harmonicinc (#7554), @xtne6f (#7556), @MosheMaorKaltura (#7572), @matvp91 (#7641), @doublex (#7667), @thexeos (#7677), @gjanblaszczyk (#7687), @wei-li-dwango (#7808), @foliovision (#7833), @philhar88 (#7845), @robybales3 (#7850), and @mrlika (#7876)
- @flagoworld, whose PR #7453 informed the live media playlist reload fix (#7875)
Feedback
Please provide feedback via Issues in GitHub. For more details on how to contribute to HLS.js, see our CONTRIBUTING guide.