OpenFunscripter / OFS

A tool to create funscripts
GNU General Public License v3.0
90 stars 39 forks source link

OFS stall when generating waveform (on a long video) #26

Closed Zalunda closed 2 years ago

Zalunda commented 2 years ago

When I open a long video (ex. 1h) and choose "Waveform\Update waveform", this is happening:

This is a pattern I saw in the past when a parent process does not "empty" the standard out / error stream from the child process continuously. The buffer between the processes gets filled and the child gets "stuck" forever because it can't write to his standard out / error.

It usually works fine as long as the text written is less than the buffer size (I dunno the size). This would explain why "Update waveform" works fine with a smaller video.

To test my theory, I replaced the "ffmpeg.exe" with a small application that calls the real ffmpeg.exe with the argument "-loglevel quiet". With that "patch", "Update waveform" always works.

OpenFunscripter commented 2 years ago

What platform are we talking here? I've never had that happen on windows 🤔

But I added your fix

Zalunda commented 2 years ago

Thanks for the fix.

I'm on Windows too.

I did more tests with my 'fake-ffmpeg' application (source below).

On my machine, if I write more than 4096 bytes to standard out or standard error, OFS stall and the ffmpeg process is stuck (see code after the 'return').

I also wrote what the real ffmpeg send to my fake-ffmpeg. After sending a lot of info on the input and output stream, it send an update every 0.5 seconds (ex. "size= 3072kB time=00:00:54.24 bitrate= 464.0kbits/s speed= 108x"). For me, it reached 4096 bytes after about 15 seconds of decoding.

So, it might depend on the time it takes to extract the audio (if CPU is used by another process => take more time). And you probably have a faster machine than me. ;)

using System;
using System.Diagnostics;
using System.IO;

namespace FakeFfmpeg
{
    class Program
    {
        static void Main(string[] args)
        {
            Environment.CurrentDirectory = Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
                @"OFS\OFS_data");

            using (var writer = File.CreateText("test.log"))
            {
                try
                {
                    writer.AutoFlush = true;

                    using (var writerOut = File.CreateText("test-out.log"))
                    using (var writerError = File.CreateText("test-error.log"))
                    {
                        writerOut.AutoFlush = true;
                        writerError.AutoFlush = true;

                        var outReceived = 0;
                        var errorReceived = 0;

                        var process = new Process();
                        process.StartInfo.FileName = "ffmpeg-original.exe";
                        // process.StartInfo.Arguments = "-loglevel quiet" + Environment.CommandLine.Substring(Environment.CommandLine.IndexOf("ffmpeg.exe") + 11);
                        process.StartInfo.Arguments = Environment.CommandLine.Substring(Environment.CommandLine.IndexOf("ffmpeg.exe") + 11);
                        process.StartInfo.UseShellExecute = false;
                        process.StartInfo.RedirectStandardOutput = true;
                        process.StartInfo.RedirectStandardError = true;
                        var stopwatch = Stopwatch.StartNew();
                        process.OutputDataReceived += (sender, e) =>
                        {
                            outReceived += e.Data.Length;
                            writer.WriteLine($"{stopwatch.Elapsed}: {outReceived} received in OUT (+{e.Data.Length})");
                            writerOut.WriteLine(e.Data);
                        };
                        process.ErrorDataReceived += (sender, e) =>
                        {
                            errorReceived += e.Data.Length;
                            writer.WriteLine($"{stopwatch.Elapsed}: {errorReceived} received in ERROR (+{e.Data.Length})");
                            writerError.WriteLine(e.Data);
                        };
                        process.Start();
                        process.BeginOutputReadLine();
                        process.BeginErrorReadLine();
                        process.WaitForExit();
                    }

                    return;

                    var nbCharactersOut = 4000;
                    var nbCharactersError = 10000;
                    for (int i = 0; i < nbCharactersOut; i++)
                    {
                        if (i % 10 == 0)
                        {
                            writer.WriteLine($"{i} written to OUT");
                        }
                        Console.Out.Write("X");
                    }
                    writer.WriteLine($"{nbCharactersOut} written to OUT");
                    for (int i = 0; i < nbCharactersError; i++)
                    {
                        if (i % 10 == 0)
                        {
                            writer.WriteLine($"{i} written to Error");
                        }
                        Console.Error.Write("Y");
                    }
                    writer.WriteLine($"{nbCharactersError} written to Error");
                }
                catch (Exception ex)
                {
                    writer.WriteLine(ex.ToString());
                }
            }
        }
    }
}