Vorrat anlegen, und VLC meldet endlich das Stocken

Zwei Änderungen, die an der Wiedergabe selbst etwas tun — beide ohne
Abhängigkeit davon, ob die Server je eine Leiter liefern.

**Vorrat.** Ohne Angabe legt AVPlayer so viel an, wie es für einen
zügigen Start braucht, und keinen Meter mehr. Das ist richtig, solange
die Leitung trägt, und falsch, sobald sie aussetzt: ein Loch von fünf
Sekunden trifft dann auf einen Puffer von fünf Sekunden, und das Bild
steht. Fünfundzwanzig Sekunden sind ein Anfangswert, kein Messergebnis.

Nur für Filme und Folgen über das Netz. **Nicht für Sender** — dort
hieße ein halbminütiger Vorrat eine halbe Minute Verzug und ein zähes
Umschalten. Und nicht für Dateien von der Platte, die brauchen keinen.
Wer das entscheidet, muss mehr wissen als der Motor, deshalb reist die
Zahl mit der Quelle statt dass die Engine rät.

**VLC hat das Stocken nie gemeldet.** `VLCMediaPlayerState` kennt keinen
Wert dafür — die Aufzählung führt nur `opening`, `playing`, `paused`,
`stopped`, `error`. Der Puffer-Fortschritt kommt über
`mediaPlayerBufferingChanged:`, und die Methode fehlte. Folge:
`.buffering` ging dort **nie** an, der Wachhund im Player löste nie aus,
und ein hängender Strom lief stumm ins Nichts — ohne Kreisel, ohne
Meldung, ohne Abbruch. Ausgerechnet auf dem Weg, der die großen Remuxe
spielt, bei denen eine schwache Leitung am ehesten scheitert.

Gewertet wird nur aus dem Spielen heraus: laut Dokumentation kommt vor
jeder Wiedergabe einmal 0,0 und einmal 1,0, und dieses anfängliche
Füllen ist der Start, kein Rückler.

Dazu meldet VLC jetzt überhaupt Statistik. `variantCount` bleibt dabei
`nil` und nicht `1` — eine Datei hat keine Leiter, und „gibt es nicht"
ist etwas anderes als „gibt es einmal". Bei der Bitrate steht die
Annahme im Kommentar statt im Nebel: libvlc führt `inputBitrate`
vermutlich in Byte je Sekunde, daher der Faktor acht.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-09-27 15:06:36 +02:00
co-authored by Claude Opus 5
parent afab93f53a
commit baa542690c
4 changed files with 114 additions and 2 deletions
@@ -94,6 +94,24 @@ public final class AVPlayerEngine: VideoPlayerEngine {
AVURLAssetPreferPreciseDurationAndTimingKey: false,
])
let item = AVPlayerItem(asset: asset)
// **Vorrat statt Reaktion.**
//
// Ohne diese Angabe legt AVPlayer so viel an, wie es für einen
// zügigen Start braucht — und keinen Meter mehr. Das ist richtig, wenn
// die Leitung trägt, und falsch, wenn sie aussetzt: ein Loch von
// fünf Sekunden trifft dann auf einen Puffer von fünf Sekunden, und
// das Bild steht.
//
// Der Preis ist ehrlich: der Start dauert länger, und wer springt,
// wirft den angelegten Vorrat weg. Beides ist weniger schlimm als ein
// Rückler mitten im Satz.
if let vorrat = source.forwardBuffer {
item.preferredForwardBufferDuration = TimeInterval(
Double(vorrat.components.seconds)
)
}
player.replaceCurrentItem(with: item)
observe(item: item)
@@ -14,11 +14,29 @@ public struct PlaybackSource: Sendable, Equatable {
/// Ab wo gestartet werden soll. Kommt aus dem zuletzt gemeldeten
/// Fortschritt; `.zero` heißt von vorn.
public let startTime: Duration
/// Wie viel Vorrat der Player anlegen soll.
///
/// `nil` überlässt es dem System. Das ist für eine Datei auf der Platte
/// richtig und für einen Sender richtig — bei einem Strom über eine
/// wackelige Leitung ist es zu wenig: der Systemwert ist auf schnellen
/// Start ausgelegt, nicht darauf, einen Aussetzer zu überstehen.
///
/// **Wer das entscheidet, muss mehr wissen als der Motor.** Ob es ein
/// Sender ist, ob die Datei lokal liegt — das weiß der Player, nicht die
/// Engine. Deshalb reist die Zahl mit der Quelle, statt dass die Engine
/// rät.
public let forwardBuffer: Duration?
public init(url: URL, origin: Origin, startTime: Duration = .zero) {
public init(
url: URL,
origin: Origin,
startTime: Duration = .zero,
forwardBuffer: Duration? = nil
) {
self.url = url
self.origin = origin
self.startTime = startTime
self.forwardBuffer = forwardBuffer
}
}