yarn hls.js 1.7.0
v1.7.0

3 days ago

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 through parallel init-segment loading, and fixes across Low-Latency HLS, subtitles, interstitials, and live streaming.

Highlights to try in this release:

  • I-frame playback — call hls.createIFramePlayer() and loadMediaAt(time) to render frames from a stream's #EXT-X-I-FRAME-STREAM-INF variants in a video element, or load an #EXT-X-I-FRAMES-ONLY media playlist directly.
  • Smoother audio switching — set hls.nextAudioTrack (or pass { flushImmediate: true } to setAudioOption for an immediate one) to schedule a seamless audio-rendition change that avoids stalls and added live latency.
  • CMCD v2 — set cmcd.version: 2 to enable it, and configure cmcd.eventTargets to send standardized event reports to an external analytics endpoint. Not all CMCD v2 events, fields, and HLS.js features are supported as of this release.
  • Faster startup — alternate audio and video init segments now load in parallel; try a stream with separate audio/video renditions.
  • Improved support for looped video — a new loopBackBufferFlush config option flushes the lower-quality back buffer on looped streams.
  • Tune level selectionabrSwitchInterval throttles automatic quality switches, and errorPenaltyExpireMs lets a level penalized by a load error be re-elected instead of being avoided for the rest of the session.
  • Stop loading stale live streams — set liveMaxUnchangedPlaylistRefresh to error out with PLAYLIST_UNCHANGED_ERROR after a set number of unchanged playlist reloads instead of polling a dead playlist indefinitely.
  • Recover from broken appends — on live and VOD alike, appendTimeout bounds a SourceBuffer append that never completes, while MEDIA_SOURCE_REQUIRES_RESET and BUFFER_APPEND_NO_PROGRESS report a MediaSource that has to be reset and fragments that append without ever buffering.

Demo page

https://073606d7.hls-js-dev.pages.dev/demo/

Breaking changes

No public exports were removed and no runtime behavior changes are required to upgrade from v1.6 to v1.7. TypeScript consumers might see new compile errors where previously loose types have been narrowed. Each is listed with upgrade guidance in the migration guide:
https://github.com/video-dev/hls.js/blob/v1.7.0/MIGRATING.md#migrating-from-hlsjs-16-to-17

Some exported type dependencies ("eventemitter3", "@svta/cml-cmcd", "@svta/cml-utils", "@svta/cml-structured-field-values") have not been bundled with hls.d.ts. This will be addressed in an upcoming patch.

Changes since the last release

v1.6.18...v1.7.0, v1.7.0-rc.3...v1.7.0

Features

  • HLS I-frame playlist support
    • #EXT-X-I-FRAME-STREAM-INF and #EXT-X-I-FRAMES-ONLY (#7757, #7791) @robwalch
    • Optimize 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 tfhd default_sample_size correctly (#7884) @rudemateo
    • Support remuxing of encrypted I-frame segments, including AES decryption of byte-range requests (#7896, #7892) @robwalch
    • Force rendering of requested I-frames (#7934) @robwalch
    • Fix I-frame trick-play rendering of stretched and backward-loaded frames (#7930) @rudemateo
    • Support muxed audio+video fMP4 and multi-trun I-frame fragments (#7940) @rudemateo
    • Consolidate the I-frame remux code into one function (#7945) @robwalch
  • CMCD v2
  • Smooth audio track switching

New API, error details, and config options

Nothing below is required to upgrade. Every addition is opt-in. Each entry links to its v1.7.0 API documentation.

Config options

  • abrSwitchInterval (0) minimum seconds between automatic ABR switches
  • appendTimeout (Infinity) milliseconds before an appendBuffer() operation is treated as timed out
  • emsgKLVSchemaUri (undefined) URN matched when extracting MISB KLV metadata from CMAF emsg boxes, defaulting to urn:misb:KLV:bin:1910.1
  • errorPenaltyExpireMs (0) milliseconds after which a level penalized for a load error can be re-elected
  • handleMpegTsVideoIntegrityErrors ('process' or 'skip') how corrupt MPEG-TS video is handled
  • iframeCacheLimit (2 * 1024 * 1024) bytes of I-frame data retained by I-frame player instances
  • liveMaxUnchangedPlaylistRefresh (Infinity, minimum 2) unchanged live reloads before PLAYLIST_UNCHANGED_ERROR
  • loopBackBufferFlush (undefined) flush the lower-quality back buffer on an upswitch when media.loop is true
  • nextAudioTrackBufferFlushForwardOffset (0.25) forward audio buffer retained on a scheduled audio switch
  • skipBufferHolePadding (0.1) padding added to the buffer-hole skip target
  • loggerId (undefined) prefixes debug logs for an Hls instance (assetPlayerId still fills in for it)
  • cmcd sub-options for CMCD v2: version (1 or 2), eventTargets (v2 event report endpoints), rtpSafetyFactor (5), loader, and reporterCallback (receives a CmcdCustomReporter for updateCustomData() and recordCustomEvent()). includeKeys is retyped from string[] to CmcdKey[]
  • Controller overrides that can be replaced or disabled: streamController, gapController, latencyController, id3TrackController, and iframeController

DRM callback arguments

Methods

  • hls.createIFramePlayer(configOverride?) returns HlsIFramesOnly | null, a player that renders #EXT-X-I-FRAME-STREAM-INF variants in a video element
  • hls.createImageIFramePlayer(configOverride?) returns HlsImageIFramesOnly | null, a player for "mjpg" image I-frame variants
  • iframePlayer.loadMediaAt(time, options?) loads, appends, and seeks to the I-frame at time (options is Partial<LoadMediaAtOptions>)
  • imagePlayer.attachImage(image) and imagePlayer.detachImage() attach or detach the HTMLImageElement used for rendering
  • hls.setAudioOption(option, flushImmediate?) takes an optional second argument to switch immediately rather than on schedule

Getters and setters

Error details

This release adds three Hls.ErrorDetails members and no new Hls.Events members.

  • PLAYLIST_UNCHANGED_ERROR (NETWORK_ERROR, non-fatal) a live playlist hit liveMaxUnchangedPlaylistRefresh consecutive unchanged reloads
  • BUFFER_APPEND_NO_PROGRESS (MEDIA_ERROR, non-fatal) an append cycle completed without increasing the fragment's buffered coverage, and adds appendsWithoutProgress to ErrorData
  • MEDIA_SOURCE_REQUIRES_RESET (MEDIA_ERROR) the MediaSource closed while attached or ended on an append error, with ResetMediaSource in errorAction.flags

Event payload and behavior changes

  • iframeVariants added to MANIFEST_LOADED and MANIFEST_PARSED
  • chunkMeta: ChunkMetadata added to FRAG_PARSED and FRAG_BUFFERED
  • subtitleTrack and closedCaptions added to CUES_PARSED
  • flushImmediate added to AudioSelectionOption and AUDIO_TRACK_SWITCHING
  • previousFrag added to FRAG_CHANGED, part to SUBTITLE_FRAG_PROCESSED and FRAG_DECRYPTED, timestampOffsets to INIT_PTS_FOUND, start, end, and error to BUFFER_FLUSHED, and context to LEVEL_LOADED, AUDIO_TRACK_LOADED, and SUBTITLE_TRACK_LOADED
  • networkDetails on playlist, fragment, asset-list, and error events is narrowed from any to NullableNetworkDetails
  • FRAG_PARSING_USERDATA is only emitted when text samples are found
  • MEDIA_DETACHED is now triggered synchronously by detachMedia() and transferMedia()
  • hls.loadingEnabled is false once the stream controller has stopped, not just after stopLoad()

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 setMediaKeys once attached (#7879, #7615) @robwalch
  • Parse the Widevine key id from the EXT-X-KEY PSSH (and encrypted event initData) when HLS playlist KEYID attribute is 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, and buffering

  • Smooth audio track switching (#7575, #7795) @krseager
  • Download init segments in parallel for a faster start (#7867) @krseager
  • Add hls.audioForwardBufferInfo getter for audio-specific forward buffer info (#7865) @robwalch
  • 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)

Stability and error handling

  • Add abrSwitchInterval to throttle automatic level switches (#7669) @christriants
  • Re-elect penalized levels once errorPenaltyExpireMs has passed (#7771) @Harshit661000143
  • Error and stop streaming after max unchanged live updates (#7704) @dzianis-dashkevich
    • Keep PLAYLIST_UNCHANGED_ERROR non-fatal so playback can reach the end of the playlist (#7812) @robwalch
  • SourceBuffer append operation timeout handling (#7670) @dzianis-dashkevich
  • Detect and recover from a MediaSource that reports "ended" or "closed" while still attached, which fixes ManagedMediaSource cold starts failing on iOS 26.2 (#7697) @zalishchuk
    • Surface the condition as the MEDIA_SOURCE_REQUIRES_RESET error so applications can handle it before automatic recovery (#7699) @christriants
    • Respond with a variant switch, a switch to SDR, and a MediaSource reset error action, so content that causes append errors no longer loops through recovery (#7702) @robwalch
    • Limit recovery attempts when a media element decode error, rather than an append error, closed the MediaSource (#7707) @robwalch

Timed metadata and track management

  • Extract MISB KLV timed metadata from MPEG-TS and CMAF streams (#7668) @jacorbello, including when audio is present (#7673) @jacorbello
  • Add subtitleTrack and closedCaptions to the CUES_PARSED event (#7843) @robwalch
  • Only emit FRAG_PARSING_USERDATA when text samples are found (#7686) @XephyrKenny
  • Allow removing text tracks that HLS.js created (#7515) @xxoo
    • (Uses removable DOM <track> elements over HTMLMediaElement.addTextTrack() which does not allow removal)

Networking and resilience

Performance, memory, and lifecycle

  • Cap the selected level to the player size with ResizeObserver, and make nextLevelSwitch more 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)
  • Faster parsing on low-powered devices: CEA-608 captions, Annex-B marker scanning in the tsdemuxer, and mp4 box parsing (#7949, #7955, #7956) @itsjamie

Bug fixes

Playback start, seeking, and 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() when autoStartLoad is disabled (#7693) @robwalch
  • Roll initPTS forward when needed by earlier segments (#7537) @robwalch
  • Correct a computeInitPts rollover 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

Media parsing and 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
  • Fix unterminated emsg infinite loop and scheme parsing (#7968) @Jaybhade

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_TIMEOUT from following the errorRetry policy in playlistLoadPolicy (#7506) @CongCong-1228

Content protection (DRM)

Subtitles and captions

  • Decrypt LL-HLS VTT AES parts per-part (#7881) @hongjun-bae
  • Fix Low-Latency HLS VTT subtitle part loading, including segments where only the first parts were requested (#7626, #7893) @robwalch
  • Align WebVTT segments missing an X-TIMESTAMP-MAP header 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, and content steering

  • Fix Low-Latency Live joins and resumption around interstitial prerolls and midrolls (#7846, #7908, #7913, #7935, #7944) @robwalch
    • _HLS_start_offset missing from asset-list requests on live join (#7846, #7944)
    • Live join with a PRE,ONCE preroll starting at the wrong position (#7908)
    • Preroll resume colliding with a midroll, and midroll buffering and playback (#7913)
    • Fatal "init load aborted" when the init segment load context changes at preroll end (#7935)
  • 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-REPLACEMENT HOST to the hostname only (#7655)
  • Pathway Cloning: apply PARAMS after PER-VARIANT-URIS (#7710)
  • Fix "discontinuity sequence mismatch" for fragment hint with no parts (#7964)
  • Preserve existing query string when adding HLS delivery directives (#7969) @CarlLindqvist

Live updates and playlist parsing

  • Support an EXT-X-DISCONTINUITY-SEQUENCE declaration after the first media segment (#7750)
  • Detect stream end after a live-to-VOD transition when endListFragments is stale (#7754) @alchemyyy
  • Fix the initial reload time of a live playlist loaded with hls.loadSource() (#7875) @robwalch
  • Defer playlist reload while the network is offline (#7926) @robwalch
  • Fix mapPartIntersection loop bounds (#7859) @sugarzyc
  • 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)
  • Support EXT-X-MEDIA-SEQUENCE declaration after EXT-X-SKIP (#7905) @hongjun-bae

Buffering, memory, recovery, startup, and teardown

  • Recover from QuotaExceededError by 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)
  • Forward transmuxer worker errors correctly (#7883)
  • Make part.gap writable and handle part muxing errors as gaps (#7814)
  • Mark fragments as gaps after repeated appends without buffered range growth (#7941) @rudemateo
    • Reported with the new BUFFER_APPEND_NO_PROGRESS error detail (see Error details above)
  • Treat un-demuxable fragments as a gap to prevent infinite reloading (#7900) @hongjun-bae
  • Fix selection with small samples (#7939) @robwalch
  • Prevent loop loading for SourceBuffer mismatch append errors (#7965)
  • Guard abandon-rules check after destroy (#7938) @AmPlace

Regressions in development and v1.7 beta

  • Fix subtitle buffering up to or past media buffer (#7921) @robwalch
  • Fix fatal KEY_SYSTEM_ERROR handling (#7920)
  • Fix autoStartLoad:false and deferred startPosition (#7903)
  • Fix uncaught init-segment abort errors (#7904)
  • Fix key-status internal-error handling (#7963)
  • Fix MPEG-TS Sample-AES decryption (#7959)

Build and CI

  • Add minified ESM builds and a dist size budget check (#7970) @itsjamie
  • 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)
  • Type networkDetails on playlist, fragment, and error events instead of any (#7458) @ondreian
  • Migrate CI to Sauce Connect 5 (#7957) @itsjamie
  • Add dev container support for contributors (#7758) @tjenkinson
  • Use the GitHub release environment for npm publish (#7544) @tjenkinson
  • Lint em-dash characters (#7899) @robwalch
  • Correct interstitial env vars and content-steering condition (#7727) @zalishchuk

Documentation and README

Demo

  • Fix TypeError on playlist load/reload inside of 3rd party library Chart.js for demo page (#7801) @krseager
  • Add an hlsjsConfig query param to populate the demo editor (#7473) @quentinms

Special thanks

Beyond the code contributors credited above, thank you to the community members whose issue reports drove fixes and improvements in this release:

Feedback

Provide feedback in GitHub Issues. For more details on how to contribute to HLS.js, see our CONTRIBUTING guide.

Don't miss a new hls.js release

NewReleases is sending notifications on new releases.