Zwei Tonspuren sind eine Wahl, kein Chor
„Auf der App höre ich einfach alle Audiospuren gleichzeitig."
MP4 ordnet Spuren derselben Art einer `alternate_group` zu, und ein
Abspieler nimmt daraus genau eine. ffmpeg leitet diese Gruppe aus der
Markierung der Quelle ab — sind dort zwei Tonspuren als Vorgabe markiert,
hält es sie für Verschiedenes und legt sie in getrennte Gruppen. Dann ist
jede für sich eingeschaltet, und man hört beide übereinander. Bei Rips aus
freier Wildbahn ist die doppelte Markierung eher Regel als Ausnahme; in
Matroska stört sie niemanden, in MP4 ist sie der Unterschied zwischen Wahl
und Chor.
Gemessen an einer Quelle mit zwei markierten Tonspuren, am Spurkopf der
fertigen MP4:
vorher Spur 2: an, Gruppe 1
Spur 3: an, Gruppe 2 <- beide laufen
nachher Spur 2: an, Gruppe 1
Spur 3: aus, Gruppe 1 <- eine zur Wahl
Das Umpacken schreibt jetzt genau eine Vorgabe je Spurart: die, die es in
der Quelle schon war, sonst die erste. Auch die Nullen gehören
hingeschrieben — ffmpeg übernähme sonst die Markierung der Quelle für alle
übrigen, und genau die ist ja das Problem. „Erzwungen" wird dabei
ausdrücklich erhalten, weil `-disposition` die ganze Markierung ersetzt
statt sie zu ergänzen.
Untertitel bekommen dieselbe Behandlung. War in der Quelle keiner
vorgesehen, bleibt es dabei — sonst erschienen auf einmal zwei übereinander.
Der Name im Zwischenspeicher trägt jetzt die Bauart mit. Ohne das bliebe
auf einem NAS, das schon umgepackt hat, die fehlerhafte Datei liegen und
würde weiter ausgeliefert, obwohl der Server es längst besser kann.
Geprüft an der echten Datei, am `tkhd` und nicht an der Disposition: was
ein Abspieler tut, hängt an „enabled" und `alternate_group`, und ffprobe
zeigt davon nur einen Teil. Ohne die Korrektur fällt die Prüfung um
(Gruppe 1 gegen 2).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1531163bc1
commit
40f41f0cee
@@ -118,4 +118,84 @@ public class RemuxArgumentsTests
|
||||
Assert.That(argumente[argumente.IndexOf("-i") + 1], Is.EqualTo("/medien/film.mkv"));
|
||||
Assert.That(argumente[^1], Is.EqualTo("/kratz/film.mp4"), "Das Ziel steht bei ffmpeg immer zuletzt");
|
||||
}
|
||||
|
||||
/// <summary>Genau eine Tonspur ist die Vorgabe — sonst laufen alle zugleich.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Gemeldet als „ich höre einfach alle Audiospuren gleichzeitig".</b>
|
||||
/// MP4 ordnet Spuren derselben Art einer <c>alternate_group</c> zu, und
|
||||
/// ein Abspieler nimmt daraus genau eine. ffmpeg leitet diese Gruppe aus
|
||||
/// der Markierung der Quelle ab — sind dort zwei Tonspuren als Vorgabe
|
||||
/// markiert, legt es sie in getrennte Gruppen, und beide laufen.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Nur_eine_Tonspur_bleibt_Vorgabe()
|
||||
{
|
||||
var zwei = new List<ProbedStream>
|
||||
{
|
||||
Bild(),
|
||||
Ton(1) with { IsDefault = true },
|
||||
Ton(2) with { IsDefault = true },
|
||||
};
|
||||
|
||||
var zeile = AlsZeile(Bauen(zwei));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(zeile, Does.Contain("-disposition:a:0 default"));
|
||||
Assert.That(zeile, Does.Contain("-disposition:a:1 0"),
|
||||
"auch die Null gehört hingeschrieben: ffmpeg übernähme sonst die Markierung der Quelle");
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Die Vorgabe der Quelle bleibt die Vorgabe.</summary>
|
||||
[Test]
|
||||
public void Die_schon_vorgegebene_Spur_bleibt_es()
|
||||
{
|
||||
var zeile = AlsZeile(Bauen([Bild(), Ton(1), Ton(2) with { IsDefault = true }]));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(zeile, Does.Contain("-disposition:a:0 0"));
|
||||
Assert.That(zeile, Does.Contain("-disposition:a:1 default"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>„Erzwungen" überlebt die Behandlung.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <c>-disposition</c> ersetzt die ganze Markierung, statt sie zu
|
||||
/// ergänzen. Wer hier nur „default" schreibt, nimmt einer erzwungenen
|
||||
/// Untertitelspur genau das, was sie ausmacht.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Erzwungene_Untertitel_bleiben_erzwungen()
|
||||
{
|
||||
var zeile = AlsZeile(Bauen(
|
||||
[Bild(), Ton(1), Text(2) with { IsForced = true }, Text(3)]));
|
||||
|
||||
Assert.That(zeile, Does.Contain("-disposition:s:0 forced"));
|
||||
}
|
||||
|
||||
/// <summary>Ohne Vorgabe in der Quelle erscheint auch kein Untertitel von selbst.</summary>
|
||||
[Test]
|
||||
public void Untertitel_ohne_Vorgabe_bekommen_keine()
|
||||
{
|
||||
var zeile = AlsZeile(Bauen([Bild(), Ton(1), Text(2), Text(3)]));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(zeile, Does.Contain("-disposition:s:0 0"));
|
||||
Assert.That(zeile, Does.Not.Contain("-disposition:s:0 default"));
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>Bei einer einzigen Spur gibt es nichts zu entscheiden.</summary>
|
||||
[Test]
|
||||
public void Eine_einzige_Tonspur_wird_nicht_angefasst()
|
||||
{
|
||||
var zeile = AlsZeile(Bauen([Bild(), Ton(1)]));
|
||||
|
||||
Assert.That(zeile, Does.Not.Contain("-disposition:a"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user