using Flaro.Core.Delivery;
using Flaro.Core.Library;
namespace Flaro.Media.Tests;
/// Die ffmpeg-Zeile, betrachtet ohne sie auszuführen.
public class RemuxArgumentsTests
{
private static ProbedStream Bild() => new(0, MediaStreamKind.Video, "h264", null, null, true, false, null, 1920, 1080);
private static ProbedStream Ton(int i, string codec = "aac", int kanäle = 6) =>
new(i, MediaStreamKind.Audio, codec, "deu", null, false, false, kanäle, null, null);
private static ProbedStream Text(int i, string codec = "subrip") =>
new(i, MediaStreamKind.Subtitle, codec, "deu", null, false, false, null, null, null);
private static List Bauen(IReadOnlyList spuren, string schachtel = "mkv")
{
var plan = DeliveryDecider.ForDownload(schachtel, spuren);
return RemuxArguments.For(new RemuxRequest("/medien/film.mkv", "/kratz/film.mp4", spuren, plan));
}
private static string AlsZeile(List argumente) => string.Join(' ', argumente);
/// Jede Spur wird einzeln benannt — und zwar alle.
[Test]
public void Alle_Spuren_werden_abgebildet()
{
var zeile = AlsZeile(Bauen([Bild(), Ton(1), Ton(2), Text(3)]));
Assert.That(zeile, Does.Contain("-map 0:0"));
Assert.That(zeile, Does.Contain("-map 0:1"));
Assert.That(zeile, Does.Contain("-map 0:2"));
Assert.That(zeile, Does.Contain("-map 0:3"));
}
[Test]
public void Bild_und_Ton_werden_kopiert_nicht_gerechnet()
{
var zeile = AlsZeile(Bauen([Bild(), Ton(1), Ton(2)]));
Assert.That(zeile, Does.Contain("-c copy"));
Assert.That(zeile, Does.Not.Contain("-c:a:"), "Nichts davon muss neu gerechnet werden");
Assert.That(zeile, Does.Not.Contain("libx264"), "Das Bild wird nie angefasst");
}
[Test]
public void Textuntertitel_werden_nach_tx3g_umgeschrieben()
{
Assert.That(AlsZeile(Bauen([Bild(), Ton(1), Text(2)])), Does.Contain("-c:s mov_text"));
}
[Test]
public void Ohne_Untertitel_steht_auch_keine_Untertitel_Anweisung_da()
{
Assert.That(AlsZeile(Bauen([Bild(), Ton(1)])), Does.Not.Contain("-c:s"));
}
[Test]
public void Bilduntertitel_werden_gar_nicht_erst_abgebildet()
{
var zeile = AlsZeile(Bauen([Bild(), Ton(1), Text(2, "hdmv_pgs_subtitle"), Text(3, "subrip")]));
Assert.That(zeile, Does.Not.Contain("-map 0:2"), "PGS passt in keine MP4");
Assert.That(zeile, Does.Contain("-map 0:3"));
}
/// Die Verwechslung, vor der der Kommentar an MediaStream.Index warnt.
///
///
/// -c:a:1 meint die zweite Tonspur der Ausgabe, nicht die Spur
/// Nummer 1 der Datei. Hier steht DTS an Dateinummer 2 und ist die
/// zweite Tonspur — also -c:a:1. Wer die Dateinummer einsetzte,
/// träfe eine andere Spur oder gar keine.
///
[Test]
public void Neu_gerechnet_wird_nach_der_Nummer_der_Ausgabe()
{
var zeile = AlsZeile(Bauen([Bild(), Ton(1, "aac"), Ton(2, "dts"), Ton(3, "aac")]));
Assert.That(zeile, Does.Contain("-c:a:1 aac"));
Assert.That(zeile, Does.Not.Contain("-c:a:0"), "Die erste Tonspur passt schon");
Assert.That(zeile, Does.Not.Contain("-c:a:2"), "Die dritte auch");
}
/// Fällt ein Untertitel weg, verschieben sich die Tonnummern nicht.
///
///
/// Tonspuren werden unter sich gezählt — ein weggelassener Untertitel
/// ändert daran nichts. Die Prüfung steht hier, weil der umgekehrte Fehler
/// naheliegt und erst an einer stummen Spur auffiele.
///
[Test]
public void Ein_weggelassener_Untertitel_verschiebt_die_Tonnummern_nicht()
{
var zeile = AlsZeile(Bauen([Bild(), Text(1, "hdmv_pgs_subtitle"), Ton(2, "aac"), Ton(3, "dts")]));
Assert.That(zeile, Does.Contain("-c:a:1 aac"));
}
[Test]
public void Mehr_Kanaele_bekommen_mehr_Bitrate()
{
Assert.That(AlsZeile(Bauen([Bild(), Ton(1, "dts", kanäle: 2)])), Does.Contain("-b:a:0 192000"));
Assert.That(AlsZeile(Bauen([Bild(), Ton(1, "dts", kanäle: 6)])), Does.Contain("-b:a:0 576000"));
Assert.That(AlsZeile(Bauen([Bild(), Ton(1, "dts", kanäle: 8)])), Does.Contain("-b:a:0 640000"),
"Gedeckelt — acht Kanäle bekämen sonst mehr, als irgendein Gerät braucht");
}
[Test]
public void Das_Inhaltsverzeichnis_wandert_nach_vorn()
{
Assert.That(AlsZeile(Bauen([Bild(), Ton(1)])), Does.Contain("-movflags +faststart"));
}
[Test]
public void Quelle_und_Ziel_stehen_an_der_richtigen_Stelle()
{
var argumente = Bauen([Bild(), Ton(1)]);
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");
}
/// Genau eine Tonspur ist die Vorgabe — sonst laufen alle zugleich.
///
///
/// Gemeldet als „ich höre 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, legt es sie in getrennte Gruppen, und beide laufen.
///
[Test]
public void Nur_eine_Tonspur_bleibt_Vorgabe()
{
var zwei = new List
{
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");
});
}
/// Die Vorgabe der Quelle bleibt die Vorgabe.
[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"));
});
}
/// „Erzwungen" überlebt die Behandlung.
///
///
/// -disposition ersetzt die ganze Markierung, statt sie zu
/// ergänzen. Wer hier nur „default" schreibt, nimmt einer erzwungenen
/// Untertitelspur genau das, was sie ausmacht.
///
[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"));
}
/// Ohne Vorgabe in der Quelle erscheint auch kein Untertitel von selbst.
[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"));
});
}
/// Bei einer einzigen Spur gibt es nichts zu entscheiden.
[Test]
public void Eine_einzige_Tonspur_wird_nicht_angefasst()
{
var zeile = AlsZeile(Bauen([Bild(), Ton(1)]));
Assert.That(zeile, Does.Not.Contain("-disposition:a"));
}
}