Aether
Відкритий код · Swift-пакет

PrismCore

FFmpeg демуксує, Apple відтворює.

Стек відтворення Apple вміє те, чого не може жоден сторонній плеєр — апаратне декодування, наскрізний Dolby Atmos, Dolby Vision, Match Content. Він просто не відкриє MKV. PrismCore ремуксує будь-який контейнер, який читає libavformat, у HLS-fMP4, роздає його з loopback-сервера й передає звичайному AVPlayer URL плейлиста. Той самий файл, рушій від Apple.

Відкрити на GitHub1.8.7LGPL-2.1+ · виняток для App Store

Працює в

Aether

Типовий рушій відтворення на iPhone, iPad, Mac, Apple TV та Apple Vision Pro — увімкнений для кожної інсталяції, не за перемикачем.

Завантажити Aether

Це Swift-пакет, а не фреймворк, який треба збирати. Його візьме будь-що, що вже передає URL в AVPlayer.

Що змінюється, коли ви натискаєте «грати»

PrismCore має бути невидимим: ви відкриваєте фільм, і він грає. А отримуєте ви все те, від чого плеєр, який малює власне зображення, мусить відмовитися.

Ресивер отримує справжній звук

Dolby Atmos виходить із файла бітстрімом і бітстрімом же доходить до ресивера — його ніде не декодують і ніде не сплющують. Ресивер засвічує Atmos тому, що йому справді дали Atmos, а не його стереонаближення.

Телевізор дійсно перемикається в Dolby Vision

На Apple TV панель програмується ще до початку відтворення — частота кадрів, динамічний діапазон, Dolby Vision — тим самим рукостисканням, яке отримують застосунки Apple. Телевізору, який відхилив майстер Dolby Vision, фільм пропонується вдруге без цієї заявки, тож ви бачите зображення, а не помилку.

Це той плеєр, який ви вже знаєте

Меню доріжок, субтитри, Picture in Picture, AirPlay, Match Content, перемотка з пульта — усе це рідний інтерфейс відтворення Apple, бо під ним справді працює плеєр Apple.

Прохолодний пристрій, спокійна батарея

Зображення декодує окремий відеочип, а не процесор, тож MKV коштує стільки ж, як і файл, підтримку якого постачає Apple. Нічого не транскодується — ні на вашому пристрої, ні на сервері, звідки файл прийшов.

Перемотка лишається миттєвою

Мапа всього файла публікується ще до того, як віддано перший кадр, тож стрибок у середину тригодинного фільму просто потрапляє туди й грає, а не готує фільм заново з початку. А тягнучи смугу перемотки, ви бачите кадр, до якого прямуєте, навіть для файла, для якого жоден сервер ніколи не робив мініатюр.

Ніщо не покидає ваш пристрій

Перепакування відбувається на пристрої, а плеєру воно роздається через 127.0.0.1 — loopback-адресу, яка до мережі взагалі не доходить. Жодного кроку в хмарі, жодного акаунта й нічого про те, що ви дивитесь, що можна було б комусь надіслати.

Той трюк, на одній картинці

  1. MKV · MPEG-TS

    Контейнер, який Apple не відкриє

  2. Розклад

    FFmpeg читає доріжки

  3. HLS-fMP4

    Перепаковано на 127.0.0.1

  4. AVPlayer

    Власний плеєр Apple

  5. Відео · Аудіо · Субтитри

  1. Файл читається, а не перетворюється

    Демуксер FFmpeg відкриває контейнер — MKV, MPEG-TS, AVI — і розділяє його вміст на доріжки: зображення, кожну мову звуку, кожні субтитри. Нічого не декодується й не перетворюється; файл лише розчитується на частини.

  2. Доріжки перепаковуються недоторканими

    Те саме стиснене зображення й звук складаються у фрагментовані MP4-частини, які описує плейлист того ґатунку, що AVPlayer відкриває нативно. Змінюється упаковка, а не вміст — біт у біт те саме відео й той самий звук, що були у файлі.

  3. Роздає це локальна адреса

    Ці частини AVPlayer отримує з 127.0.0.1 — адреси, яка ніколи не покидає пристрій — так, ніби фільм стрімиться. У цьому весь трюк: плеєр Apple охоче стрімить те, що відмовляється відкрити як файл.

  4. Решту робить рушій Apple

    Апаратне декодування, наскрізний Atmos, рукостискання Dolby Vision, меню доріжок, PiP і AirPlay — нічого з цього не переписується, бо з точки зору AVPlayer не діється нічого незвичного.

Двох речей перепакувати не можна, і вони мають окреме поводження. Субтитри, які є зображенням тексту, а не текстом (PGS у блу-рей ремуксах), читає просто на пристрої OCR від Vision, і вони приєднуються як звичайні субтитри. А кілька кодеків, яких Apple не декодує взагалі — VP9, MPEG-2, VC-1 — ідуть другим шляхом, де зображення декодує й малює сам PrismCore; там ви втрачаєте системний інтерфейс плеєра, але фільм грає.

Про що питають

Воно перекодовує мої файли?

Ні. Зображення та звук копіюються точно такими, якими їх прочитали — змінюється лише коробка, у якій вони лежать. І сам файл не змінюється: PrismCore тільки читає його.

То чому AVPlayer не відкриє MKV сам?

Apple постачає підтримку фіксованого списку контейнерів, і MKV до нього не входить — навіть коли відео та звук усередині є форматами, які Apple декодує чудово. Це проблема упаковки, а не декодування, і саме тому перепакування достатньо, щоб її розв’язати.

Що буде зі звуком у TrueHD чи DTS?

Стек Apple не бере їх як бітстрім, тож недоторканими вони пройти не можуть. Замість зводити їх у стерео PrismCore перетворює їх на EAC3 з усіма каналами на місцях — surround лишається surround’ом. Єдине, що цієї дороги не переживає, — Atmos, несений у TrueHD; Atmos в EAC3, який має більшість ремуксів, проходить без змін.

Чи має мій Plex або Jellyfin транскодувати?

Ні, і в цьому значна частина сенсу. Сервер надсилає оригінальний файл, а перепакування відбувається на вашому пристрої — тож немає ні серверного транскодування, яке плавить NAS, ні втраченої на ньому якості.

Це можна вимкнути?

Так — Налаштування ▸ Відтворення в Aether. Типово увімкнено; якщо вимкнути, відтворення повертається до інших рушіїв точно як раніше, для цієї інсталяції й без жодного перевстановлення чи скидання.

Чи довше починається відтворення?

Ні. Джерело відкривається один раз, і з нього читається лише заголовок: це десятки мілісекунд для локального файла й близько десятої частки секунди через мережу — «Готуємо» ви бачите приблизно стільки ж, як для файла, який Apple відкриває напряму.

Для розробників

Усе нижче — про те, як узяти рушій у власний застосунок. Якщо ви тут як глядач, ви вже знаєте всю історію — далі йде сантехніка.

Що воно бере на себе

Контейнери та кодеки

Усе, що читає libavformat — MKV, MPEG-TS, AVI. HEVC, H.264 та AV1 проходять stream copy напряму разом з AAC, AC3, EAC3, FLAC і ALAC. TrueHD, DTS, Opus та решта йдуть через аудіоміст і прибувають як EAC3, а не сплющеними. Анаморфне джерело зберігає форму, яку заявляє його контейнер — у боксі pasp, який AVPlayer шанує.

Dolby Atmos, а не даунмікс

EAC3+JOC копіюється недоторканим і ніколи не декодується в PCM — а бокс dec3, який FFmpeg губить при stream copy, повертається на місце: єдиний сигнал, через який AVFoundation обирає шлях Dolby замість звичайного DD+. Перевірено на ресивері через HDMI.

Dolby Vision та HDR

Profile 5 зберігає свій запис dvh1, Profile 7 через libdovi перетворюється на одношаровий 8.1, а на tvOS панель програмується ще до того, як AVPlayer побачить плейлист — рукостискання має відбутися першим, інакше варіант відхиляється. Майстер, який телевізор не прийняв, повторюється без цієї заявки, а не падає.

Кожна доріжка й кожні субтитри

Кожна аудіодоріжка стає окремою alternate rendition з назвою, мовою та кількістю каналів, тож меню доріжок AVKit просто працює. Текстові субтитри стають сегментованим WebVTT, що переживає PiP і AirPlay; PGS та VOBSUB несуть лише бітмапи, тож їх читає OCR від Vision просто на пристрої, і вони приєднуються як звичайні renditions — доріжка не робить нічого, доки плеєр справді її не запросить, тому ремукс із десятками PGS-доріжок нічого не коштує.

Перемотка як у локальному файлі

Мапа сегментів, вирівняна на ключові кадри, публікується ще до першого пакета, а перемотка поза виробленим вікном на вимогу переприв’язує демуксер до потрібного ключового кадру. Кеш із байтовим бюджетом обмежує диск, а не перемотку — витіснені сегменти просто виробляються знову, а сегмент, який саме роздається, ніколи не стає жертвою витіснення. Виробництво тримається приблизно на хвилину попереду останнього сегмента, який програвач справді завантажив, тож багатогігабайтний файл ніколи не змагається з власним кешем — а читання з джерела осідає біля реального бітрейту фільму, а не швидкості каналу. Контейнер без власного індексу на цьому не застряг: перше послідовне відтворення збирає кожен ключовий кадр, який прочитало, і зберігає мапу, тож наступне планує за нею з першої секунди. Та сама мапа відповідає й SeekPreviewService, який із ключового кадру, що покриває позицію, робить мініатюру для власної смуги перемотки хоста.

Програмний шлях для решти

Кодеки, які AVPlayer не декодує взагалі — VP9, MPEG-2, VC-1 — ідуть через libavcodec у AVSampleBufferDisplayLayer, zero-copy усюди, де VideoToolbox має апаратний декодер. AV1 питає справжній кремній, а не назву чипа, а перевірено черезрядкові джерела лишають нативний шлях заради деінтерлейсера — на GPU, над кадрами, які декодер уже видав, якщо цей фільтр є у складеному FFmpeg.

Швидкий старт відтворення

Раніше відтворення відкривало джерело двічі — один раз для рішення хоста про маршрут, другий для виробництва — а через мережу цей другий відкрив є повним колом усередині очікування, на яке дивиться користувач. Тепер зонд передає сесії свій відкритий контекст, кожне відкриття читає лише те, що потрібно контейнеру, який описує себе сам, а внутрішні очікування перевіряються кожні 10 мс, а не 100. Ремукс 5,4 ГБ HEVC/EAC3 через HTTP доходить від зонда до плейлиста приблизно за 135 мс замість 520 — а файл без індексу перемотки, який інакше сканувався б хвилину, теж запускається, на обмеженому плані.

Ліцензія, яка працює в App Store

LGPL-2.1-or-later з винятком для магазинів застосунків (Application Store Exception). Підписаний застосунок не дає нікому перезлінкувати себе, тож чиста LGPL і роздача через магазин конфліктували; виняток знімає саме цю вимогу для магазинних збірок, а зміни в самому PrismCore лишаються під LGPL і мають бути опубліковані. Публічний API стабільний із 1.0 — зламна зміна означає мажорну версію, і кожен випуск записаний у changelog.

Як це виглядає поруч

На платформах Apple зазвичай доводиться обирати між плеєром, який зберігає все хороше, але відкриває лише контейнери Apple, і тим, що відкриває все, але малює власні кадри й декодує звук у PCM — саме там гинуть Atmos і Dolby Vision. PrismCore узагалі не плеєр: він годує AVPlayer, тож віддавати нічого не треба.

PrismCoreAVPlayerVLCKitlibmpv
Відтворює MKV, MPEG-TS, AVI
Dolby Atmos проходить як бітстрім
Dolby Vision вмикається на дисплеї
Match Content на tvOS — частота та діапазон
Рідний плеєр Apple: меню доріжок, PiP, AirPlay
VP9, MPEG-2 та VC-1

VLCKit і libmpv малюють у власний шар і віддають вам PCM, тому середні рядки в них не виходять — це наслідок володіння шляхом рендерингу, а не недогляд. AVPlayer не проходить перший і останній рядок, бо приймає лише контейнери, підтримку яких постачає Apple.

Як почати

Додайте пакет і передайте йому URL. Одна точка входу досліджує джерело й повертає або плейлист для AVPlayer, або програмний конвеєр, уже запущений.

.package(url: "https://github.com/Wenzlik/PrismCore.git", from: "1.8.7")
switch try await PrismCoreEngine.open(url: mkvURL) {
case .remux(let session, let playlist):
    player.replaceCurrentItem(with: AVPlayerItem(url: playlist))
    …
    await session.stop()

case .software(let pipeline):
    hostView.layer.addSublayer(pipeline.displayLayer!)
    pipeline.play()
}

Коли перше відтворення вже працює, варто знати про дві опції. Дайте сесії каталог для кешу — і контейнер, який не має власного індексу, збере його просто під час гри, тож наступне відтворення перемотується як індексований файл. Той самий каталог живить і мініатюри для смуги перемотки: це власний невеликий конвеєр — окремий контекст, декодування на CPU, кеш за ключовими кадрами — тож вони ніколи не гальмують відтворення, до якого належать, з будь-яким рушієм на екрані.

let session = try PrismCoreSession(url: mkvURL, keyframeIndexCacheDirectory: caches)

let previews = SeekPreviewService(url: mkvURL, keyframeIndexCacheDirectory: caches)
let frame: CGImage = try await previews.thumbnail(at: 42 * 60)

Мінімальні версії — iOS 16, tvOS 17, macOS 14 і visionOS 1. FFmpeg приходить через динамічні xcframework від MPVKit, тож хост, який уже везе MPVKit, не додає жодних нових бінарних залежностей. Якщо залежності за вас розв’язує CI, закріпіть точну версію й тримайте закомічений Package.resolved у злагоді з нею — інакше збірка на пристрої та збірка в TestFlight тихо працюють на різних рушіях.

Просто хочете щось подивитися?

Це була розробницька половина сторінки. Якщо ви тут, щоб дивитися власні фільми й серіали, PrismCore уже робить свою роботу всередині Aether — думати про нього не доведеться.

Подивитися Aether

PrismCore створює Václav Zmrhal · zmrhal.cz