A native, cross-platform media player for the modern Apple ecosystem.
Stream from Jellyfin, play from local folders and network shares, with a unified provider abstraction, GPU-accelerated visuals, and a custom FFmpeg-backed playback engine.
v0.2.8a · iOS 27 · macOS 27 · tvOS 27 · SwiftUI + Swift Concurrency
| Sexy Library View | Continue Watching : Home Row Support |
|---|---|
![]() |
![]() |
Ocelot is a SwiftUI media player that runs natively on iPhone, iPad, Mac, and Apple TV from a single codebase. It connects to self-hosted Sphynx, Jellyfin, and Plex servers, plays back local files and SMB network shares, and renders everything through a custom playback engine (AetherEngine) that handles formats Apple's native stack won't — including MKV, Dolby Vision, HDR, and exotic subtitle/audio codecs.
The app is built around four big ideas:
- Direct-stream only — Ocelot never asks the server to transcode. It pulls the original file straight off Jellyfin and decodes it on-device, bypassing the Jellyfin/Plex playback engine entirely. No server-side re-encode, no transcode buffering, no waiting on someone else's CPU — just the raw stream and a fast local decoder.
- Symbiote — a provider-agnostic content layer so the UI never branches on "is this Jellyfin or local?"
- Dynamic tiles — self-updating media cards that live-refresh artwork, progress, and library membership in place, without churning the grid.
- A diagnostics-first player — every stage of probe → load → first-frame is instrumented, so playback behavior is observable end to end.
⚡️ Powered by AetherEngine. None of the above — the direct-stream model, the format coverage, the HDR/Dolby Vision pipeline — would exist without AetherEngine, the custom FFmpeg-backed playback engine at Ocelot's core. It's the muscle behind the whole app.
| Area | What you get |
|---|---|
| Sources | Jellyfin servers · local folders (up to 3) · SMB/CIFS network shares · single file/URL playback |
| Formats | MKV, MP4, MOV, AVI, TS, M2TS, WebM, and more · SDR / HDR / Dolby Vision · direct stream only — zero server transcoding |
| Playback | Multi-track audio & subtitle selection · skip intro/credits · resume across sessions · scrubbing · black-bar auto-crop · Now Playing & system remote commands |
| Library | Live-updating home shelves · hierarchical browsing (Series → Seasons → Episodes) · cast & metadata enrichment · unified search |
| Platforms | iOS tab bar · macOS floating pill nav + window aspect locking · tvOS focus-driven Siri Remote UI |
| Design | Liquid Glass interface · custom GPU-accelerated visuals · haptics throughout |
| Engine | FFmpeg demux/decode · HLS loopback bridging · EBU-R128 loudness normalization · live telemetry |
Detail and player views present as GPU-composited cards that slide up from the bottom and stay mounted off-screen when dismissed (warm re-open, no relayout). A scroll-aware drag gesture dismisses from the top of the content with momentum-carrying springs.
AetherEngine is the heart of Ocelot. Everything that makes the app special — direct-stream playback, MKV/HEVC/AV1 support, HDR and Dolby Vision, gapless seeking, live telemetry — comes from this custom, FFmpeg-backed playback engine. Ocelot is, in many ways, a beautiful client wrapped around AetherEngine.
Apple's native playback stack won't touch most of what a real media library contains — MKV containers, exotic audio codecs, PGS subtitles, certain HDR profiles. AetherEngine fills that gap. Because it can decode virtually any container and codec on-device, the server never needs to transcode to a format Apple understands — which is the single fact that makes Ocelot's direct-stream-only model possible.
What the engine handles, so Ocelot doesn't have to:
- Container & codec coverage — FFmpeg-based probing and demux across MKV, MP4, TS, M2TS, WebM and more; hardware decode with a software fallback path.
- Direct streaming — URL loading straight from the source (HTTP/CDN/SMB), with HLS loopback bridging for streams that need it.
- HDR & Dolby Vision — SDR / HDR / Dolby Vision detection and rendering.
- Audio bridge — resampling, mixing, and sync, plus EBU-R128 loudness normalization for live streams.
- Tracks & subtitles — multi-track audio/subtitle enumeration and selection, with text and bitmap subtitle cue delivery.
- Seeking — async, keyframe-aligned seeks with a settle path that avoids UI flicker.
- Live telemetry — bitrate, FPS, dropped frames, buffer depth, network throughput, AV-sync gap, and memory.
Ocelot interacts with all of this through a clean async surface and never reaches into decoding itself. The credit for playback quality belongs to the engine.
A single SwiftUI codebase adapts its navigation, layout, and chrome per platform:
- iOS / iPadOS — a bottom tab bar with Home / Search / Settings fixed and libraries spilling into an adaptive sidebar; detail and player cards slide up below the Dynamic Island; live updates pause in the background.
- macOS — a floating, collapsible pill column in place of tabs; full-bleed grids; ⌘-key navigation (⌘1 Home, ⌘2/⌘K Search, ⌘3+ libraries); a window manager that locks the window's aspect ratio to the video during playback and restores free resizing on browse.
- tvOS — top-level focus-driven tabs, room-distance tile sizing with generous gutters, Siri Remote tilt/parallax, native full-screen playback.
Ocelot leans on Apple's Liquid Glass for its interactive, morphing controls, backed by custom GPU-accelerated onboarding and motion, skeleton loading states that match final layout proportions to prevent pop-in, and haptics across macOS and iOS. A single SwiftUI codebase delivers a polished, platform-native feel on every device.
| Dependency | Role |
|---|---|
| AetherEngine | Custom FFmpeg-backed cross-platform playback engine (decode, HLS loopback, audio bridge, telemetry) — the heart of the app |
| FFmpeg | Demux and decode libraries underpinning the engine. Currently statically linked; production builds will move to dynamically-linked FFmpeg (embedded, code-signed frameworks, built --disable-gpl under LGPL v2.1) for App Store distribution. |
| GRDB | SQLite persistence for the Symbiote cache (WAL mode, write-LRU eviction) |
| AMSMB2 | SMB 2.x/3.x client for network-share playback |
| LibDovi | Dolby Vision metadata parsing |
Built entirely with SwiftUI and Swift Concurrency (async/await, the Observation framework) — no Combine — targeting iOS / macOS / tvOS 27.
┌─────────────────────────────────────────────────────────────┐
│ App Core │
│ (owns AetherEngine + Symbiote manager) │
└───────────────┬─────────────────────────────┬───────────────┘
│ │
┌───────▼────────┐ ┌────────▼─────────┐
│ Symbiote │ │ AetherEngine │
│ (live updates, │ │ (FFmpeg decode, │
│ 4-phase fetch)│ │ HLS, telemetry) │
└───┬────────┬────┘ └────────┬─────────┘
│ │ │
┌────────▼──┐ ┌───▼─────────┐ ┌────────▼─────────┐
│ Symbiote │ │ Providers │ │ Playback Reporter│
│ Cache │ │ (Jellyfin, │ │ (resume/report) │
│ (GRDB) │ │ Local) │ └──────────────────┘
└───────────┘ └─────────────┘
│
┌────────▼────────────────────────────────────────────┐
│ UI: dynamic tiles · card presentation · player │
│ Platform-adaptive nav (tabs / pills / focus) │
└─────────────────────────────────────────────────────┘

