import Foundation /// Welcher Engine die Wiedergabe übernimmt. public enum PlaybackEngineKind: String, Sendable, Equatable, Codable { /// Der Systemplayer. Bringt Bild-in-Bild, AirPlay und die Steuerung im /// Sperrbildschirm mit — deshalb hat er immer Vorrang, wenn er kann. case avPlayer /// VLCKit. Spielt praktisch jedes Format, kann dafür kein Bild-in-Bild. case vlc } /// Wie ein Titel abgespielt werden soll. public struct PlaybackDecision: Sendable, Equatable { public enum Method: Sendable, Equatable { /// Die Datei wird unverändert übertragen und lokal dekodiert. case directPlay(engine: PlaybackEngineKind) /// Der Server legt dieselben Ströme in eine andere Schachtel. /// /// **Der dritte Weg, und der wichtigste für diesen Bestand.** Ein /// Blu-ray-Rip liegt als Matroska vor, und AVFoundation öffnet /// Matroska nicht — obwohl das Bild darin H.264 oder HEVC ist und der /// Ton AAC oder AC3, also alles, was AVPlayer bestens kann. Im Weg /// steht allein die Verpackung. /// /// Umpacken heisst: dieselben Ströme, Byte für Byte, in eine MP4. Kein /// Bild wird angefasst, es geht **nichts** an Qualität verloren, und /// den Server kostet es fast nichts — anders als beim Neurechnen. /// /// Der Gewinn ist gross: solche Titel landen nicht mehr bei VLC, /// sondern bei AVPlayer. Damit gibt es dort Bild-in-Bild, AirPlay, /// die Systembedienung — und eine fortlaufende Datei, die sich /// beliebig weit vorausladen lässt. case remux /// Der Server rechnet um. Ab Phase 2; `maximumBitrate` in Kilobit. case transcode(maximumBitrate: Int?) } /// Warum es so entschieden wurde. /// /// Wandert in die technischen Infos auf der Detailseite und ins Protokoll. /// Ohne diese Begründung ist „warum ruckelt das" nicht zu beantworten. public enum Rationale: Sendable, Equatable { case nativelySupported case containerUnsupported(String) case videoCodecUnsupported(String) case audioCodecUnsupported(String) /// Der Server hat die Datei noch nicht analysiert. case streamsUnknown case bitrateExceedsLimit(mediaBitrate: Int, limit: Int) /// Transcoding wäre passender, ist aber (noch) nicht verfügbar. case transcodingUnavailable /// Im Player ausdrücklich eine Stufe gewählt. case qualityChosen } public let method: Method public let rationale: [Rationale] public init(method: Method, rationale: [Rationale]) { self.method = method self.rationale = rationale } public var engine: PlaybackEngineKind? { switch method { case .directPlay(let engine): engine // Der Transcoder liefert HLS, und damit ist AVPlayer der richtige // Engine. **Nicht** aber, wie hier früher stand, „inklusive // automatischem Bitratenwechsel": den gibt es nur, wenn die Playlist // mehrere Stufen führt, und das muss ausdrücklich bestellt werden. case .transcode: .avPlayer // Umgepackt kommt eine MP4 heraus — genau das, wofür es AVPlayer gibt. case .remux: .avPlayer } } public var isDirectPlay: Bool { if case .directPlay = method { return true } return false } }