Matroska geht jetzt an AVPlayer, nicht mehr an VLC

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>
This commit is contained in:
Scarriffle
2026-09-28 20:00:44 +02:00
co-authored by Claude Opus 5
parent 806eb381c7
commit 62691fc2bc
10 changed files with 222 additions and 7 deletions
@@ -215,3 +215,101 @@ struct PlaybackDeciderTests {
#expect(decision.method == .directPlay(engine: .avPlayer))
}
}
@Suite("Umpacken statt neu rechnen")
struct RemuxDecisionTests {
private let decider = PlaybackDecider()
private var policy: PlaybackPolicy {
PlaybackPolicy(allowsTranscoding: true, allowsFallbackEngine: true)
}
/// Der Blu-ray-Rip: Matroska aussen, lauter Dinge innen, die AVPlayer
/// bestens kann. Im Weg steht allein die Verpackung — und dafür gibt es
/// das Umpacken, nicht VLC und schon gar nicht den Transcoder.
@Test("Matroska mit brauchbarem Inhalt wird umgepackt")
func matroskaIsRemuxed() {
let entscheidung = decider.decide(
profile: MediaProfile(
container: "mkv", videoCodec: "hevc", audioCodec: "ac3",
bitrate: 20_000, hasKnownStreams: true
),
policy: policy
)
#expect(entscheidung.method == .remux)
// Und damit AVPlayer: Bild-in-Bild, AirPlay, weites Vorausladen.
#expect(entscheidung.engine == .avPlayer)
}
/// Der Gegenbeweis. Stimmt der Ton nicht, fehlt nicht der Zugang zur
/// Datei, sondern das Können — dann hilft eine andere Schachtel nichts.
@Test("Bei DTS hilft Umpacken nicht")
func dtsStillGoesToVLC() {
let entscheidung = decider.decide(
profile: MediaProfile(
container: "mkv", videoCodec: "hevc", audioCodec: "dts",
bitrate: 20_000, hasKnownStreams: true
),
policy: policy
)
#expect(entscheidung.method == .directPlay(engine: .vlc))
}
@Test("Bei AV1 ebenso wenig")
func av1StillGoesToVLC() {
let entscheidung = decider.decide(
profile: MediaProfile(
container: "mkv", videoCodec: "av1", audioCodec: "aac",
bitrate: 20_000, hasKnownStreams: true
),
policy: policy
)
#expect(entscheidung.method == .directPlay(engine: .vlc))
}
/// Eine MP4, die AVPlayer ohnehin öffnet, wird nicht angefasst. Umpacken
/// wäre dort Arbeit ohne Wirkung.
@Test("Was schon passt, bleibt unverändert")
func mp4IsUntouched() {
let entscheidung = decider.decide(
profile: MediaProfile(
container: "mp4", videoCodec: "h264", audioCodec: "aac",
bitrate: 8_000, hasKnownStreams: true
),
policy: policy
)
#expect(entscheidung.method == .directPlay(engine: .avPlayer))
}
/// Reicht die Leitung nicht, nützt Umpacken nichts: die Datei bliebe
/// gleich gross. Dann muss wirklich gerechnet werden.
@Test("Zu grosse Bitrate geht weiterhin an den Transcoder")
func bitrateStillTranscodes() {
let entscheidung = decider.decide(
profile: MediaProfile(
container: "mkv", videoCodec: "hevc", audioCodec: "ac3",
bitrate: 80_000, hasKnownStreams: true
),
policy: PlaybackPolicy(
allowsTranscoding: true, allowsFallbackEngine: true, maximumBitrate: 8_000
)
)
#expect(entscheidung.method == .transcode(maximumBitrate: 8_000))
}
/// Ohne Transcoder gibt es auch kein Umpacken — beides läuft über
/// denselben Weg im Server.
@Test("Ohne Serverhilfe bleibt es bei VLC")
func withoutServerHelpVLC() {
let entscheidung = decider.decide(
profile: MediaProfile(
container: "mkv", videoCodec: "hevc", audioCodec: "ac3",
bitrate: 20_000, hasKnownStreams: true
),
policy: PlaybackPolicy(
allowsTranscoding: false, allowsFallbackEngine: true
)
)
#expect(entscheidung.method == .directPlay(engine: .vlc))
}
}