0

I am generating a large collection MP4 videos from an OpenGL source. This has worked perfectly so far but I recently noticed that some TV media player don't like video without audio, see here for more info.

So far i hve been using the following code:

    private bool StartRecording()
    {
        _ffmpeg = new Process();
        _ffmpeg.StartInfo.FileName = "ffmpeg.exe";
        _ffmpeg.StartInfo.Arguments = $"-f rawvideo -pix_fmt rgba -s {RecordSize.Width}x{RecordSize.Height} -r 30 -i - -c libx264 -crf 17 -pix_fmt yuv420p -b:v 1024k -bufsize:v 1835008 {RecordFile} -tune animation";
        _ffmpeg.StartInfo.RedirectStandardInput = true;
        _ffmpeg.Start();

        if(_ffmpeg.HasExited)
        {
            IsRecording = false;
            return false;
        }
        return true;
    }

    private void RecordFrame(RenderUpdate update)
    {
        if (_ffmpeg == null)
            return;

        var frame = Project.RecordFrame(update);
        var data = new byte[frame.Length];
        var height = RecordSize.Height;
        var width = RecordSize.Width;

        for (var k = 0; k < height; k++)
        {
            var j = height - k - 1;
            Buffer.BlockCopy(
                frame, k * width * 4,
                data, j * width * 4,
                width * 4);
        }

        _ffmpeg.StandardInput.BaseStream.Write(data, 0, data.Length);
    }

    private void StopRecording()
    {
        Project.StopRecording();
        if (_ffmpeg == null)
            return;

        _ffmpeg.StandardInput.Flush();
        _ffmpeg.StandardInput.Close();
        _ffmpeg.Close();
        _ffmpeg.Dispose();
    }

And it has worked perfectly until I modified the 'ffmpeg' cmd line arguments to include '-i anullsrc' for the audio.

  _ffmpeg.StartInfo.Arguments = $"-f rawvideo -pix_fmt rgba -s {RecordSize.Width}x{RecordSize.Height} -r 30 -i - -f lavfi -i anullsrc=channel_layout=mono:sample_rate=8k -c:v libx264 -crf 17 -pix_fmt yuv420p -b:v 1024k -bufsize:v 1835008 {RecordFile} -tune animation";

The moment I add '-f lavfi -i anullsrc' to the cmd line the call to 'StopRecording()' has no effect and ffmpeg continues to run.

I can manually stop it by closing the console window or even using ctrl-c, but the job needs to be automated so this is just not an option.

Ultimately my question is Why is ffmpeg behaving this way?

I've come up with a couple of work arounds, but I don't like either:

  1. Post process the videos as per the link I mentioned earlier. Not practical. I need to generate 1000s of videos and a single batch of 350 takes around 6 hours. 50% of that time is consumed by ffmpeg encoding, so to re-encode them all just to add a null audio track would nearly double the time.
  2. Try to push a 'ctrl-c' through to the ffmpeg process as per this post. I did some quick tests with 'GenerateConsoleCtrlEvent' without sucess, and the idea of having to manually start and stop the Process via 'kernel32.dll' seems excessive not to mention old school.

Any insight would be greatly appreciated.

Rabbex
  • 141
  • 1
  • 7
  • add `-shortest` output option so it stops when your primary input ends. All source filters (except for `movie`/`amovie`) run indefinitely unless you specify a positive `duration` option (if the option is avail). – kesh Oct 01 '22 at 17:09
  • Thanks kesh but that had no effect. Because the primarary input is StandardInput.BaseStream and not a file it has no end. I can stop sending it data but without a way to indicate the job is finished it just sits and waits. This was never an issue until I included the null audio because calling Close() on the process did the job. – Rabbex Oct 01 '22 at 17:36
  • 1
    What I do in Python is to close the process' `stdin` to signal FFmpeg to start shutting down its operation. You then just wait till the process closes. This works, and it shouldn't be different in C#. – kesh Oct 01 '22 at 17:42
  • -kesh, you have given me another idea though, because I know in advance the absolute number of frames and the frame rate I should be able to specify a duration. I'll give it a try tommorow. Cheers – Rabbex Oct 01 '22 at 17:45
  • If you look at the StopRecording() method that is exactly what I have done. Again it worked perfectly until I included the null audio. – Rabbex Oct 01 '22 at 17:48
  • my bad. you're right. And I guess `_ffmpeg.Close()` waits until the process terminates normally? – kesh Oct 01 '22 at 17:52
  • 1
    @BrownBear can you please post a code sample we can build and execute (with `using` and `Main()` function). It's difficult for me to make your code executable, because I don't have experience with C#. I think kesh is correct - you have to add `-shortest` argument, and close `stdin`. Place the `{RecordFile}` at the end: `_ffmpeg.StartInfo.Arguments = $"-f rawvideo -pix_fmt rgba -s {RecordSize.Width}x{RecordSize.Height} -r 30 -i - -f lavfi -i anullsrc=channel_layout=mono:sample_rate=8k -c:v libx264 -crf 17 -pix_fmt yuv420p -b:v 1024k -bufsize:v 1835008 -tune animation -shortest {RecordFile}";` – Rotem Oct 01 '22 at 20:42
  • Rotem thanks for your reply, yes I will do but it may take 24hrs+ – Rabbex Oct 01 '22 at 22:17
  • @Rotem, on second thought I won't do that because I just cut and pasted you cmd args into my code and Hey Presto problem gone. Even tested the MP4 on the TV and no annoying complaints about the audio. Thank you very much, in the end it seems to have been keshs sugestion of '-shortest' and your order of the args. – Rabbex Oct 01 '22 at 22:40

0 Answers0