Skip to main content

Architecture

@admmedia/eveplayer wraps a single Video.js 8 Player behind one class, EvePlayer. Consumers get a typed surface and nothing else (the package entry point re-exports only EvePlayer and the public types).

EvePlayer is the only thing a consumer imports or calls. It owns exactly one Video.js Player, wires VHS for adaptive streaming, adds the custom control-bar pieces, and forwards player events back out through a small typed EventEmitter.

Playback engine​

@videojs/http-streaming ("VHS") runs with overrideNative set to !(IS_ANY_SAFARI || IS_IOS): HLS and DASH are parsed and driven in JavaScript (MSE) on Chromium and Firefox, while Safari and iOS play HLS through the native engine (forcing MSE there wedges playback). DASH still runs through MSE on Safari regardless, since it has no native DASH. Quality levels come from VHS tech representations, so getQualityLevels() and the custom ABR selector are inert for HLS on Safari/iOS (native playback exposes no rendition list) and for progressive MP4/MP3.

The underlying <video> carries playsinline so iOS plays in place instead of handing off to the OS fullscreen player.

DOM nesting gotcha​

The class creates a <video class="video-js"> and appends it into the container you pass. Video.js then wraps that <video> in its own root element, which also carries the class video-js, one level below container. Video.js state classes (vjs-paused, vjs-user-active, vjs-has-started, …) live on that inner element: target container .video-js, not container itself, when reading player state from CSS or the DOM.

Subsystems​

AreaNotes
Custom control-bar buttonsPiP toggle and settings gear, registered once per page as Video.js components.
Picture-in-PictureA CSS-driven floating/draggable overlay, not the native browser PiP API, which is disabled. An in-flow .vjsp-pip-placeholder (text from pipPlaceholderText) holds the player's space while it floats.
Settings menuA hand-built stacked flyout (Quality / Playback speed), anchored bottom-right.
Native track menusAudio-language, subs/caps and chapters menus are lifted out of their buttons and re-anchored to match the settings flyout.
Chapterskind: 'chapters' cues are handled separately from subtitle text tracks on purpose. Video.js's chapters button appears when the source has chapter cues; chapters: false removes it. The same cues split the progress bar into segments and title the hover preview; chapterMarkers: false turns that off.
DRMNot supported. Encrypted sources fail with an encrypted error category; there is no EME/CDM wiring.

Format support​

FormatChromeFirefoxEdgeSafari (macOS)Safari (iOS)
MP4 (H.264)✅✅✅✅✅
MP3✅✅✅✅✅
HLS✅ *✅ *✅ *✅ (native)✅ (native)
MPEG-DASH✅ *✅ *✅ *⚠️ unreliable *⚠️ limited
CMAF / HLS✅ *✅ *✅ *✅ (native)✅ (native)
CMAF / DASH✅ *✅ *✅ *⚠️ unreliable *⚠️ limited

* Handled by @videojs/http-streaming (bundled dependency).

HLS with legacy TS segments: many HLS origins still serve MPEG-2 Transport Stream segments rather than fMP4/CMAF. The player transmuxes those TS segments to fMP4 in the browser before handing them to MSE, so TS-based HLS sources play with no extra configuration (the origin does not need to be CMAF-only).

⚠️ unreliable on Safari macOS: real DASH/CMAF sources have been observed to load and buffer normally (paused: false, healthy buffered ranges) while currentTime never advances; no error event fires, and the player's own stall-recovery does not resolve it. Reproduced across the entire published version history of the player (0.1.0 through current), which rules out a regression in the library (the cause sits in Safari's MediaSource implementation and/or @videojs/http-streaming's handling of it). Until resolved upstream, prefer an HLS source for Safari rather than relying on DASH there.