„Auto" misst jetzt die Verbindung, statt die Einstellung abzulesen
Der Denkfehler, und er war meiner: `automatic` hiess wörtlich „nimm, was in den Einstellungen steht". Stand dort „Original", bat die App über eine Zugverbindung um eine 4K-Datei und wartete vierzig Sekunden auf etwas, das nie kommen konnte. Das ist keine Automatik, das ist eine Durchreiche mit einem irreführenden Namen. **Die Einstellung ist jetzt die Obergrenze, nicht das Ziel** — „bis hierhin, wenn die Leitung es trägt". Im eigenen Netz bleibt alles wie bisher: dort gibt es keine Grenze, weil die Leitung das Original trägt und ein Deckel das Bild ohne Grund verschlechterte. Die Unterscheidung steht in `isLocalConnection` und ist nicht geraten. Unterwegs beginnt „Auto" bei acht Megabit — genug für ordentliches 1080p, wenig genug, dass es fast überall anläuft — und tastet sich von dort hinauf, solange es gut geht, bis höchstens zur eingestellten Decke. Hinunter geht es sofort, sobald es hakt. **Und die Auflösung geht mit.** Bitrate allein genügt nicht: 4K mit zwei Megabit sieht schlechter aus als 720p mit zwei Megabit — dieselbe Datenmenge, auf viermal so viele Bildpunkte verteilt. Wer die Bitrate senkt, muss die Auflösung mitnehmen, sonst spart er Daten und verschenkt das Bild. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
efed8717a3
commit
fd11a9eebe
@@ -4862,13 +4862,13 @@
|
||||
"de" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Wird eine Datei größer als die eingestellte Grenze, rechnet der Server sie unterwegs herunter und passt die Bitrate laufend an die Verbindung an."
|
||||
"value" : "Die Obergrenze, nicht das Ziel. Im eigenen Netz überträgt der Server das Original; unterwegs beginnt Flixy kleiner und tastet sich hinauf, solange die Verbindung es trägt — höchstens bis zu dieser Stufe. Auflösung und Bitrate gehen dabei zusammen."
|
||||
}
|
||||
},
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "If a file exceeds the limit, the server transcodes it on the fly and adapts the bitrate to your connection."
|
||||
"value" : "An upper limit, not a target. On your own network the server sends the original; away from home Flixy starts lower and works its way up as long as the connection holds — never above this step. Resolution and bitrate move together."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,17 +227,63 @@ final class PlayerModel {
|
||||
/// als ein Sender, der gar nicht mehr angeht.
|
||||
private var downgradeBudget: Int { isLive ? 1 : 2 }
|
||||
|
||||
/// **Womit „Auto" anfängt.**
|
||||
///
|
||||
/// Hier lag der Denkfehler: `automatic` hiess wörtlich „nimm, was in den
|
||||
/// Einstellungen steht", und stand dort „Original", bat die App über eine
|
||||
/// Zugverbindung um eine 4K-Datei. Das ist keine Automatik, das ist eine
|
||||
/// Durchreiche.
|
||||
///
|
||||
/// Jetzt ist die Einstellung die **Obergrenze**, nicht das Ziel: „bis
|
||||
/// hierhin, wenn die Leitung es trägt". Angefangen wird mit einem Wert,
|
||||
/// der fast überall anläuft; von dort geht es hinauf, solange es gut
|
||||
/// geht, und hinunter, sobald es hakt.
|
||||
///
|
||||
/// **Im eigenen Netz keine Grenze.** Dort trägt die Leitung das Original,
|
||||
/// und ein Deckel machte das Bild ohne Grund schlechter. Die
|
||||
/// Unterscheidung ist nicht geraten — sie steht in `isLocalConnection`.
|
||||
private var startingCeiling: Int? {
|
||||
guard !isLocalConnection else { return nil }
|
||||
// Acht Megabit tragen 1080p ordentlich und laufen auch über eine
|
||||
// mässige Fernverbindung an. Wer mehr will und mehr hat, kommt über
|
||||
// das Hinaufschalten dorthin.
|
||||
return 8_000
|
||||
}
|
||||
|
||||
/// Die Auflösung, die zur aktuellen Stufe gehört.
|
||||
///
|
||||
/// **Bitrate allein genügt nicht.** 4K mit zwei Megabit sieht schlechter
|
||||
/// aus als 720p mit zwei Megabit — dieselbe Datenmenge, auf viermal so
|
||||
/// viele Bildpunkte verteilt. Wer die Bitrate senkt, muss die Auflösung
|
||||
/// mitnehmen, sonst spart er Daten und verschenkt das Bild.
|
||||
private var automaticResolution: String? {
|
||||
guard quality == .automatic, let ceiling = automaticCeiling else { return nil }
|
||||
let sprossen = PlayerQuality.available(forSourceHeight: sourceHeight)
|
||||
.compactMap { option -> PlayerQuality.Rung? in
|
||||
guard case .limited(let rung) = option else { return nil }
|
||||
return rung
|
||||
}
|
||||
// Die Sprosse, die zu dieser Bitrate gehört — oder die nächstgrössere,
|
||||
// falls die Zahl von woanders kommt.
|
||||
let passend = sprossen.first { $0.bitrate >= ceiling } ?? sprossen.last
|
||||
guard let passend else { return nil }
|
||||
return "\(passend.width)x\(passend.height)"
|
||||
}
|
||||
|
||||
/// Die Richtlinie, wie sie **jetzt** gilt.
|
||||
///
|
||||
/// Die eingestellte Obergrenze, überschrieben von dem, was die Automatik
|
||||
/// Die eingestellte Obergrenze, gedeckelt von dem, was die Automatik
|
||||
/// unterwegs gelernt hat. Getrennt von `quality`, damit eine schlechte
|
||||
/// Zugfahrt nicht als gemerkte Stufe das Wohnzimmer verschlechtert.
|
||||
private var effectivePolicy: PlaybackPolicy {
|
||||
let base = quality.policy(basedOn: policy)
|
||||
guard quality == .automatic, let ceiling = automaticCeiling else { return base }
|
||||
guard quality == .automatic else { return base }
|
||||
|
||||
// Die Einstellung ist die Decke, die Automatik der Boden darunter.
|
||||
guard let ceiling = automaticCeiling ?? startingCeiling else { return base }
|
||||
return PlaybackPolicy(
|
||||
allowsTranscoding: true,
|
||||
// Ohne Ersatzmotor: sonst spielte VLC die Datei unverändert ab,
|
||||
// Ohne Ersatz-Engine: sonst spielte VLC die Datei unverändert ab,
|
||||
// und die abgesenkte Grenze bliebe wirkungslos.
|
||||
allowsFallbackEngine: false,
|
||||
maximumBitrate: min(ceiling, base.maximumBitrate ?? ceiling),
|
||||
@@ -578,7 +624,9 @@ final class PlayerModel {
|
||||
case .transcode(let maximumBitrate):
|
||||
TranscodeSettings(
|
||||
maximumBitrate: maximumBitrate,
|
||||
resolution: quality.resolution,
|
||||
// Bei „Auto" die Auflösung zur gewählten Stufe, sonst die der
|
||||
// ausdrücklich gewählten.
|
||||
resolution: quality.resolution ?? automaticResolution,
|
||||
// **Nur bei „automatisch".** Eine ausdrücklich gewählte Stufe
|
||||
// ist eine Ansage, keine Obergrenze: wer „720p · 3 Mbps"
|
||||
// antippt, bekommt das und nicht „höchstens 3, meistens
|
||||
@@ -845,7 +893,14 @@ final class PlayerModel {
|
||||
if lastStepUp == nil { self.lastStepUp = now }
|
||||
return
|
||||
}
|
||||
guard let höher = sprossen.first(where: { $0 > aktuell }) else { return }
|
||||
// **Bis zur eingestellten Decke, nicht bis zur Startstufe.** Wer
|
||||
// „Original" eingestellt hat und eine Leitung hat, die es trägt,
|
||||
// soll dort auch ankommen — das ist die andere Hälfte von
|
||||
// „richtet sich nach der Verbindung".
|
||||
let decke = quality.policy(basedOn: policy).maximumBitrate
|
||||
guard let höher = sprossen.first(where: { $0 > aktuell }),
|
||||
decke.map({ höher <= $0 }) ?? true
|
||||
else { return }
|
||||
ziel = höher
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user