
Estou planejando renderizar um projeto pessoal em 3D como uma sequência de imagens, onde cada quadro leva cerca de um minuto para ser criado e salvo como um arquivo de imagem.
Se eu usasse a sintaxe ffmpeg regular para converter esta sequência de imagens em um arquivo de vídeo ( ffmpeg -i image-%03d.png output.mp4
) antes da conclusão da renderização, ela irá parar como esperado quando atingir o último arquivo de imagem. Mas como a lista de arquivos de imagem está crescendo com o tempo, usando essa sintaxe eu precisaria esperar até que o projeto terminasse a renderização para converter tudo em um arquivo de vídeo, o que vai demorar um pouco.
Fiquei curioso para saber se é possível deixar o ffmpeg"espere"para os próximos arquivos da sequência, e definindo um "último quadro" (ou cancelando usando Ctrl+C) ele saberia parar de esperar por mais arquivos e finalizar o arquivo de vídeo?
Considerei alimentar uma espécie de fluxo de vídeo como entrada, que seria gerado usando os arquivos de imagem e um programa externo, mas não tenho certeza se isso funcionaria e uma abordagem geral.
Responder1
Graças ao slhck, consegui criar um script C# que passa continuamente arquivos para o stdin do ffmpeg à medida que são criados, que pode ser cancelado pressionando qualquer tecla ou quando o quadro final especificado for atingido:
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();
}
}
}
Parece que o image2pipe
formato não suporta TGA, mas funciona bem com PNG, por exemplo.