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
| Area | Notes |
|---|---|
| Custom control-bar buttons | PiP toggle and settings gear, registered once per page as Video.js components. |
| Picture-in-Picture | A 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 menu | A hand-built stacked flyout (Quality / Playback speed), anchored bottom-right. |
| Native track menus | Audio-language, subs/caps and chapters menus are lifted out of their buttons and re-anchored to match the settings flyout. |
| Chapters | kind: '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. |
| DRM | Not supported. Encrypted sources fail with an encrypted error category; there is no EME/CDM wiring. |
Format support
| Format | Chrome | Firefox | Edge | Safari (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.