
Я планирую визуализировать персональный 3D-проект в виде последовательности изображений, где создание каждого кадра и сохранение его в виде файла изображения занимает около минуты.
Если бы я использовал обычный синтаксис ffmpeg для преобразования этой последовательности изображений в видеофайл ( ffmpeg -i image-%03d.png output.mp4
) до завершения рендеринга, он, как и ожидалось, остановился бы, как только достигнет последнего файла изображения. Но поскольку список файлов изображений со временем растет, используя этот синтаксис, мне пришлось бы ждать, пока проект не завершит рендеринг, чтобы преобразовать все это в видеофайл, что займет довольно много времени.
Мне было интересно, можно ли разрешить ffmpeg"ждать"для следующих файлов в последовательности, и определив «последний кадр» (или отменив с помощью Ctrl+C), он поймет, что нужно прекратить ждать следующих файлов и завершить видеофайл?
Я рассматривал возможность подачи в качестве входных данных видеопотока, который будет генерироваться с использованием файлов изображений и внешней программы, но я не уверен, будет ли это работать и будет ли это общим подходом.
решение1
Благодаря slhck мне удалось создать скрипт на C#, который непрерывно передает файлы на стандартный ввод ffmpeg по мере их создания, что можно отменить нажатием любой клавиши или при достижении указанного конечного кадра:
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();
}
}
}
Похоже, что этот image2pipe
формат не поддерживает TGA, но прекрасно работает, например, с PNG.