Ein Abspann darf aus Stücken bestehen
Zwei Fälle fielen durch, beide am Bestand gesehen. Bei einem Marvel-Film liegt hinter dem Abspann eine Post-Credit-Szene: die Suche begann am Dateiende, fand nur den kurzen Rest dahinter und verwarf ihn als zu kurz. Und ein Abspann, den eine Szene in der Mitte teilt, zerfiel in zwei Reste, von denen keiner lang genug war. Dieselbe Ursache: der Abspann musste eine zusammenhängende dunkle Strecke bis zum Dateiende sein. Jetzt bildet die Suche Läufe, fasst sie über Lücken bis vier Minuten zusammen — so lang ist die längste bekannte Post-Credit-Szene plus Reserve — und lässt die **Summe** über die Mindestlänge entscheiden statt jedes einzelne Stück. Ein Lauf unter fünfzehn Sekunden ist eine Blende und zählt nicht mit. Dafür mussten die eindeutigen Indizes über (Titel, Art) weichen; sie standen mit dem Vermerk „eine Art einmal je Titel" da, und genau das stimmt nicht mehr. `SchreibenAsync` ersetzt jetzt die Menge, statt eine Zeile umzuschreiben — das ist der einzige Weg, der in beide Richtungen stimmt, wenn aus zwei Stücken wieder eines wird. **Drei Stellen trugen das nicht.** Im Web hiess „Abspann überspringen" bei einem Film immer Schluss — beim ersten von zwei Stücken bekäme niemand die Szene je zu sehen; geschlossen wird jetzt nur, wenn nichts mehr folgt. Auf iOS verwarf der Spieler die Abspannmarke eines Films ganz; diese Regel gehört nicht dorthin, sondern auf den Server, und ist ersatzlos weg. Und die Gesehen-Schwelle nahm irgendeine Abspannzeile — ohne `OrderBy` entschied die Datenbank; gemeint ist immer die früheste. Dazu der Schalter „Zusätzliche Szenen mit überspringen" (Vorgabe aus) bei den Geräteeinstellungen, und ein Messwerkzeug: ein Knopf auf der Filmseite lädt die Kurve je Schlüsselbild als Tabelle — Dunkelanteil des ganzen Bildes und der beiden Randstreifen. Ob bei Outtakes im Abspann die Ränder dunkel bleiben, ist eine Vermutung; bevor daraus eine Schwelle wird, will ich die Zahlen sehen. Fünf neue Prüfungen, darunter die wichtigste: ein durchgehender Abspann bleibt ein Stück. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
791c00dacc
commit
32f592b83e
@@ -92,9 +92,56 @@ public sealed partial class Bildabtaster(FfmpegOptions optionen, ILogger<Bildabt
|
||||
return proben.Average(p => p.Dunkelanteil);
|
||||
}
|
||||
|
||||
/// <param name="zuschnitt">
|
||||
/// Ein <c>crop</c>-Ausdruck vor der Messung, oder <c>null</c> für das
|
||||
/// ganze Bild. Nur das Messwerkzeug benutzt das.
|
||||
/// </param>
|
||||
/// <summary>Drei Kurven über denselben Abschnitt — zum Nachsehen.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Ein Werkzeug, keine Erkennung.</b> Die Randstreifen sind eine
|
||||
/// Vermutung: bei Outtakes im Abspann läuft das Bild vielleicht in der
|
||||
/// Mitte und die Ränder bleiben schwarz. Ob das trägt, sieht man nur an
|
||||
/// echten Dateien — und dafür braucht es die Zahlen, bevor eine Schwelle
|
||||
/// gesetzt wird.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Drei Durchläufe, nicht einer.</b> Zwei <c>blackframe</c> in einer
|
||||
/// Kette schrieben denselben Metadatenschlüssel und überschrieben
|
||||
/// einander; mit <c>split</c> ginge es, kostete aber drei Ausgabewege
|
||||
/// und eine Zusammenführung. Für etwas, das jemand einmal je Film von
|
||||
/// Hand anstösst, sind drei Läufe über Schlüsselbilder das günstigere
|
||||
/// Geschäft.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public async Task<IReadOnlyList<Kurvenpunkt>> KurveAsync(
|
||||
string pfad, double ab, double dauer, CancellationToken ct = default)
|
||||
{
|
||||
async Task<IReadOnlyList<Bildprobe>> Lauf(string? zuschnitt) => Lesen(
|
||||
await LaufenAsync(pfad, ab, dauer, nurSchluesselbilder: true,
|
||||
TimeSpan.FromMinutes(10), ct, zuschnitt),
|
||||
ab);
|
||||
|
||||
var gesamt = await Lauf(null);
|
||||
var links = await Lauf("crop=iw*0.2:ih:0:0");
|
||||
var rechts = await Lauf("crop=iw*0.2:ih:iw*0.8:0");
|
||||
|
||||
// Nach Zeit zusammenführen: die drei Läufe sehen dieselben
|
||||
// Schlüsselbilder, aber darauf zu bauen wäre eine Annahme.
|
||||
double BeiZeit(IReadOnlyList<Bildprobe> reihe, double sekunde) =>
|
||||
reihe.Count == 0 ? double.NaN
|
||||
: reihe.MinBy(p => Math.Abs(p.Sekunde - sekunde)).Dunkelanteil;
|
||||
|
||||
return gesamt
|
||||
.Select(p => new Kurvenpunkt(
|
||||
p.Sekunde, p.Dunkelanteil, BeiZeit(links, p.Sekunde), BeiZeit(rechts, p.Sekunde)))
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private async Task<string> LaufenAsync(
|
||||
string pfad, double ab, double dauer, bool nurSchluesselbilder,
|
||||
TimeSpan frist, CancellationToken ct)
|
||||
TimeSpan frist, CancellationToken ct, string? zuschnitt = null)
|
||||
{
|
||||
var start = new ProcessStartInfo(optionen.Ffmpeg)
|
||||
{
|
||||
@@ -135,7 +182,8 @@ public sealed partial class Bildabtaster(FfmpegOptions optionen, ILogger<Bildabt
|
||||
// dieselbe Schwelle bei der einen Datei etwas anderes als bei der
|
||||
// nächsten — „Schwelle 247,6" im Protokoll war der Hinweis darauf.
|
||||
start.ArgumentList.Add(
|
||||
$"scale={Breite}:-2,format=gray,"
|
||||
(zuschnitt is null ? "" : zuschnitt + ",")
|
||||
+ $"scale={Breite}:-2,format=gray,"
|
||||
+ $"blackframe=amount=0:threshold={Dunkelschwelle},"
|
||||
+ "metadata=print:key=lavfi.blackframe.pblack:file=-");
|
||||
start.ArgumentList.Add("-f");
|
||||
|
||||
Reference in New Issue
Block a user