Meine Rundung war eine Klinge, und ich bin hineingelaufen

Du hast recht, und zwar doppelt.

**Der Versatz stand im falschen Menü.** „Später (+100 ms)" unter den
Tonspuren liest sich wie der Name einer Spur. Jetzt hat er einen eigenen
Knopf in der Leiste, mit eigenem Zeichen und festen Stufen von „Ton 1 s
früher" bis „Ton 1 s später", dazwischen „Ohne Versatz" — mit Haken, wie
bei der Qualität. Im Tonmenü stehen wieder nur Sprachen.

**Und das Schlimmere: die Rundung aufs Schlüsselbild hat es verschlechtert.**
Sie musste auf die Millisekunde stimmen, sonst nimmt ffmpeg das
Schlüsselbild davor. Gemessen an einer Datei mit Schlüsselbildern alle
fünf Sekunden:

    -ss 15.015  →  Bild ab 0,000 | Ton ab 0,004
    -ss 15.014  →  Bild ab 0,000 | Ton ab 4,983

Eine Millisekunde. Und die Zeile, die den Wert an ffmpeg gibt, rundete auf
drei Nachkommastellen — bei 23,976 Bildern je Sekunde liegen
Schlüsselbilder auf Werten wie 3,336667, und die überleben das nicht
zuverlässig. Damit konnte jede Fortsetzung schlechter werden als vorher.
Das war mein Fehler, eingebaut in E8GSRE.

**Der richtige Hebel ist `-noaccurate_seek`**, und er braucht gar keine
Genauigkeit. Damit schneidet ffmpeg auch den Ton nicht auf die Sekunde,
sondern lässt ihn beim selben Schlüsselbild anfangen wie das kopierte
Bild. Gemessen, dieselbe Datei, fünf verschiedene Sprungwerte quer durch
die Bildgruppe:

    15,014 → 0,015 | 17 → 0,004 | 19,9 → 0,004 | 12,5 → 0,015 | 3,2 → 0,011

Statt bis zu fünf Sekunden also überall unter zwanzig Millisekunden, und
zwar ohne dass irgendein Wert getroffen werden muss. Fünf Testfälle quer
durch die Bildgruppe halten das jetzt fest; vorher prüfte der Sprungtest
nur, ob überhaupt Segmente entstehen.

Die Schlüsselbildsuche bleibt — aber nur noch, um dem Spieler eine
ehrliche Uhr zu geben. Vergreift sie sich, geht die Uhr falsch; Bild und
Ton bleiben zusammen. Vorher hing die Ausrichtung daran, und das war der
schlechte Tausch.

Und die Sekundenangaben gehen jetzt mit sechs Nachkommastellen hinaus
statt mit drei.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-07 20:24:21 +02:00
co-authored by Claude Opus 5
parent 6ad4deb634
commit dc9d1cea85
5 changed files with 119 additions and 39 deletions
+3
View File
@@ -95,6 +95,9 @@ export const icons = {
* reserviert, was etwas wegnimmt. Wer meldet, dass ein Plakat nicht passt,
* löst keinen Alarm aus. */
fahne: rahmen('<path d="M6 21V4"/><path d="M6 4.8h11.6l-2.5 4 2.5 4H6"/>'),
/** Tonversatz — zwei Pfeile, die auseinandergehen. */
versatz: rahmen('<path d="M3 12h18"/><path d="M7 8l-4 4 4 4"/><path d="M17 8l4 4-4 4"/>'),
} as const;
export type IconName = keyof typeof icons;
+32 -33
View File
@@ -85,12 +85,13 @@ export function geraeteprofil(): {
/** Wie oft der Stand zum Server geht. */
const MELDEABSTAND = 10_000;
/** In welchen Schritten sich der Tonversatz verstellen lässt. */
const VERSATZSCHRITT = 100;
/** Wie weit in beide Richtungen. Darüber hinaus ist es kein Versatz mehr,
* sondern eine kaputte Datei. */
const VERSATZGRENZE = 5000;
/** Die Stufen des Tonversatzes in Millisekunden.
*
* **Nicht jeder Hunderter von −5000 bis +5000.** Das wären einhunderteins
* Einträge für etwas, das man im Jahr dreimal braucht. Diese elf decken ab,
* was vorkommt — und wer mehr braucht, hat keine Einstellung zu wenig,
* sondern eine kaputte Datei. */
const VERSAETZE = [-1000, -500, -300, -200, -100, 0, 100, 200, 300, 500, 1000];
/** Wo gemerkte Tonversätze liegen — je Datei, in diesem Browser.
*
@@ -132,10 +133,11 @@ export function tonspracheMerken(sprache: string): void {
try { localStorage.setItem(SPRACHSCHLUESSEL, sprache); } catch { /* Privates Fenster. */ }
}
/** „läuft mit", „200 ms später", „300 ms früher". */
/** „Ohne Versatz", „Ton 200 ms später", „Ton 1 s früher". */
function versatzWort(ms: number): string {
if (ms === 0) return 'läuft mit dem Bild';
return ms > 0 ? `${ms} ms später` : `${-ms} ms früher`;
if (ms === 0) return 'Ohne Versatz';
const betrag = Math.abs(ms) >= 1000 ? `${Math.abs(ms) / 1000} s` : `${Math.abs(ms)} ms`;
return `Ton ${betrag} ${ms > 0 ? 'später' : 'früher'}`;
}
/** Die Stufen, die im Menü stehen. Muss zu `Stromentscheid.Stufen` passen. */
@@ -373,6 +375,7 @@ export function spieler(anfrage: Spieleranfrage): void {
const tonmenue = menue('tonspur', 'Tonspur');
const textmenue = menue('untertitel', 'Untertitel');
const stufenmenue = menue('regler', 'Qualität');
const versatzmenue = menue('versatz', 'Tonversatz');
const links = document.createElement('div');
links.className = 'pl-gruppe';
@@ -380,7 +383,8 @@ export function spieler(anfrage: Spieleranfrage): void {
const rechts = document.createElement('div');
rechts.className = 'pl-gruppe';
rechts.append(rest, tonmenue.knopf, textmenue.knopf, stufenmenue.knopf, vollbild);
rechts.append(
rest, tonmenue.knopf, versatzmenue.knopf, textmenue.knopf, stufenmenue.knopf, vollbild);
const unten = document.createElement('div');
unten.className = 'pl-unten';
@@ -647,30 +651,25 @@ export function spieler(anfrage: Spieleranfrage): void {
}))
: [{ text: 'Diese Datei hat keinen Ton.', gewaehlt: false, tun: () => {} }];
// **Der Versatz steht im Tonmenü, nicht in einem eigenen.** Wer ihn
// sucht, sucht ihn beim Ton — und ein vierter Knopf in der Leiste wäre
// einer für etwas, das man im Jahr dreimal braucht.
tonmenue.setzen(spuren);
// **Der Versatz hat ein eigenes Menü.** Er stand vorher unter den
// Tonspuren, und dort las er sich wie eine: „Später (+100 ms)" sah aus
// wie der Name einer Spur. Eine Einstellung gehört nicht in eine Liste
// von Inhalten.
//
// Zwei Schritte und ein Zurück statt einer Liste: eine Aufzählung von
// −5000 bis +5000 in Hundertern wären einhunderteins Einträge, und man
// sucht ohnehin nicht einen Wert, sondern rückt ihn, bis es passt.
const verstellen = (um: number): void => {
tonversatz = Math.max(-VERSATZGRENZE, Math.min(VERSATZGRENZE, tonversatz + um));
versatzMerken(anfrage.dateiId, tonversatz);
void stromAufbauen(filmzeit());
};
// Ohne Tonspur gibt es nichts zu verschieben.
const versatzpunkte = toene.length === 0 ? [] : [
{ text: `Ton ${versatzWort(tonversatz)}`, gewaehlt: false, tun: () => {} },
{ text: `Später (+${VERSATZSCHRITT} ms)`, gewaehlt: false, tun: () => verstellen(VERSATZSCHRITT) },
{ text: `Früher (−${VERSATZSCHRITT} ms)`, gewaehlt: false, tun: () => verstellen(-VERSATZSCHRITT) },
...(tonversatz !== 0
? [{ text: 'Versatz zurücksetzen', gewaehlt: false, tun: () => verstellen(-tonversatz) }]
: []),
];
tonmenue.setzen([...spuren, ...versatzpunkte]);
// Feste Stufen statt zweier Tasten, wie bei der Qualität: so zeigt der
// Haken, wo man steht, und man trifft einen Wert, statt sich
// hinzutasten — jeder Schritt baut den Strom neu auf.
versatzmenue.setzen(VERSAETZE.map(ms => ({
text: versatzWort(ms),
gewaehlt: ms === tonversatz,
tun: () => {
tonversatz = ms;
versatzMerken(anfrage.dateiId, ms);
void stromAufbauen(filmzeit());
},
})));
const texte = auskunft?.subtitleStreams ?? [];
textmenue.setzen(