Skip to content
AetherEngine

AetherEngine

A media player engine for Apple platforms. FFmpeg demuxes, VideoToolbox decodes, AVPlayer handles Dolby Atmos.
Latest 7.7.1

Apps shipping AetherEngine today:

Shipping something on AetherEngine? Submit it to get listed here and in the README.

Real HDR & Dolby Vision

HDR10, HDR10+ (per-frame ST 2094-40), Dolby Vision (P5, P7 as single-layer 8.1, P8.1, P8.4, AV1 P10.x), and HLG, with full metadata and automatic Match Content.

Dolby Atmos

EAC3+JOC stream-copied on every route (HDMI MAT 2.0, AirPods spatial, Bluetooth downmix), plus 5.1 / 7.1 surround with correct channel layout.

Broad codec coverage

H.264, HEVC, and AV1 via VideoToolbox; AV1 without hardware, VP9, MPEG-2, and VC-1 via the software dav1d path. MKV, MP4, WebM, MPEG-TS, AVI, and more.

Audio-only path

A lean pipeline with no video machinery and system Now-Playing on tvOS and iOS, driven by a single LoadOptions flag.

Live TV & DVR

Unbounded live with optional timeshift, and direct HLS ingest with AES-128 clear-key and SSAI ad-pod handling.

You ship the UI

No AVPlayerViewController, no opinionated controls, no analytics. Bind the view, call play(), read the published state.

On Apple platforms the real choice is between AVPlayer, with deep OS integration but only the formats Apple ships, and a VLC- or mpv-derived engine, which plays almost anything but renders its own frames and bypasses the system’s Dolby Vision, Atmos, and HDR handling. AetherEngine is built to give you both: FFmpeg’s format breadth layered on top of VideoToolbox and AVPlayer, so Dolby Vision, Atmos, and Match Content keep working. KSPlayer is the closest analog, it reaches the same outcome through the same AVPlayer route, but it ships as a full player with its own UI, and its free build is GPL while a paid LGPL tier unlocks AV1 hardware decoding, the full demuxer and decoder set, and a list of playback features its own feature matrix enumerates; AetherEngine is an embeddable engine you drive from your own SwiftUI, with that codec and HDR breadth in the open-source core.

AetherEngineKSPlayerAVPlayerVLCKitlibmpv
ApproachEmbeddable engine, Apple-onlyFull player with bundled UI, FFmpeg + AVPlayer, Apple-onlyApple’s built-in playerlibVLC wrapped for Applelibmpv, cross-platform
Container & codec breadthWide, FFmpeg demuxWide, FFmpeg demuxNarrow, Apple’s setWideWide
Hardware decodeVideoToolbox, dav1d SW fallbackVideoToolbox, FFmpeg SW fallbackVideoToolboxVideoToolbox plus softwareVideoToolbox plus software
Dolby VisionP5, P7 as 8.1, P8.1, P8.4, AV1 P10.x, real display switchP5, P8 via AVPlayer; their matrix lists P5 HDR display without overheating as paid LGPLP5 and P8.1 onlyTone-maps, no DV displayTone-maps, no DV display
Dolby AtmosEAC3+JOC stream-copied (HDMI MAT, spatial)EAC3+JOC via AVPlayerEAC3+JOC passthroughDecodes to PCM, no object passthroughNo Atmos passthrough on Apple
HDR on tvOSNative Match Content switchNative Match Content on AVPlayer path, else Metal tone-mapNative Match ContentSoftware tone-mappingSoftware tone-mapping
Rendering & UIOS-native, you ship SwiftUIOwn Metal renderer, bundled controlsOS-native, you ship UIOwn renderer, bundled controlsOwn renderer, bundled OSC
Apple TV / App StoreYes, LGPL plus store exceptionFree build GPL, paid LGPL tier for AV1 hardware decode and the full decoder setYesYes, LGPLNot practical, GPL, no tvOS

The engine leans on the platform where the platform is best (hardware decode, Dolby Vision display, Atmos passthrough) and only falls back to its own software path (dav1d, libavcodec) for the formats VideoToolbox cannot handle.

The table above is qualitative. The numbers below are measured, on a 4K HDR HEVC file in Matroska, the container media servers actually serve. They are produced by aetherengine-bench, which is run by this project’s author, so its method, its raw data and the cases where AetherEngine does not win are all in that repository.

Measured on Apple-M1 (MacBookAir10,1), macOS 26.5.2, hevc-4k-hdr10.mkv, windowed, 3840x2160 px rendered, 60 s, median of 2. MacBookAir10,1 is fanless: sustained decode can reach thermal pressure, which is why the protocol has cooldowns between runs and discards throttled windows.

GPU powerCPU loadRSSPlays
AetherEngine63 mW5.2% of a core325 MBYes
KSPlayer149 mW6.6% of a core331 MBYes
AVPlayern/a (refuses hevc-4k-hdr10.mkv: This media format is not supported.)
VLCKit362 mW14.3% of a core102 MBYes
libmpv887 mW18.1% of a core373 MBYes

Launch failures on hevc-4k-hdr10.mkv (crashes that were retried, not refusals, see the README): KSPlayer 1.

Frame delivery and output (median; resolution, bit depth and HDR transfer are informational per engine only, shown as ‘varies across repeats’ when they disagree, see the README):

  • AetherEngine: 1440/1438 delivered/expected, dropped 0, 3840x1714, 10-bit, hdr10, rendered into 3840x2160.
  • KSPlayer: 1440/1440 delivered/expected, dropped 0, 3840x1714, 10-bit, SMPTE_ST_2084_PQ, rendered into 3840x2160. Served via KSMEPlayer.
  • VLCKit: 1442/1440 delivered/expected, dropped 0, 3840x1714, bit depth and color transfer not reported by this engine, rendered into 3840x2160.
  • libmpv: 1442/1440 delivered/expected, dropped 0, 3840x1714, 10-bit, pq, rendered into 3840x2160.

Versions: AetherEngine 6.26.0, KSPlayer 2.3.4, AVPlayer macOS 26.5.2, VLCKit 4.0.0-alpha.21, libmpv mpv v0.41.0.

Power figures are package power with an idle baseline subtracted, so they are attributable to the run and not to the machine. CPU package power was measured for every run but is not published above: the idle baseline drifts with load enough that one engine’s own repeats can disagree more than the column would be used to show between engines. For example, on hevc-4k-hdr10.mkv libmpv measured 100 to 372 mW of CPU package power across its own repeats, a 3.7x range. Recorded for every run in Results/; see “Known gaps” in the README for the full reasoning. libmpv is measured with —hwdec=auto-safe (hardware decode), not mpv’s own software-decode default, see “Fairness decisions” in the README. libmpv’s rows come from a separate run of the same protocol on the same day and the same machine. Its rows in the main run were lost to a defect in the mpv report writer (a missing import), not to anything about mpv itself. The GPU idle baseline on this machine is under 1 mW, and CPU load and RSS are not baseline-subtracted at all, so a separate baseline does not move the three published columns. Method and raw results: https://github.com/superuser404notfound/aetherengine-bench

AetherEngine is the engine, not an app. If you are here to actually watch something, Sodalite is the native Apple TV client for Jellyfin built on top of it. This site is for developers integrating the engine.

Read the introduction to get started.