Ein Blu-ray-Rip liegt als Matroska vor, und AVFoundation oeffnet Matroska nicht — obwohl das Bild darin H.264 oder HEVC ist und der Ton AC3, also alles, was AVPlayer bestens kann. Im Weg stand allein die Verpackung. Dafuer gibt es jetzt einen dritten Weg neben Durchreichen und Neurechnen: umpacken. Dieselben Stroeme, Byte fuer Byte, in eine MP4. Kein Bild wird angefasst, es geht nichts an Qualitaet verloren, und den Server kostet es fast nichts. Entscheidend ist dabei `Protocol: http` statt `hls`. Eine Abspielliste laesst sich nicht vorausladen — AVPlayer holt Segment fuer Segment, und Apple laesst uns dazwischen nicht eingreifen. Eine fortlaufende Datei kommt ueber gewoehnliche Bereichsanfragen, und die kann man so weit vorausholen, wie man will. Der Vorratswunsch steht dort deshalb bei zehn Minuten statt bei einer. Der Gewinn ist gross: solche Titel bekommen Bild-in-Bild, AirPlay und die Systembedienung, die es bei VLC nicht gibt. Umgepackt wird nur, wenn **allein** die Schachtel im Weg steht. Stimmt auch nur ein Codec nicht — DTS, TrueHD, AV1 —, fehlt nicht der Zugang zur Datei, sondern das Koennen; dann bleibt es bei VLC. Sechs Tests halten die Grenze fest, in beide Richtungen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.4 KiB
Swift
82 lines
3.4 KiB
Swift
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
|
|
}
|
|
}
|