DTS, TrueHD und Vorbis gehen jetzt an den Server statt an VLC
Bisher entschied der Decider: steht nur die Schachtel im Weg, wird
umgepackt — stimmt ein Codec nicht, geht es an VLC. Das war beim Ton zu
grob gedacht.
Beim Umpacken nennt das Geräteprofil dem Server, was ankommen darf:
"VideoCodec": "h264,hevc"
"AudioCodec": "aac,ac3,eac3,mp3"
Trifft ein Strom eine dieser Angaben, kopiert der Server ihn; trifft er
sie nicht, rechnet er **genau diesen einen** um. Eine Matroska mit HEVC
und DTS braucht also kein VLC: das Bild geht Byte für Byte durch, und nur
die Tonspur wird nach AC3 oder AAC kodiert. Das kostet Tonqualität und
sonst nichts.
Die Bedingung heisst deshalb nicht mehr „nur die Schachtel", sondern
„das Bild bleibt": Container und Ton dürfen im Weg stehen, der
Videocodec nicht. Dazu musste die Prüfung der Schachtel aufhören, sich
immer zu stellen — eine MP4 mit DTS stand mit einem reinen Tonhindernis
da und fiel trotzdem durch die Liste umpackbarer Container.
**Der grössere Gewinn liegt auf dem Apple TV.** Dort gibt es VLC nicht,
und bisher landete eine Datei mit DTS-Ton beim vollen Neurechnen — Bild
inbegriffen, obwohl mit dem Bild nichts war. Jetzt bleibt das Bild
unberührt. Auf iPhone, iPad und Mac verhält sich derselbe Film jetzt
gleich, und zwar über AVPlayer: mit Bild-in-Bild, AirPlay und
Sperrbildschirm, die es auf dem VLC-Weg nie gab.
Geprüft im Simulator gegen die Attrappe mit `--vlc` (meldet DTS): der
Film läuft, und in der Leiste steht jetzt der AirPlay-Knopf, der vorher
fehlte — der sichtbarste Beleg dafür, dass AVPlayer spielt und nicht VLC.
„Umgepackt" und „umgepackt, Ton umgerechnet" sind in den technischen
Infos und in der Diagnose auseinandergehalten. Ein Weg, der den Ton neu
kodiert, darf nicht „ohne Neukodierung" heissen.
Was bleibt bei VLC: nicht unterstützte **Bild**codecs, allen voran AV1,
und Dateien, die der Server noch nicht untersucht hat. Dafür gäbe es nur
das volle Neurechnen — eine eigene Entscheidung, nicht diese.
iOS und tvOS bauen, 463 Tests grün.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
3ed682dd00
commit
9e7f3393b4
@@ -66,8 +66,11 @@ struct PlaybackDeciderTests {
|
||||
#expect(decision.rationale.contains(.containerUnsupported("mkv")))
|
||||
}
|
||||
|
||||
/// **Nur ohne Serverhilfe.** Darf umgepackt werden, geht dieselbe Datei
|
||||
/// an AVPlayer — siehe `RemuxDecisionTests`. Hier ist Umrechnen verboten,
|
||||
/// und dann bleibt VLC der einzige Weg.
|
||||
@Test(
|
||||
"Nicht unterstützter Ton geht an VLC",
|
||||
"Nicht unterstützter Ton geht ohne Transcoder an VLC",
|
||||
arguments: ["dts", "dca", "truehd", "vorbis", "opus"]
|
||||
)
|
||||
func unsupportedAudioFallsBackToVLC(codec: String) {
|
||||
@@ -162,14 +165,30 @@ struct PlaybackDeciderTests {
|
||||
#expect(decision.method == .directPlay(engine: .avPlayer))
|
||||
}
|
||||
|
||||
@Test("Ohne VLC übernimmt der Transcoder")
|
||||
func withoutFallbackEngineTranscodingTakesOver() {
|
||||
/// Auf dem Apple TV gibt es VLC nicht. Früher landete eine Datei mit
|
||||
/// DTS-Ton deshalb beim vollen Neurechnen — Bild inbegriffen, obwohl mit
|
||||
/// dem Bild nichts war. Jetzt bleibt es beim Umpacken, und nur der Ton
|
||||
/// wird angefasst.
|
||||
@Test("Ohne VLC reicht Umpacken, solange das Bild passt")
|
||||
func withoutFallbackEngineRemuxIsEnough() {
|
||||
let policy = PlaybackPolicy(allowsTranscoding: true, allowsFallbackEngine: false)
|
||||
|
||||
let decision = PlaybackDecider().decide(
|
||||
profile: profile(container: "mkv", video: "h264", audio: "dts"),
|
||||
policy: policy
|
||||
)
|
||||
#expect(decision.method == .remux)
|
||||
}
|
||||
|
||||
/// Erst wenn auch das Bild nicht geht, muss wirklich gerechnet werden.
|
||||
@Test("Ohne VLC übernimmt der Transcoder, sobald das Bild nicht passt")
|
||||
func withoutFallbackEngineTranscodingTakesOver() {
|
||||
let policy = PlaybackPolicy(allowsTranscoding: true, allowsFallbackEngine: false)
|
||||
|
||||
let decision = PlaybackDecider().decide(
|
||||
profile: profile(container: "mkv", video: "av1", audio: "aac"),
|
||||
policy: policy
|
||||
)
|
||||
#expect(decision.method == .transcode(maximumBitrate: nil))
|
||||
}
|
||||
|
||||
@@ -180,9 +199,10 @@ struct PlaybackDeciderTests {
|
||||
let policy = PlaybackPolicy(allowsTranscoding: true, allowsFallbackEngine: false)
|
||||
|
||||
let decision = PlaybackDecider().decide(
|
||||
profile: profile(container: "mkv", video: "h264", audio: "dts"),
|
||||
profile: profile(container: "mkv", video: "av1", audio: "aac"),
|
||||
policy: policy
|
||||
)
|
||||
#expect(decision.method == .transcode(maximumBitrate: nil))
|
||||
#expect(decision.engine == .avPlayer)
|
||||
}
|
||||
|
||||
@@ -241,22 +261,64 @@ struct RemuxDecisionTests {
|
||||
#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() {
|
||||
/// Steht der Ton im Weg, bleibt das Bild trotzdem unberührt: der Server
|
||||
/// kopiert es und rechnet genau die eine Tonspur um. Das ist der Grund,
|
||||
/// warum DTS auf dem Apple TV nicht mehr das ganze Bild kostet.
|
||||
@Test(
|
||||
"Nicht unterstützter Ton wird umgepackt, nicht an VLC gegeben",
|
||||
arguments: ["dts", "dca", "truehd", "vorbis", "opus"]
|
||||
)
|
||||
func unsupportedAudioIsRemuxed(codec: String) {
|
||||
let entscheidung = decider.decide(
|
||||
profile: MediaProfile(
|
||||
container: "mkv", videoCodec: "hevc", audioCodec: "dts",
|
||||
container: "mkv", videoCodec: "hevc", audioCodec: codec,
|
||||
bitrate: 20_000, hasKnownStreams: true
|
||||
),
|
||||
policy: policy
|
||||
)
|
||||
#expect(entscheidung.method == .directPlay(engine: .vlc))
|
||||
#expect(entscheidung.method == .remux)
|
||||
#expect(entscheidung.engine == .avPlayer)
|
||||
// Die Begründung ist das, woran die Oberfläche später erkennt, dass
|
||||
// der Ton angefasst wurde — ohne sie stünde dort „ohne Neukodierung".
|
||||
#expect(entscheidung.rationale.contains(.audioCodecUnsupported(codec)))
|
||||
}
|
||||
|
||||
@Test("Bei AV1 ebenso wenig")
|
||||
func av1StillGoesToVLC() {
|
||||
/// Eine MP4 mit DTS steht mit einem reinen Tonhindernis da. Sie an der
|
||||
/// Liste umpackbarer Schachteln zu messen, hiesse sie an einer Frage
|
||||
/// scheitern zu lassen, die sich gar nicht stellt.
|
||||
@Test("Auch eine MP4 mit DTS wird umgepackt")
|
||||
func mp4WithDTSIsRemuxed() {
|
||||
let entscheidung = decider.decide(
|
||||
profile: MediaProfile(
|
||||
container: "mp4", videoCodec: "h264", audioCodec: "dts",
|
||||
bitrate: 20_000, hasKnownStreams: true
|
||||
),
|
||||
policy: policy
|
||||
)
|
||||
#expect(entscheidung.method == .remux)
|
||||
}
|
||||
|
||||
/// Schachtel und Ton zugleich — der häufigste Fall bei einem Remux mit
|
||||
/// Tonspur aus der Blu-ray.
|
||||
@Test("Schachtel und Ton zusammen gehen auch")
|
||||
func containerAndAudioTogether() {
|
||||
let entscheidung = decider.decide(
|
||||
profile: MediaProfile(
|
||||
container: "mkv", videoCodec: "h264", audioCodec: "truehd",
|
||||
bitrate: 20_000, hasKnownStreams: true
|
||||
),
|
||||
policy: policy
|
||||
)
|
||||
#expect(entscheidung.method == .remux)
|
||||
#expect(entscheidung.rationale.contains(.containerUnsupported("mkv")))
|
||||
#expect(entscheidung.rationale.contains(.audioCodecUnsupported("truehd")))
|
||||
}
|
||||
|
||||
/// Der Gegenbeweis, und die Grenze: stimmt das **Bild** nicht, hilft kein
|
||||
/// Durchreichen — dann müsste jede Bildzeile neu gerechnet werden, und
|
||||
/// dafür ist dies nicht der Weg.
|
||||
@Test("Bei falschem Bildcodec bleibt es beim Rückfall")
|
||||
func wrongVideoCodecIsNotRemuxed() {
|
||||
let entscheidung = decider.decide(
|
||||
profile: MediaProfile(
|
||||
container: "mkv", videoCodec: "av1", audioCodec: "aac",
|
||||
|
||||
Reference in New Issue
Block a user