PrismCore / API reference / Engine and session

Enumeration

PrismCoreEngine

enum PrismCoreEngine

Declared 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: Int

The 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 -> Decision

Probe 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 -> SourcePrewarmOutcome

Fetch 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 -> Playback

Probe, 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 -> SoftwarePlaybackPipeline

Start 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>) -> SessionDiagnosticReport

A 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:

Swift
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 Decision

What was decided, and why — for a host's log line and for tests that want the decision without paying for the engine.

Instance properties

let engine: Engine

No documentation comment in the source.

let reason: String

Human-readable justification, e.g. "video vp9 has no native path, software decoder libvpx-vp9".

Enumeration

PrismCoreEngine.Decision.Engine

enum Engine

No documentation comment in the source.

Cases

case remux

No documentation comment in the source.

case software

No documentation comment in the source.

Enumeration

PrismCoreEngine.Playback

enum Playback

A 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 RoutingFailure

Why 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 noVideoStream

No 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

var description: String { get }

No documentation comment in the source.

Generated from the public symbols of PrismCore 3.5.4. All types