Skip to content

feat: allow disabling the segment metadata track - #1614

Open
manNomi wants to merge 1 commit into
videojs:mainfrom
manNomi:feat/optional-segment-metadata-track
Open

manNomi wants to merge 1 commit into
videojs:mainfrom
manNomi:feat/optional-segment-metadata-track

Conversation

@manNomi

@manNomi manNomi commented Sep 30, 2026 •

Copy link
Copy Markdown

Description

Adds useSegmentMetadataTrack so applications can opt out of the internal segment-metadata text track. The default is true and retains the existing hidden track and segment metadata cues.

We encountered the Safari/iOS playback stall reported in #1600. We currently work around it in our application with an event listener for track additions. Every time a track is added we check whether its label is segment-metadata and immediately remove the corresponding <track> element from the video element. This option lets us skip creating the track instead of maintaining that removal listener.

Related work:

  • #1550 set the native segment metadata track to hidden to support cues and cuechange events.
  • #1601 proposes disabling the track for Safari/iOS HLS playback. Its discussion reports that the original reproduction works in Safari 26.4.

WebKit cause and version scope

WebKit bug 302828 was fixed by 16667e0251. In HTMLTrackElement::scheduleLoad() the old code returned early when src was absent. The fix removes that return so the empty URL reaches the failed-load path. WebKit also added a regression test for a track without src.

The WebKit commit explains why the media element can remain at HAVE_CURRENT_DATA and leave play() pending. VHS creates a native track without src and sets it to hidden in the configuration below. Our recordings show the corresponding playback stall.

The official Safari 26.4 release notes list this fix as 164125914. The compatibility target is Safari/iOS versions before 26.4 that still contain this WebKit bug. The issue is not limited to 26.3: we reproduced it on iOS 18.0 and iOS 26.3.1. Older versions without the fix may be affected when this native-track path is used. We have not established the earliest affected release or tested every earlier version. Safari 26.4 is the documented fixed release and is excluded from that target.

This is an explicit opt-out for applications supporting affected browsers. It does not automatically disable metadata for Safari users. Applications that rely on segment metadata should keep the default.

Specific Changes proposed

  • Add useSegmentMetadataTrack as an initialization and source option. A source option takes precedence.
  • Skip track creation when the option is false. Existing segment cue insertion and removal code already handles an absent track.
  • Add coverage for default behavior and option precedence with native and emulated text tracks. Verify source changes clean up the previous track and that segment appending and buffer removal work without it.
  • Document the loss of segment metadata cues when disabled. Add a checked-by-default toggle to the existing demo page.
const player = videojs('video', {
  html5: {
    vhs: {
      overrideNative: true,
      useSegmentMetadataTrack: false
    }
  }
});

Live browser reproduction

Open the live reproduction in the Safari version you want to test. No installation is required. Each link starts a fresh player:

Tap Start playback test and observe for at least 20 seconds. The page displays actual media state and lets you download telemetry as JSONL. It runs in your browser and does not emulate an affected Safari version. Network or player errors are reported separately.

Reproduction source at the verified commit. This static adaptation uses the same four pinned player assets as the original fixture below. Bundle provenance and SHA-256 checksums are included. It does not upload telemetry or remove tracks through an event listener.

The hosted pages were also checked in Safari on the iOS 26.3.1 (23D8133) Simulator. The baseline and default-control runs remained at 0 seconds after more than 20 seconds with readyState=2 and play() pending. The opt-out run advanced beyond 30 seconds with readyState=4.

Simulator verification

The recordings use Safari in Xcode's iPhone 16 Pro Simulator with Video.js 8.24.1 core and locally built VHS. The baseline is unmodified commit a9f9d7ac0264b373f14da1bb2f2e7fe8f2775c4f (VHS 3.17.5). The patched build is commit db62124a387621f88321cd4e07c30e24af4e2827 from this PR.

Both variants use the same public Apple BipBop VOD rendition and the following settings:

{
  muted: true,
  playsinline: true,
  html5: {
    nativeTextTracks: true,
    nativeAudioTracks: false,
    nativeVideoTracks: false,
    vhs: {
      overrideNative: true,
      experimentalUseMMS: true
      // Only the patched opt-out run adds useSegmentMetadataTrack: false.
    }
  }
}

Playback is requested with a button click. The page displays elapsed time and actual media state. Telemetry confirms that VHS and ManagedMediaSource are active.

Simulator runtime Unmodified VHS Patched VHS with option omitted Patched VHS with option false
iOS 18.0 (22A3351) Stalls at 0 seconds with 30 seconds buffered. readyState=2 and play() remains pending. Same stall. play() resolves and video advances beyond 20 seconds with readyState=4.
iOS 26.3.1 (23D8133) Stalls at 0 seconds with 30 seconds buffered. readyState=2 and play() remains pending. Same stall. play() resolves and video advances beyond 20 seconds with readyState=4.

In each video the unmodified baseline is on the left and the patched opt-out is on the right. These are separate Simulator captures placed side by side. Initial idle time is trimmed and resolution is reduced without changing playback speed.

iOS 18.0

ios18-before-after.mp4
iOS 18.0 comparison screenshot

Still frame from the recording above. Baseline on the left and patched opt-out on the right.

iOS 18.0: baseline play() pending at 0 seconds and patched video advancing

iOS 26.3.1

ios263-before-after.mp4
iOS 26.3.1 comparison screenshot

Still frame from the recording above. Baseline on the left and patched opt-out on the right.

iOS 26.3.1: baseline play() pending at 0 seconds and patched video advancing

Download the reproduction fixture and telemetry. Extract it and run node server.cjs. The README includes the baseline, patched and default-control URLs. The archive includes pinned bundles and SHA-256 checksums. It does not remove tracks through an event listener.

The iOS 26.3.1 runtime was verified independently with simctl. Its Safari user agent contains a frozen iPhone OS 18_7 token.

These are simulator results for the listed runtimes. They do not establish the behavior of every older Safari/iOS release.

Validation

  • npm run lint -- --errors: passed. Full npm run lint reports 0 errors and 410 warnings.
  • CI_TEST_TYPE=unit npm run build-test and CI_TEST_TYPE=unit npx karma start scripts/karma.conf.js --browsers ChromeHeadless --single-run: 1,408 passed and 7 skipped on Chrome Headless 154.
  • CI_TEST_TYPE=unit npm run build-prod: passed.

GitHub Actions currently requires maintainer approval for this external contribution. The local results above are separate from CI.

AI tools assisted with the implementation and translation of this PR description into English.

Requirements Checklist

  • Feature implemented / Bug fixed
  • If necessary, more likely in a feature request than a bug fix
    • Unit Tests updated or fixed
    • Docs/guides updated
    • Example created (existing demo toggle and attached reproduction)
  • Reviewed by Two Core Contributors

@welcome

welcome Bot commented Sep 30, 2026

Copy link
Copy Markdown

💖 Thanks for opening this pull request! 💖

Things that will help get your PR across the finish line:

  • Run npm run lint -- --errors locally to catch formatting errors earlier.
  • Include tests when adding/changing behavior.
  • Include screenshots and animated GIFs whenever possible.

We get a lot of pull requests on this repo, so please be patient and we will get back to you as soon as we can.

@manNomi
manNomi marked this pull request as ready for review September 30, 2026 07:55
@codecov

codecov Bot commented Sep 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.04%. Comparing base (a9f9d7a) to head (db62124).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1614      +/-   ##
==========================================
+ Coverage   84.00%   84.04%   +0.03%     
==========================================
  Files          44       44              
  Lines       11713    11715       +2     
  Branches     2625     2626       +1     
==========================================
+ Hits         9840     9846       +6     
+ Misses       1873     1869       -4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant