0

I am planning to render a personal 3d project as an image sequence, where each frame takes about a minute to be created and saved as an image file.

If I were to use the regular ffmpeg syntax to convert this image sequence to a video file (ffmpeg -i image-%03d.png output.mp4) before the render has finished, it will as expected stop once it reached the last image file. But as the list of image files is growing over time, using this syntax I would need to wait until the project is done rendering in order to convert the entire thing to a video file, which will take quite a while.

I was curious if it is possible to let ffmpeg "wait" for the next files in the sequence, and by defining a "last frame" (or by cancelling using Ctrl+C) it would know to stop waiting for more files and finish the video file?

I have considered feeding a video stream of sorts as the input, which would be generated using the image files and an external program, but I am unsure about if this would work and a general approach.

3
  • 1
    You can also stream input to ffmpeg via a pipe (see, e.g., here). If you can export to stdin of a process, and use ffmpeg's image2pipe, this should work.
    – slhck
    Commented Oct 7, 2017 at 18:07
  • @slhck Thank you, this is exactly what I needed! If I block the thread writing to stdin while that's waiting for files, ffmpeg will also wait, and it instantly processes the files as it receives them.
    – RedMser
    Commented Oct 7, 2017 at 19:06
  • Great, glad it worked!
    – slhck
    Commented Oct 7, 2017 at 21:41

1 Answer 1

4

Thanks to slhck, I managed to come up with a C# script which continuously passes files to the stdin of ffmpeg as they get created, which can be cancelled by pressing any key or when the specified end frame is reached:

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();
        }
    }
}

It seems like the image2pipe format does not support TGA, but works just fine with PNG for example.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .