Aether
Open source · package Swift

PrismCore

FFmpeg démultiplexe, Apple lit.

La pile de lecture d’Apple fait des choses qu’aucun lecteur tiers ne peut faire — décodage matériel, Dolby Atmos en passthrough, Dolby Vision, Match Content. Simplement, elle n’ouvre pas un MKV. PrismCore remballe tout conteneur que libavformat sait lire en HLS-fMP4, le sert depuis un serveur en boucle locale et donne à un simple AVPlayer une URL de playlist. Le même fichier, le moteur d’Apple.

Voir sur GitHub1.8.7LGPL-2.1+ · exception App Store

En production dans

Aether

Le moteur de lecture par défaut sur iPhone, iPad, Mac, Apple TV et Apple Vision Pro — actif à chaque installation, pas derrière un réglage.

Obtenir Aether

C’est un package Swift, pas un framework à compiler. Tout ce qui donne déjà une URL à AVPlayer peut s’en servir.

Ce qui change quand vous lancez la lecture

PrismCore est fait pour être invisible : vous ouvrez un film, il se lance. Ce qu’il vous apporte, c’est précisément tout ce qu’un lecteur qui dessine sa propre image doit abandonner.

Votre ampli reçoit la vraie bande-son

Le Dolby Atmos quitte le fichier en bitstream et arrive en bitstream à l’ampli — jamais décodé, jamais aplati en route. L’ampli affiche Atmos parce qu’on lui donne vraiment de l’Atmos, pas une approximation stéréo.

Le téléviseur passe réellement en Dolby Vision

Sur l’Apple TV, la dalle est programmée avant le début de la lecture — fréquence d’images, plage dynamique, Dolby Vision — avec la même négociation que reçoivent les apps d’Apple. À un téléviseur qui refuse un master Dolby Vision, le film est proposé une seconde fois sans cette revendication : vous obtenez une image, pas une erreur.

C’est le lecteur que vous connaissez déjà

Menu des pistes, sous-titres, Picture in Picture, AirPlay, Match Content, avance avec la télécommande : tout cela est l’interface de lecture d’Apple, parce qu’en dessous c’est bel et bien le lecteur d’Apple.

Appareil froid, batterie tranquille

L’image est décodée par la puce vidéo dédiée et non par le processeur : une MKV coûte autant qu’un fichier pris en charge d’origine. Rien n’est transcodé — ni sur votre appareil, ni sur le serveur d’où vient le fichier.

Se déplacer reste instantané

Une carte du fichier entier est publiée avant que la première image ne soit servie : un saut au milieu d’un film de trois heures atterrit là et joue, au lieu de repréparer le film depuis le début. Et en faisant glisser la barre de lecture, vous voyez l’image que vous visez, même sur un fichier dont aucun serveur n’a jamais généré les vignettes.

Rien ne quitte votre appareil

Le remballage se fait sur l’appareil et est servi au lecteur via 127.0.0.1 — une adresse de boucle locale qui n’atteint jamais le réseau. Aucune étape dans le nuage, aucun compte, et rien sur ce que vous regardez qui puisse partir ailleurs.

L’astuce, en une image

  1. MKV · MPEG-TS

    Le conteneur qu’Apple n’ouvre pas

  2. Démultiplexage

    FFmpeg lit les pistes

  3. HLS-fMP4

    Remballé sur 127.0.0.1

  4. AVPlayer

    Le lecteur d’Apple lui-même

  5. Vidéo · Audio · Sous-titres

  1. Le fichier est lu, pas converti

    Le démultiplexeur de FFmpeg ouvre le conteneur — MKV, MPEG-TS, AVI — et sépare son contenu en pistes : l’image, chaque langue audio, chaque sous-titre. Rien n’est décodé ni converti ; le fichier est seulement lu en morceaux.

  2. Les pistes sont remballées intactes

    La même image et le même son compressés sont placés dans des fragments MP4, décrits par une playlist du genre qu’AVPlayer ouvre nativement. C’est l’emballage qui change, pas le contenu — bit pour bit la même vidéo et le même audio que dans le fichier.

  3. Une adresse locale les sert

    Ces fragments sont remis à AVPlayer depuis 127.0.0.1, l’adresse qui ne quitte jamais l’appareil, comme si le film était diffusé en streaming. C’est toute l’astuce : le lecteur d’Apple diffuse volontiers ce qu’il refuse d’ouvrir comme fichier.

  4. Le moteur d’Apple fait le reste

    Décodage matériel, Atmos en passthrough, négociation Dolby Vision, menu des pistes, PiP et AirPlay — rien de tout cela n’est réécrit, car du point de vue d’AVPlayer il ne se passe rien d’inhabituel.

Deux choses ne peuvent pas être remballées et reçoivent leur propre traitement. Les sous-titres qui sont des images de texte plutôt que du texte (les PGS des remux Blu-ray) sont lus sur l’appareil par l’OCR de Vision et rejoignent les sous-titres ordinaires. Et une poignée de codecs qu’Apple ne décode pas du tout — VP9, MPEG-2, VC-1 — empruntent une seconde voie où PrismCore décode et dessine l’image lui-même : on y perd l’habillage du lecteur système, mais le film se lit.

Les questions qui reviennent

Est-ce que mes fichiers sont réencodés ?

Non. L’image et le son sont recopiés exactement tels qu’ils ont été lus — seule change la boîte dans laquelle ils se trouvent. Le fichier lui-même n’est pas modifié non plus : PrismCore ne fait que le lire.

Alors pourquoi AVPlayer n’ouvre-t-il pas une MKV tout seul ?

Apple fournit la prise en charge d’une liste fixe de conteneurs, et MKV n’y figure pas — même quand la vidéo et l’audio à l’intérieur sont des formats qu’Apple décode parfaitement. C’est un problème d’emballage, pas de décodage, et c’est précisément pourquoi remballer suffit à le régler.

Que devient l’audio TrueHD ou DTS ?

La pile d’Apple ne les accepte pas en bitstream : ils ne peuvent donc pas passer intacts. Plutôt que de les réduire en stéréo, PrismCore les convertit en EAC3 avec chaque canal à sa place — le surround reste du surround. La seule chose qui ne survit pas au trajet, c’est l’Atmos porté par du TrueHD ; l’Atmos en EAC3, celui que portent la plupart des remux, passe sans être touché.

Mon serveur Plex ou Jellyfin doit-il transcoder ?

Non, et c’est une bonne part de l’intérêt. Le serveur envoie le fichier d’origine et le remballage se fait sur votre appareil : pas de transcodage côté serveur pour faire fondre un NAS, et aucune qualité perdue au passage.

Peut-on le désactiver ?

Oui — Réglages ▸ Lecture dans Aether. Il est actif par défaut ; désactivé, la lecture retombe sur les autres moteurs exactement comme avant, pour cette installation, sans réinstallation ni réinitialisation.

La lecture démarre-t-elle plus lentement ?

Non. La source est ouverte une seule fois et seul son en-tête est lu : quelques dizaines de millisecondes sur un fichier local, environ un dixième de seconde sur le réseau — vous voyez « Préparation » à peu près aussi longtemps qu’avec un fichier qu’Apple ouvre directement.

Pour les développeurs

Tout ce qui suit concerne l’intégration du moteur dans votre propre app. Si vous êtes venu en spectateur, vous avez déjà toute l’histoire — le reste, c’est de la plomberie.

Ce qu’il prend en charge

Conteneurs et codecs

Tout ce que libavformat sait lire — MKV, MPEG-TS, AVI. HEVC, H.264 et AV1 passent en copie de flux, avec AAC, AC3, EAC3, FLAC et ALAC. TrueHD, DTS, Opus et le reste passent par le pont audio et arrivent en EAC3 au lieu d’être aplatis. Une source anamorphique garde la forme que déclare son conteneur, dans la boîte pasp qu’AVPlayer respecte.

Du Dolby Atmos, pas un downmix

L’EAC3+JOC est copié intact et jamais décodé en PCM — et la boîte dec3 que FFmpeg perd lors d’une copie de flux est réinsérée : le seul signal qui pousse AVFoundation à prendre la voie Dolby plutôt que du DD+ ordinaire. Vérifié sur un ampli en HDMI.

Dolby Vision et HDR

Le Profile 5 garde son entrée dvh1, le Profile 7 devient du 8.1 monocouche via libdovi, et sur tvOS la dalle est programmée avant même qu’AVPlayer ne voie la playlist — la négociation doit précéder, sinon la variante est rejetée d’emblée. Un master refusé par un téléviseur est réessayé sans la revendication au lieu d’échouer.

Chaque piste, et chaque sous-titre

Chaque piste audio devient une rendition alternative avec son nom, sa langue et son nombre de canaux, si bien que le menu de pistes d’AVKit fonctionne tout seul. Les sous-titres texte deviennent du WebVTT segmenté qui survit au PiP et à AirPlay ; les PGS et VOBSUB ne portent que des images, ils sont donc lus sur l’appareil par l’OCR de Vision et rejoignent les renditions ordinaires — une piste ne fait rien tant qu’un lecteur ne la demande pas vraiment, ce qui rend gratuit un remux qui porte des dizaines de pistes PGS.

Naviguer comme dans un fichier local

Un plan de segments aligné sur les images clés est publié avant le premier paquet, et un saut hors de la fenêtre produite réancre le démultiplexeur à la demande. Un cache à budget d’octets limite le disque, pas la navigation — les segments évincés sont simplement reproduits, et un segment en cours de livraison n’est jamais celui qu’on évince. La production se cale environ une minute devant le dernier segment réellement récupéré par le lecteur, si bien qu’un fichier de plusieurs gigaoctets ne court jamais après son propre cache — et la lecture de la source se stabilise au vrai débit du film plutôt qu’à la vitesse de la ligne. Un conteneur sans index propre n’en reste pas là : la première lecture séquentielle récolte chaque image clé qu’elle lit et conserve la carte, si bien que la lecture suivante planifie dessus dès la première seconde. Cette même carte répond à SeekPreviewService, qui décode l’image clé couvrant n’importe quelle position en vignette pour la barre de lecture de l’hôte.

Une voie logicielle pour le reste

Les codecs qu’AVPlayer ne décode pas du tout — VP9, MPEG-2, VC-1 — passent par libavcodec vers un AVSampleBufferDisplayLayer, sans copie partout où VideoToolbox a un décodeur matériel. L’AV1 interroge le silicium réel, pas le nom de la puce, et les sources entrelacées avérées quittent la voie native pour le désentrelaceur — sur le GPU, sur les images que le décodeur a déjà produites, quand la build FFmpeg embarque ce filtre.

Un démarrage rapide

Une lecture ouvrait sa source deux fois — une fois pour la décision d’aiguillage de l’hôte, une fois pour produire — et sur le réseau cette seconde ouverture est un aller-retour au milieu de l’attente que l’utilisateur regarde. La sonde transmet désormais son contexte ouvert à la session, chaque ouverture ne lit que ce dont a besoin un conteneur qui se décrit lui-même, et les attentes internes se vérifient toutes les 10 ms au lieu de 100. Un remux HEVC/EAC3 de 5,4 Go en HTTP passe de la sonde à la playlist en 135 ms environ au lieu de 520 — et un fichier sans index de recherche, qu’il faudrait sinon parcourir une minute, démarre quand même, sur un plan borné.

Une licence compatible avec l’App Store

LGPL-2.1-or-later avec une Application Store Exception. Une app signée ne peut laisser personne la relier à nouveau, ce qui mettait la LGPL pure et la distribution en boutique en tension ; l’exception lève cette seule exigence pour les builds de boutique, tandis que les modifications de PrismCore lui-même restent en LGPL et doivent être publiées. L’API publique est stable depuis la 1.0 — une rupture signifie une version majeure, et chaque version est consignée dans le changelog.

Ce que ça change

Sur les plateformes Apple, on choisit d’ordinaire entre un lecteur qui garde le meilleur mais n’ouvre que les conteneurs d’Apple, et un autre qui ouvre tout mais dessine ses propres images et décode l’audio en PCM — c’est précisément là que meurent l’Atmos et le Dolby Vision. PrismCore n’est pas un lecteur : il alimente AVPlayer, il n’y a donc rien à sacrifier.

PrismCoreAVPlayerVLCKitlibmpv
Lit les MKV, MPEG-TS, AVI
Dolby Atmos transmis en bitstream
Dolby Vision activé sur l’écran
Match Content sur tvOS — fréquence et plage
L’interface du lecteur d’Apple : pistes, PiP, AirPlay
VP9, MPEG-2 et VC-1

VLCKit et libmpv dessinent dans leur propre couche et vous rendent du PCM : c’est pourquoi les lignes du milieu leur échappent — une conséquence de posséder le rendu, pas un oubli. AVPlayer échoue sur la première et la dernière ligne, car il n’accepte que les conteneurs pour lesquels Apple fournit le support.

Démarrer

Ajoutez le package, puis donnez-lui une URL. Un point d’entrée sonde la source et renvoie soit une playlist pour AVPlayer, soit un pipeline logiciel déjà démarré.

.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()
}

Une fois la première lecture en place, deux options méritent le détour. Donnez un dossier de cache à la session et un conteneur sans index propre s’en constitue un pendant la lecture : la fois suivante, il se parcourt comme un fichier indexé. Ce même dossier alimente les vignettes de la barre de lecture, qui forment leur propre petit pipeline — contexte séparé, décodage sur le processeur, cache par image-clé — si bien qu’elles ne retardent jamais la lecture à laquelle elles appartiennent, quel que soit le moteur à l’écran.

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)

Les versions minimales sont iOS 16, tvOS 17, macOS 14 et visionOS 1. FFmpeg arrive via les xcframeworks dynamiques de MPVKit : un hôte qui embarque déjà MPVKit n’ajoute aucune dépendance binaire. Si votre CI résout les dépendances à votre place, épinglez une version exacte et gardez le Package.resolved commité au même niveau — sinon un build sur appareil et un build TestFlight tournent discrètement sur des moteurs différents.

Vous vouliez juste regarder quelque chose ?

C’était la moitié développeurs de la page. Si vous êtes ici pour regarder vos propres films et séries, PrismCore fait déjà son travail dans Aether — vous n’aurez jamais à y penser.

Découvrir Aether

PrismCore est développé par Václav Zmrhal · zmrhal.cz