
Ich plane, ein persönliches 3D-Projekt als Bildsequenz zu rendern, wobei es etwa eine Minute dauert, jedes Einzelbild zu erstellen und als Bilddatei zu speichern.
Wenn ich die normale ffmpeg-Syntax verwenden würde, um diese Bildsequenz in eine Videodatei umzuwandeln ( ffmpeg -i image-%03d.png output.mp4
), bevor das Rendern abgeschlossen ist, würde es wie erwartet anhalten, sobald die letzte Bilddatei erreicht ist. Da die Liste der Bilddateien jedoch mit der Zeit wächst, müsste ich bei dieser Syntax warten, bis das Projekt mit dem Rendern fertig ist, um das Ganze in eine Videodatei umzuwandeln, was ziemlich lange dauern wird.
Ich war neugierig, ob es möglich ist, ffmpeg"Warten"für die nächsten Dateien in der Sequenz und durch die Definition eines „letzten Frames“ (oder durch Abbrechen mit Strg+C) wüsste es, dass es nicht mehr auf weitere Dateien warten und die Videodatei fertigstellen soll?
Ich habe überlegt, als Eingabe eine Art Videostream einzuspeisen, der mithilfe der Bilddateien und eines externen Programms generiert würde, bin mir aber nicht sicher, ob das funktionieren würde, und was ein allgemeiner Ansatz ist.
Antwort1
Dank slhck ist es mir gelungen, ein C#-Skript zu entwickeln, das Dateien beim Erstellen kontinuierlich an die Standardeingabe von ffmpeg übergibt. Dieses Skript kann durch Drücken einer beliebigen Taste oder beim Erreichen des angegebenen End-Frames abgebrochen werden:
static void Main()
{
//Async main method
AsyncMain().GetAwaiter().GetResult();
}
static async Task AsyncMain()
{
Console.WriteLine("Press any key to quit prematurely.");
var maintask = RunFFMPEG();
var readtask = Task.Run(() => Console.Read());
await Task.WhenAny(maintask, readtask);
}
static async Task RunFFMPEG()
{
await Task.Run(() =>
{
const int fps = 30;
const string outfile = "out.mp4";
const string args = "-y -framerate {0} -f image2pipe -i - -r {0} -c:v libx264 -movflags +faststart -pix_fmt yuv420p -crf 19 -preset veryslow {1}";
const string dir = @"C:\testrender\";
const string pattern = "{0}.png";
const string path = dir + pattern;
const int startNum = 0;
const int endNum = 100;
var pinf = new ProcessStartInfo("ffmpeg", string.Format(args, fps, outfile));
pinf.UseShellExecute = false;
pinf.RedirectStandardInput = true;
pinf.WorkingDirectory = dir;
Console.WriteLine("Starting ffmpeg...");
var proc = Process.Start(pinf);
using (var stream = new BinaryWriter(proc.StandardInput.BaseStream))
{
for (var i = startNum; i < endNum; i++)
{
//"D4" turns 5 to 0005 - change depending on pattern of input files
var file = string.Format(path, i.ToString("D4"));
System.Threading.SpinWait.SpinUntil(() => File.Exists(file) && CanReadFile(file));
Console.WriteLine("Found file: " + file);
stream.Write(File.ReadAllBytes(file));
}
}
proc.WaitForExit();
Console.WriteLine("Closed ffmpeg.");
});
bool CanReadFile(string file)
{
//Needs to be able to read file
FileStream fs = null;
try
{
fs = File.OpenRead(file);
return true;
}
catch (IOException)
{
return false;
}
finally
{
if (fs != null)
fs.Close();
}
}
}
Es scheint, dass das image2pipe
Format TGA nicht unterstützt, aber beispielsweise mit PNG problemlos funktioniert.