PrismCore / API reference / HLS playlists

Enumeration

MasterPlaylistBuilder

enum MasterPlaylistBuilder

Declared in Sources/PrismCore/HLS/MasterPlaylistBuilder.swift · PrismCore 3.5.4

Builds the HLS master playlist that wraps the remux's media playlist.

A bare media playlist is enough to play, but it says nothing about the stream: no CODECS, no VIDEO-RANGE, no SUPPLEMENTAL-CODECS, and — the reason the session serves a master at all today — no way to offer more than one audio track. Those attributes are the only way to ask Apple's stack for HDR and to make AVKit engage Dolby Vision, and an EXT-X-MEDIA group is the only way an AVMediaSelectionGroup of audio renditions comes into being.

Deliberately pure: values in, string out, no I/O and no libavformat. Every signaling rule is therefore pinned by a unit test instead of by a device. The rules themselves are unforgiving — a manifest that over-claims is worse than no manifest at all, because AVPlayer rejects the whole item (-11868 no compatible alternates, -11848 SDR-parked panel, -1002 when every variant was filtered at parse time) rather than degrading.

Type methods

static func build(_ variant: VariantDescription) throws -> String

No documentation comment in the source.

static func declaresDolbyVision(_ variant: VariantDescription) -> Bool

Whether build(_:) prints any Dolby Vision claim for this variant — a dvh1 primary tag (Profile 5) or a SUPPLEMENTAL-CODECS entry.

This is what the master-rejection fallback's first tier drops: a DV claim is validated against the panel's current mode, and on a panel whose true state no read can prove (Match Content off), dropping the claim — while keeping VIDEO-RANGE, the renditions and the subtitles — is the retry that still plays as HDR10 with everything selectable.

static func channelsAttribute(channelCount: Int?, isObjectAudio: Bool) -> String?

The CHANNELS value for a rendition — "16/JOC" for Dolby Atmos, the plain channel count otherwise.

16/JOC is not a channel count, it is HLS's declaration that the rendition carries joint object coding: 16 is the fixed figure Apple's authoring rules require, whatever the underlying bed is (an Atmos EAC3 track's ch_layout reads 6). Without it the objects still reach an AVR — the stream is copied untouched, and the receiver is what decodes JOC — but AVFoundation has no way to know the rendition is Atmos, so it can't prefer it in a selection group and tvOS won't badge it.

  • Parameter isObjectAudio: must be true only for a stream-copied EAC3+JOC track. Declaring JOC over the audio bridge's output would be a lie: the bridge decodes to PCM and re-encodes, so the objects are gone.
static func audioCodecString(forCodecName name: String, profileName: String? = nil) -> String?

RFC 6381 tags for the audio codecs the v0 remuxer stream-copies. nil for anything the phase-3 bridge would have to touch — by then the caller declares the bridged codec (ec-3), not the source's.

Structure

MasterPlaylistBuilder.AudioRendition

struct AudioRendition

One selectable audio rendition. The remuxer emits one per viable audio track of the source, each backed by its own media playlist.

Initializers

init(groupID: String = "aud", name: String, language: String? = nil, codecString: String? = nil, channels: String? = nil, uri: String? = nil, isDefault: Bool = true, characteristics: [String] = [])

No documentation comment in the source.

Instance properties

var groupID: String

No documentation comment in the source.

var name: String

No documentation comment in the source.

var language: String?

BCP-47 / ISO-639 tag as it came out of the container metadata.

var codecString: String?

RFC 6381 tag: mp4a.40.2, ac-3, ec-3, fLaC, alac.

var channels: String?

HLS CHANNELS value. Plain channel count for most codecs; EAC3 with object audio uses the "16/JOC" form, which is how Atmos is advertised — hence a String, not an Int.

var uri: String?

nil means the audio is muxed into the variant's own segments (v0's shape: one fMP4 with both tracks). HLS reads a URI-less EXT-X-MEDIA exactly that way, so the rendition still shows up as a selectable AVMediaSelectionOption.

var isDefault: Bool

DEFAULT=YES. Exactly one rendition of a group may claim it, and the caller decides which — the builder prints what it is told.

var characteristics: [String]

UTI media characteristics (CHARACTERISTICS), e.g. public.accessibility.enhances-speech-intelligibility on a dialogue-boost rendition. Empty prints nothing. This is how a host finds such renditions programmatically — AVMediaSelectionOption.hasMediaCharacteristic(_:) — instead of matching display names.

Structure

MasterPlaylistBuilder.ClosedCaptionRendition

struct ClosedCaptionRendition

One CEA-608 service carried in-band in the video's SEI, declared so AVPlayer exposes it natively (TYPE=CLOSED-CAPTIONS).

Opt-in only: the engine already serves each service it finds as a WebVTT SubtitleRendition, and a variant that declares nothing gets CLOSED-CAPTIONS=NONE — see streamInfLine.

Initializers

init(groupID: String = "cc", name: String, language: String? = nil, channel: Int)

No documentation comment in the source.

Instance properties

var groupID: String

No documentation comment in the source.

var name: String

No documentation comment in the source.

var language: String?

No documentation comment in the source.

var channel: Int

1…4, printed as INSTREAM-ID="CC1"…"CC4".

Structure

MasterPlaylistBuilder.Resolution

struct Resolution

No documentation comment in the source.

Initializers

init(width: Int, height: Int)

No documentation comment in the source.

Instance properties

let width: Int

No documentation comment in the source.

let height: Int

No documentation comment in the source.

Enumeration

MasterPlaylistBuilder.SignalingError

enum SignalingError

No documentation comment in the source.

Cases

case frameRateRequiredForHDR(DynamicRange)

AVPlayer filters a VIDEO-RANGE=PQ/HLG variant that carries no FRAME-RATE out of the master while parsing it, then fails the item with -1002 without ever fetching the media playlist. A source whose frame rate the demuxer couldn't determine therefore must not be served a master at all — it plays media-direct.

case dolbyVisionProfile5RequiresCapableDisplay

Profile 5 has no base layer: dvh1 is the only correct declaration (without it the IPT-PQ chroma is read as YCbCr and the picture goes green/purple), and a dvh1 variant offered to a non-DV display is rejected with -11868. So P5 + non-DV display has no valid master; it plays media-direct.

Structure

MasterPlaylistBuilder.SubtitleRendition

struct SubtitleRendition

One WebVTT subtitle rendition (phase 6).

DEFAULT=NO,AUTOSELECT=NO unless the caller says otherwise, and that default is load-bearing: with either set to YES AVKit engages the rendition by itself, which double-draws against a host overlay and turns subtitles on for users who never asked. The host normally selects the rendition programmatically through the legible AVMediaSelectionGroup instead.

isDefault exists for the one case where "the user never asked" is false: a session built with a preferredSubtitleLanguage, which is the host relaying an explicit request. Nothing else may set it.

Initializers

init(groupID: String = "subs", name: String, language: String? = nil, uri: String, isForced: Bool = false, isDefault: Bool = false)

No documentation comment in the source.

Instance properties

var groupID: String

No documentation comment in the source.

var name: String

No documentation comment in the source.

var language: String?

BCP-47 / ISO-639 tag from the container metadata (or the caller's, for an external file).

var uri: String

Relative URI of the rendition's media playlist. Required — a subtitle rendition is never muxed into the variant's segments.

var isForced: Bool

Container FORCED disposition: subtitles for foreign dialogue only. Emitted as FORCED=YES so the host can tell them apart in the selection group.

var isDefault: Bool

DEFAULT=YES,AUTOSELECT=YES — see the type doc. At most one rendition of a group may claim it.

Structure

MasterPlaylistBuilder.VariantDescription

struct VariantDescription

No documentation comment in the source.

Initializers

init(mediaPlaylistURI: String = "index.m3u8", bandwidth: Int, averageBandwidth: Int? = nil, resolution: Resolution? = nil, frameRate: Double? = nil, dynamicRange: DynamicRange = .sdr, videoCodec: VideoCodec, dolbyVision: DolbyVisionConfiguration? = nil, displayIsDolbyVisionCapable: Bool = false, audioRenditions: [AudioRendition] = [], subtitles: [SubtitleRendition] = [], closedCaptions: [ClosedCaptionRendition] = [])

No documentation comment in the source.

Instance properties

var mediaPlaylistURI: String

Relative URI of the media playlist the remuxer writes.

var bandwidth: Int

Peak bitrate estimate, in bits/s. Caller-provided: the remuxer knows the source's bit rate, the builder does not measure.

var averageBandwidth: Int?

No documentation comment in the source.

var resolution: Resolution?

No documentation comment in the source.

var frameRate: Double?

Required for VIDEO-RANGE=PQ / HLG (see build).

var dynamicRange: DynamicRange

No documentation comment in the source.

var videoCodec: VideoCodec

No documentation comment in the source.

var dolbyVision: DolbyVisionConfiguration?

The source's DV configuration, when it has one.

var displayIsDolbyVisionCapable: Bool

Whether the display this session is going to can present Dolby Vision. Not a source property and not cacheable: the same file gets a different manifest on a DV panel and on an HDR10 one, and claiming DV to a non-DV display fails the item outright.

var audioRenditions: [AudioRendition]

Every selectable audio rendition, in the order they are offered. Empty for a silent source (no EXT-X-MEDIA, no AUDIO attribute).

var subtitles: [SubtitleRendition]

WebVTT subtitle renditions, in declaration order. Empty means the manifest says nothing about subtitles — the pre-phase-6 shape.

var closedCaptions: [ClosedCaptionRendition]

In-band caption services to declare. Empty — the default — prints CLOSED-CAPTIONS=NONE, which hides them from AVPlayer.

Enumeration

MasterPlaylistBuilder.VideoCodec

enum VideoCodec

How the variant's video codec is declared.

Cases

case hevc(HEVCConfigurationRecord)

HEVC, declared from the source's own hvcC profile_tier_level.

case avc(AVCConfigurationRecord)

H.264, declared from the source's own avcC three PTL bytes.

case av1(AV1ConfigurationRecord)

AV1, declared from the source's own av1C.

case explicit(String)

Anything else, where the caller already has the RFC 6381 string.

Generated from the public symbols of PrismCore 3.5.4. All types