PrismCore / API reference / Engine and session
Enumeration
PrismCoreEngine
enum PrismCoreEngineDeclared in Sources/PrismCore/PrismCoreEngine.swift · PrismCore 3.5.4
The one call that answers "how do I play this?" — and hands back something already started.
PrismCore has two engines with genuinely different shapes, and phase 7 is where that stops being hideable. PrismCoreSession is a service: it remuxes into HLS-fMP4 and gives the host a playlist URL for its own AVPlayer, which keeps PiP, AirPlay, Atmos passthrough and the Dolby Vision handshake. SoftwarePlaybackPipeline is a player: for containers whose video AVPlayer cannot decode at all there is no URL to hand anywhere, so it owns the decode, the renderers and the clock, and the host gets a layer.
A host must not have to ask twice, and must not have to re-derive the decision from SourceProbe itself — so the decision lives here.
Why not inside PrismCoreSession
The roadmap's phase 7 line says "wire it into PrismCoreSession". That would be the worse design and it isn't what this does. A session is built around a work directory, a loopback server, a segmenting remuxer, demand coordination and a disk budget — the software path needs none of it. Folding the pipeline in would mean every software-decoded title allocates a temp directory and binds a TCP port it will never use, and start() would return a URL that doesn't exist for half its cases. Routing above both engines keeps each one honest about what it owns.
Type properties
static let defaultPrewarmByteBudget: IntThe default per-source prewarm budget, and its ceiling: half the coordinated reader's 4 MB retention, so the reader can take over everything that was fetched and still have room for its own fills.
static var isAudioBridgeAvailable: Bool { get }Whether this build can re-encode non-streamable audio to EAC3.
Public because it changes what PrismCore can do, and a host that ships its own FFmpeg build is the one that decides the answer: stock MPVKit has the ac3/eac3 decoders only, so the bridge is unavailable until a fork configures --enable-encoder=eac3.
Type methods
static func decide(for info: SourceInfo, availability: SoftwareDecoderAvailability = .report(), isAudioBridgeAvailable: Bool = Self.isAudioBridgeAvailable) throws -> DecisionProbe url and decide which engine takes it — without starting anything.
Exposed separately from open so a host can log or gate on the decision, and so the routing rules are testable without standing up a server or a renderer.
@discardableResult static func prewarm(url: URL, httpHeaders: [String : String] = [:], byteBudget: Int = defaultPrewarmByteBudget) async -> SourcePrewarmOutcomeFetch the bytes a play of url will read first — the container header and, for a Matroska, the Cues at its tail with the last cluster before them — so a later open over the coordinated HTTP reader (coordinatedHTTP: true) starts from memory.
For a host that knows what is likely next: the following episode, the item under the cursor, a detail page. Safe to call speculatively and to ignore: the result is advisory, and a prewarm nobody plays is dropped by the store's budget or the first memory warning.
What makes it safe to use. Before the first prewarmed byte is delivered, the reader asks the origin once more (a one-byte range) and takes the blocks only if the strong ETag and the length are what they were. An origin that reports no strong ETag — including one that reports only Last-Modified — cannot be prewarmed at all; see SourcePrewarmOutcome.Status.validatorUnavailable.
What it costs the origin. Every request goes through the same per-origin admission as playback, at lower priority: never while another request to the origin is in flight, and not at all within a minute of a refusal. A 429 / 503 is recorded, not retried.
Parameters:
- httpHeaders: the headers the play will send. Part of the identity: a prewarm under one token is not offered to a play under another.
- byteBudget: the most this prewarm may hold. Clamped to
defaultPrewarmByteBudget, which is all a reader can retain.
static func discardPrewarmedSources()Drop every prewarmed source — for a host that knows the user left the screen the prewarms were for, or that wants the memory back first.
static func open(url: URL, httpHeaders: [String : String] = [:], display: DisplayCapabilities = .conservative, segmentCacheBytes: Int? = 1 << 30, audioDelaySeconds: Double = 0, coordinatedHTTP: Bool = false) async throws -> PlaybackProbe, decide, and start the chosen engine.
The probe is a real open — a network round trip on a server source — and starting either engine blocks on I/O, so this is async and must not be called from a context that can't wait.
Parameters:
- display: what the display can present. Only the remux path uses it (it gates the HDR and Dolby Vision claims in the master playlist); the software path renders into a layer and makes no claims.
- segmentCacheBytes: remux path only — disk budget for produced segments.
static func openSoftware(probed: ProbedSource, audioDelaySeconds: Double = 0) async throws -> SoftwarePlaybackPipelineStart the software pipeline over an already-probed source — the entry a host uses when it ran SourceProbe.open + decide itself (to log or gate on the decision) and wants the probe's open to count. The pipeline adopts the context; a ProbedSource already consumed elsewhere makes it open probed.url afresh instead.
Returned loaded and paused, like open(url:)'s software case.
static func diagnosticReport(for probed: ProbedSource, decision: Result<Decision, any Error>) -> SessionDiagnosticReportA report for a source that never got a session — declined, routed to the software path, or simply not played — from its probe and the routing outcome:
let probed = try await SourceProbe.openDetached(url: url, httpHeaders: headers)
let outcome = Result { try PrismCoreEngine.decide(for: probed.info) }
let report = PrismCoreEngine.diagnosticReport(for: probed, decision: outcome)options and runtime are absent: there was no session to have them.
Structure
PrismCoreEngine.Decision
struct DecisionWhat was decided, and why — for a host's log line and for tests that want the decision without paying for the engine.
Instance properties
Enumeration
PrismCoreEngine.Decision.Engine
enum EngineNo documentation comment in the source.
Cases
Enumeration
PrismCoreEngine.Playback
enum PlaybackA started engine, ready for the host to present.
Cases
case remux(session: PrismCoreSession, playlist: URL)Remux path: point an AVPlayer at playlist and keep session alive for the playback's life, then stop() it. Treat the URL as opaque — master or media playlist is a property of the source.
case software(pipeline: SoftwarePlaybackPipeline)Software path: add pipeline.displayLayer to a view, then play(). Loaded and paused on return; stop() when done.
Enumeration
PrismCoreEngine.RoutingFailure
enum RoutingFailureWhy PrismCore declined a source. Every case means "give this to the host's other engine" — for Aether, Prism/libmpv — rather than "this file is broken".
Cases
case probeFailed(underlying: any Error)libavformat couldn't open or describe the source (an expired token, a proxy that isn't up yet, genuinely unreadable bytes).
case noVideoStreamNo video stream at all. PrismCore is a video path; an audio-only source belongs to whatever the host plays music with.
case noDecoderForVideo(codecName: String)The video can't ride the fMP4 pipeline and this FFmpeg build has no decoder for it either. Carries the codec name so a host can log something a user could act on.
case startupFailed(underlying: any Error)The engine started but never produced a playable presentation inside its startup budget.
Instance properties
Generated from the public symbols of PrismCore 3.5.4. All types