„Kenne ich" heisst nicht „kann ich diese Datei"
Der Spieler schickte sein Profil aus `canPlayType` — und das ist eine Auskunft des `<video>`-Elements. Ein Strom läuft aber nicht durch das Element, sondern durch den MSE-Puffer, und die beiden sind sich nicht einig. Gemessen in Chromium: | | canPlayType | MediaSource.isTypeSupported | |---|---|---| | `audio/flac` | probably | **false** | | `audio/mp4; codecs="mp3"` | probably | **false** | Der Server glaubte dem Profil, packte die Datei pflichtschuldig um und liess die Spur unberührt — und der Browser stand vor etwas, das er nach eigener Auskunft können müsste. Heraus kam „Dieser Browser kann die Spuren dieser Datei nicht öffnen". Jetzt zählt ein Codec nur, wenn **beide** Auskünfte ihn bestätigen; welcher der beiden Wege es wird, entscheidet der Server erst danach. Und geprüft wird jeder Typ in der Form, in der er wirklich ankommt — `audio/mp4` und nicht `audio/flac`: genau dieser Unterschied entschied bei FLAC zwischen „geht" und „geht nicht". Gemessen: MP3 fällt dadurch aus dem Profil, der Server rechnet es um, statt es durchzureichen. **Und wenn doch etwas durchrutscht, wird gerechnet statt gemeldet.** Scheitert die Wiedergabe mit einem Codec- oder Dekodierfehler, baut der Spieler den Strom genau einmal neu auf — mit `transcode: true`, an derselben Stelle. Der Satz „über das Zahnrad lässt sich eine Fassung wählen, die er versteht" ist weg: wer das liest, weiss weder welche, noch warum der Server sie nicht gleich gegeben hat. Lief schon ein gerechneter Strom, unterbleibt der zweite Versuch — dann liegt es nicht am Codec, und dreissig Sekunden Warten auf dasselbe sind keine Hilfe. **Nicht geprüft:** dass der Rückfall im Betrieb auslöst. Jede Datei im Prüfbestand spielt; ein echtes Scheitern liess sich hier nicht herbeiführen. Gemessen ist der Serverteil (`remux` wird zu `transcode`) und das neue Profil. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e30158b84c
commit
9c2e2b5f47
@@ -610,7 +610,18 @@ public sealed record StreamRequest(
|
||||
IReadOnlyList<string>? AudioCodecs,
|
||||
int? Height = null,
|
||||
int? AudioStream = null,
|
||||
double? StartSeconds = null);
|
||||
double? StartSeconds = null,
|
||||
|
||||
/// <summary>Umkodieren, egal was das Profil sagt.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Für den zweiten Versuch.</b> Der Spieler schickt sein Profil aus
|
||||
/// <c>canPlayType</c> — und das ist eine Behauptung: „kenne ich" heisst
|
||||
/// nicht „kann ich diese Datei". Scheitert die Wiedergabe trotzdem, weiss
|
||||
/// der Browser mehr als jede Abfrage vorher, und sein Urteil schlägt
|
||||
/// seine eigene Auskunft.
|
||||
/// </remarks>
|
||||
bool Transcode = false);
|
||||
|
||||
/// <summary>Eine wählbare Spur, so wie sie im Menü steht.</summary>
|
||||
public sealed record SpurAuskunft(int Index, string Label, string? Language, bool IsDefault);
|
||||
|
||||
@@ -61,14 +61,20 @@ public static class StreamEndpoints
|
||||
// **Nur ohne Wünsche.** Wer eine Tonspur oder eine Stufe
|
||||
// wählt, bekommt eine Sitzung: beides lässt sich an einer
|
||||
// durchgereichten Datei nicht einstellen.
|
||||
if (hoehe is null && anfrage.AudioStream is null
|
||||
//
|
||||
// **Und nicht, wenn der Spieler schon gescheitert ist.** Dann
|
||||
// hat er die Datei gesehen und nicht geöffnet; sie ihm ein
|
||||
// zweites Mal unverändert hinzulegen wäre derselbe Fehler mit
|
||||
// mehr Geduld.
|
||||
if (hoehe is null && anfrage.AudioStream is null && !anfrage.Transcode
|
||||
&& await Durchreichbar(db, id, profil) is { } datei)
|
||||
{
|
||||
return Results.Ok(await Direktauskunft(db, datei, anfrage.StartSeconds ?? 0));
|
||||
}
|
||||
|
||||
var sitzung = await koordinator.StartenAsync(
|
||||
id, benutzer, profil, hoehe, anfrage.AudioStream, anfrage.StartSeconds ?? 0);
|
||||
id, benutzer, profil, hoehe, anfrage.AudioStream, anfrage.StartSeconds ?? 0,
|
||||
erzwingen: anfrage.Transcode);
|
||||
|
||||
return Results.Ok(await Auskunft(db, sitzung));
|
||||
}
|
||||
|
||||
@@ -93,9 +93,14 @@ public sealed class Streamkoordinator(
|
||||
public Stromsitzung? Sitzung(Guid kennung) => _sitzungen.GetValueOrDefault(kennung)?.Sitzung;
|
||||
|
||||
/// <summary>Startet einen Strom und gibt die Sitzung zurück.</summary>
|
||||
/// <param name="erzwingen">
|
||||
/// Umkodieren, auch wenn das Profil etwas anderes sagt — der Spieler hat
|
||||
/// es mit dem ersten Plan probiert und ist gescheitert.
|
||||
/// </param>
|
||||
public async Task<Stromsitzung> StartenAsync(
|
||||
Guid dateiId, Guid benutzer, DeviceProfile profil,
|
||||
int? hoehe, int? tonspur, double ab, CancellationToken ct = default)
|
||||
int? hoehe, int? tonspur, double ab, CancellationToken ct = default,
|
||||
bool erzwingen = false)
|
||||
{
|
||||
AbgelaufeneWegraeumen();
|
||||
|
||||
@@ -123,7 +128,7 @@ public sealed class Streamkoordinator(
|
||||
s.Index, s.Kind, s.Codec, s.Language, s.Title,
|
||||
s.IsDefault, s.IsForced, s.Channels, s.Width, s.Height)).ToList();
|
||||
|
||||
var plan = Stromentscheid.Fuer(datei.Container, gelesen, profil, hoehe);
|
||||
var plan = Stromentscheid.Fuer(datei.Container, gelesen, profil, hoehe, erzwingen);
|
||||
|
||||
var kennung = Guid.CreateVersion7();
|
||||
var ordner = Path.Combine(pfade.Transcode, "stroeme", kennung.ToString("N"));
|
||||
|
||||
Reference in New Issue
Block a user