Skip to content

Design: Re-evaluate Mux Data Lifecycle and Hooks as a Player-Level Extension #2872

Description

@luwes

Status: Implemented in #2880, which moves extension ownership to the player as described below. The architecture is laid out for discussion in internal/design/player/extensions.md (draft); it is not yet a decided record.

Context

Before #2880, #1860 had moved Mux Data into markup as a child of the player:

<video-player>
  <mux-video></mux-video>
  <mux-data></mux-data>
</video-player>

The UI host (packages/html/src/extensions/media-extension-element.ts, packages/react/src/utils/use-media-extension.ts) owned the MuxDataExtension instance for the lifetime of the player and followed whichever media the player context exposed. The extension itself, however, was still written to the media-level MediaExtension contract (packages/media/src/dom/html-media-adapter/html-media-adapter.ts): it was registered per adapter with addMediaExtension(), received setAdapter() / attach() / detach() from the adapter, and monitored the adapter's HTMLVideoElement target directly (packages/extensions/mux-data/src/mux-data.ts).

That left the lifecycle split across two levels:

  • Player-level state: the MuxDataExtension instance, its options, playerInitTime, and viewSessionId (generated once per instance so views group into one session).
  • Media-level state: the mux-embed monitor on the <video> target, the loadstart listener on the adapter, and the hls.js / dash.js engine hooks.

When the player swapped media, the host removed the extension from the old adapter and re-added it to the new one, which destroyed and re-created the monitor. Whether that swap should be one Mux Data "player" emitting videochange, or a new monitor, was decided implicitly by the adapter contract rather than by the player. A plain <video> with no adapter was silently ignored.

Decisions

As implemented in #2880:

  • Mux Data observes the media the player attached, not adapter internals. MuxDataExtension implements the PlayerExtension contract from @videojs/core/dom and receives attach({ media, container }) / detach() from the player's PlayerExtensionCoordinator with whatever media the player resolved: a plain <video>, a custom media element, or an adapter. It reaches the native element through getMediaElement(media) and the engine through getMediaAdapter(media) + isEngineAdapter, public helpers in @videojs/media/dom, instead of the adapter's protected target. The loadstart listener remains, on the attached media, but only as a hint to reconcile (below).

  • Nothing is adapter-driven. setAdapter() is gone. The owner (<mux-data> / <MuxData>) creates and destroys the instance; the player attaches and detaches it alongside the store and moves it when the media changes. Registering or releasing an extension re-attaches the store to the same target, so a late-registered <mux-data> takes effect.

  • Media-swap semantics: one Mux Data session per extension instance, one monitor per native element. On attach and on every loadstart, the extension compares the media's native element, src, and engine against what the live monitor reflects:

    • same element, same src: no-op, so a remote-playback load(), engine rebuild, or MediaSource re-attach does not split the view;
    • same element, new src: videochange on the live monitor (or updateData if the monitor started before its first source);
    • same element, new engine: the hls.js / dash.js hook is swapped on the live monitor without re-monitoring;
    • different native element (the player swapped media, or a custom media / adapter swapped the element it fronts): the old monitor is destroyed and a new one starts on the new element, because mux-embed binds a monitor to an element.

    view_session_id and player_init_time are per extension instance and survive all of the above, so the views of one player group into one session either way. The decision lives in the extension's reconcile step and is driven only by the player's attach lifecycle.

  • The MediaExtension registry is removed. MediaExtension, addMediaExtension, getMediaExtensions, getMediaProp, setMediaProp, getMediaOwner, and targetOverride are gone from @videojs/media/dom; HTMLMediaAdapter forwards straight to its target. Mux Data supplies no mediaOverride, so it is a pure observer on the player-level contract.

  • SPF (Feature: SPF Mux Data Integration #1845): SPF Medias expose engine, so the extension sees it, but toMuxDataEngineOptions matches engines by shape (hls.js, dash.js) and an SPF engine has no mux-embed integration, so it is monitored from the element alone (with a __DEV__ warning). Engine-level SPF telemetry stays in Feature: SPF Mux Data Integration #1845's scope; nothing in refactor(packages)!: attach extensions at the player #2880 blocks it.

Acceptance criteria

  • Recommendation written and implemented: Mux Data's lifecycle and hooks live at the player through PlayerExtension; the MediaExtension contract is retired (refactor(packages)!: attach extensions at the player #2880, internal/design/player/extensions.md). The design record stays draft until the open questions it lists are discussed.
  • Media-element swaps have a defined outcome (above), owned by the player's attach lifecycle. Covered by packages/extensions/mux-data/src/tests/extension.test.ts ("keeps the monitor across a same-source loadstart", "emits videochange on the live monitor when the source changes", "re-monitors when the media swaps the element it fronts", "keeps one view session id across video changes", "follows the media the player attaches").
  • No separate follow-up issues: the agreed changes ship in refactor(packages)!: attach extensions at the player #2880 rather than being filed separately. A plain <video> that loads through <source> children (empty src, pick on currentSrc) is covered too; blob: URLs are never treated as a video.

Related

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions