import Foundation /// Was AVPlayer auf diesem Gerät nativ abspielen kann. /// /// Diese Liste ist der Grund, warum es das Umpacken gibt: iOS spielt weder /// Matroska-Dateien noch DTS- oder TrueHD-Ton, und genau das liegt in /// typischen Beständen. Alles, was hier nicht steht, lässt die App vom Server /// umpacken — beim Abspielen wie beim Herunterladen. public struct DeviceCapabilities: Sendable, Equatable { public var containers: Set public var videoCodecs: Set public var audioCodecs: Set public init(containers: Set, videoCodecs: Set, audioCodecs: Set) { self.containers = containers self.videoCodecs = videoCodecs self.audioCodecs = audioCodecs } /// Der Stand für iOS 26 auf allen Geräten, die diese Version tragen /// (A12 aufwärts). public static let avFoundation = DeviceCapabilities( // Kein `mkv`, kein `avi`, kein `ts`. Das ist keine Auslassung, sondern // der Kern des Problems: Matroska ist der übliche Container für // Remuxe, und AVFoundation öffnet ihn nicht. containers: ["mp4", "m4v", "mov", "qt"], // AV1 fehlt bewusst: Hardware-Dekodierung gibt es erst ab A17 Pro, und // Software-Dekodierung von 4K-AV1 bringt auch neue Geräte an die // Grenze. Umrechnen ist dort das Verlässlichere. videoCodecs: ["h264", "avc1", "hevc", "h265", "hvc1", "dvh1", "dvhe"], // DTS, TrueHD und Vorbis fehlen — die drei Gründe, warum ein sonst // tadelloser MP4-Film trotzdem stumm bliebe. audioCodecs: ["aac", "mp3", "ac3", "eac3", "alac", "pcm", "lpcm"] ) /// Schachteln, die sich verlustfrei in eine MP4 umpacken lassen. /// /// Nicht jede: ein Container kann Dinge enthalten, für die MP4 keinen /// Platz hat. Diese drei sind die, die in echten Beständen vorkommen und /// deren Ströme regelmässig MP4-tauglich sind. public static let remuxableContainers: Set = ["mkv", "matroska", "ts", "m2ts", "avi"] /// Ob diese Datei so, wie sie ist, geöffnet werden kann. /// /// **Hier und nicht beim Aufrufer**, weil es die Frage inzwischen zweimal /// gibt: beim Abspielen entscheidet sie `PlaybackDecider`, beim /// Herunterladen muss sie vorher beantwortet sein — was offline liegt, /// kann hinterher niemand mehr umpacken. /// /// **Was fehlt, zählt als passend.** Nennt der Server einen Codec nicht, /// ist das keine Aussage über seine Untauglichkeit. Vorsichtshalber /// umzupacken hiesse, aus einer tadellosen MP4 eine neu gerechnete Datei /// zu machen. public func opens(container: String?, videoCodec: String?, audioCodec: String?) -> Bool { func passt(_ wert: String?, in liste: Set) -> Bool { guard let wert, !wert.isEmpty else { return true } return liste.contains(wert.lowercased()) } return passt(container, in: containers) && passt(videoCodec, in: videoCodecs) && passt(audioCodec, in: audioCodecs) } }