using System.Diagnostics; namespace Flaro.Media.Tests; /// Baut echte Mediendateien mit ffmpeg. /// /// /// Echte Dateien und keine aufgezeichneten Antworten. Bei TMDB ist das /// umgekehrt richtig — dort steht ein fremder Dienst dahinter. Hier geht es /// um das Werkzeug, das Flaro im Container ohnehin mitbringt, und die Frage /// „liest er die zweite Tonspur mitsamt Sprache?" lässt sich nur an einer /// Datei beantworten, die wirklich zwei hat. /// public static class Testdateien { public static bool FfmpegVorhanden => Suchen("ffmpeg") is not null && Suchen("ffprobe") is not null; public static string? Suchen(string name) { foreach (var ordner in (Environment.GetEnvironmentVariable("PATH") ?? "").Split(Path.PathSeparator)) { if (ordner.Length == 0) continue; var pfad = Path.Combine(ordner, name); if (File.Exists(pfad)) return pfad; } return null; } /// Zwei Tonspuren mit Sprache und Namen, dazu ein Untertitel. public static void MitZweiTonspuren(string ziel) { var untertitel = Path.ChangeExtension(ziel, ".srt"); File.WriteAllText(untertitel, "1\n00:00:00,000 --> 00:00:01,000\nEin Untertitel\n\n"); Ausführen("ffmpeg", [ "-y", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc=duration=1:size=160x120:rate=10", "-f", "lavfi", "-i", "sine=frequency=440:duration=1", "-f", "lavfi", "-i", "sine=frequency=880:duration=1", "-i", untertitel, "-map", "0:v", "-map", "1:a", "-map", "2:a", "-map", "3:s", "-metadata:s:a:0", "language=deu", "-metadata:s:a:0", "title=Deutsch", "-metadata:s:a:1", "language=deu", "-metadata:s:a:1", "title=Audiodeskription", "-metadata:s:s:0", "language=deu", "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", "-c:s", "srt", ziel, ]); File.Delete(untertitel); } /// Zwei Tonspuren, und beide sind als Vorgabe markiert. /// /// /// Keine Schikane, sondern der Normalfall. Wer eine Datei aus /// mehreren Quellen zusammenmuxt, lässt die Markierung gern auf jeder /// stehen. In Matroska stört das niemanden; in MP4 heisst es, dass ein /// Abspieler beide Spuren für etwas Eigenes hält und gleichzeitig /// abspielt. /// public static void MitZweiVorgabetonspuren(string ziel) => Ausführen("ffmpeg", [ "-y", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc=duration=1:size=160x120:rate=10", "-f", "lavfi", "-i", "sine=frequency=440:duration=1", "-f", "lavfi", "-i", "sine=frequency=880:duration=1", "-map", "0:v", "-map", "1:a", "-map", "2:a", "-metadata:s:a:0", "language=deu", "-metadata:s:a:1", "language=eng", "-disposition:a:0", "default", "-disposition:a:1", "default", "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", ziel, ]); /// Eine schlichte MP4 mit einer Tonspur. public static void Einfach(string ziel) => Ausführen("ffmpeg", [ "-y", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc=duration=1:size=160x120:rate=10", "-f", "lavfi", "-i", "sine=frequency=440:duration=1", "-c:v", "libx264", "-preset", "ultrafast", "-c:a", "aac", ziel, ]); /// Eine MKV mit einer Tonspur in einem Codec, den Apple nicht kennt. /// /// /// DTS, also der echte Fall. So liegen Blu-ray-Rips vor, und genau /// daran entscheidet sich, ob eine sonst tadellose Datei stumm bliebe. /// Der Encoder gilt bei ffmpeg als erprobungsweise und braucht deshalb /// -strict -2 — das betrifft nur das Erzeugen dieser Testvorlage, /// nicht Flaro. /// public static void MitUntauglichemTon(string ziel) => Ausführen("ffmpeg", [ "-y", "-loglevel", "error", "-f", "lavfi", "-i", "testsrc=duration=1:size=160x120:rate=10", "-f", "lavfi", "-i", "sine=frequency=440:duration=1", "-f", "lavfi", "-i", "sine=frequency=880:duration=1", "-map", "0:v", "-map", "1:a", "-map", "2:a", "-metadata:s:a:0", "language=deu", "-metadata:s:a:0", "title=Deutsch", "-metadata:s:a:1", "language=eng", "-metadata:s:a:1", "title=English", "-c:v", "libx264", "-preset", "ultrafast", "-c:a:0", "aac", "-c:a:1", "dca", "-strict", "-2", ziel, ]); private static void Ausführen(string werkzeug, string[] argumente) { var start = new ProcessStartInfo(Suchen(werkzeug)!) { RedirectStandardError = true, UseShellExecute = false }; foreach (var a in argumente) start.ArgumentList.Add(a); using var prozess = Process.Start(start)!; var fehler = prozess.StandardError.ReadToEnd(); prozess.WaitForExit(); if (prozess.ExitCode != 0) throw new InvalidOperationException($"{werkzeug} scheiterte: {fehler}"); } }