Skip to content
This repository has been archived by the owner on Jul 29, 2024. It is now read-only.

Reading from stdin doesn't work #634

Open
Marzhin971 opened this issue Feb 17, 2023 · 2 comments
Open

Reading from stdin doesn't work #634

Marzhin971 opened this issue Feb 17, 2023 · 2 comments

Comments

@Marzhin971
Copy link

Marzhin971 commented Feb 17, 2023

Trying to write, in C#, a routine that calls an ffmpeg process, get the piped data inside a named pipe, then give the taken YUV data to SvtHevcEncApp via its stdin stream.

The way I do that works for a lot of external encoders like x264, x265, vvc, aom and xvid.

The shortened code I wrote is at the end of this post.

The behaviour of SvtHevcEncApp is always the same and easily reproductible : After one write is done to the bufferedStream (or directly into the svt_hevc process StandardInput), whichever the amount of data, SvtHevcEncApp will "think" the stream has ended and finishes encoding. It will still encode the number of frames one (and only one) buffer can contain, but the video content has its planes wrong. but I'll post another issue for that. My current problem is about the piping method.

AFAIK, signalling an end of stream to a process is done by closing the writer, that's what I do after receiving a data.Length that is shorter than the buffer (a information telling me ffmpeg has finished sending data to the pipe), but an exception is raised, in the same instruction (bufStream.Write), SvtHevcEncApp received the data, encoded what it can, leaves with exitcode==0, and then the IO exception is raised telling me the channel was closed and it's always in the first run of that loop. When the exception is raised, ffmpeg was still running, but not SvtHevcEncApp.

At this point, I can't be sure if it's something that's wrong in SvtHevcEncApp, but I think it is, since all other standalone encoders are working perfectly well.

static void DoPipe()
{
  int buffersize = 100000000;

  NamedPipeServerStream sPipe = new NamedPipeServerStream("testpipe.y4m", PipeDirection.InOut, 1,       PipeTransmissionMode.Byte, PipeOptions.None, buffersize, buffersize);

  string inputFile = @"h:\test_video.mp4";

  Process ffmpeg = new Process();
  ffmpeg.StartInfo.FileName = "ffmpeg.exe";
  ffmpeg.StartInfo.UseShellExecute = false;
  ffmpeg.StartInfo.RedirectStandardInput = true;
  ffmpeg.StartInfo.RedirectStandardOutput = true;
  ffmpeg.StartInfo.RedirectStandardError = true;
  ffmpeg.OutputDataReceived += new DataReceivedEventHandler(Output_ffmpeg_Received);
  ffmpeg.ErrorDataReceived += new DataReceivedEventHandler(Error_ffmpeg_Received);
  ffmpeg.StartInfo.Arguments = "-hide_banner -nostdin -i " + inputFile + " -t 30 -pix_fmt yuv420p -s 1920x1080 -an -y -f yuv4mpegpipe \"\\\\.\\pipe\\testpipe.y4m\"";

  ffmpeg.Start();
  ffmpeg.BeginErrorReadLine();
  ffmpeg.BeginOutputReadLine();

  sPipe.WaitForConnection();
  BinaryReader reader = new BinaryReader(sPipe);
  bool stop = false;

  Process svt_hevc = new Process();
  svt_hevc.StartInfo.FileName = "SvtHevcEncApp.exe";
  svt_hevc.StartInfo.UseShellExecute = false;
  svt_hevc.StartInfo.RedirectStandardInput = true;
  svt_hevc.StartInfo.RedirectStandardError = true;
  svt_hevc.StartInfo.RedirectStandardOutput = true;
  svt_hevc.OutputDataReceived += new DataReceivedEventHandler(Output_encoder_Received);
  svt_hevc.ErrorDataReceived += new DataReceivedEventHandler(Error_encoder_Received);
  svt_hevc.StartInfo.Arguments = "-color-format 1 -bit-depth 8 -w 1920 -h 1080 -fps 25 -i stdin -rc 0 -q 10 -errlog -encMode 6 - b test.svt-hevc.hevc";

  svt_hevc.Start();
  svt_hevc.BeginErrorReadLine();
  svt_hevc.BeginOutputReadLine();

  var bufStream = new BufferedStream(svt_hevc.StandardInput.BaseStream, buffersize);

  do
  {
    byte[] data = reader.ReadBytes(buffersize);

    if (data.Length > 0)
      try { bufStream.Write(data, 0, data.Length);  }
      catch (ArgumentException e) { Console.WriteLine("Argument Exception {0}", e.Message); }
      catch (IOException e) { Console.WriteLine("IO Exception {0}", e.Message); } // SvtHevcEncApp raises an exception here
      catch (NotSupportedException e) { Console.WriteLine("NotSupportedException {0}", e.Message); }
      catch (ObjectDisposedException e) { Console.WriteLine("Object Disposed Exception {0}",e.Message); }

  if (data.Length < buffersize) stop = true; // If data length is not buffersize, ffmpeg has finished. we stop the loop

  } while (!svt_hevc.HasExited && !stop); // we dont need to wait for ffmpeg to finish, stop will tell us

  sPipe.Close(); // closing ffmpeg pipe
  svt_hevc.StandardInput.BaseStream.Close(); // signalling to svt that the stream is EOF
  ffmpeg.WaitForExit(); // to be sure
  svt_hevc.WaitForExit(); // to be very sure
}
@1480c1
Copy link
Member

1480c1 commented Feb 17, 2023

Unfortunately, the app is not designed similar to other encoders and has a few issues and quirks, one of them is the fact that it does not properly check for eof from stdin. It is pretty much required to use -n to tell the encoder when to stop

@Marzhin971
Copy link
Author

Marzhin971 commented Feb 17, 2023

In the meantime, I added a test to avoid working from a pipe with the two commands :

ffmpeg -hide_banner -t 30 -nostdin -i h:\test_video.mp4 -pix_fmt yuv420p -s 1920x1080 -an -y -f yuv4mpegpipe testpipe.y4m

svthevcencapp -color-format 1 -bit-depth 8 -w 1920 -h 1080 -fps 25 -i testpipe.y4m -rc 0 -q 10 -errlog -encMode 6 -thread-count 24 -b test.svt-hevc.x265

It works perfectly and planes are ok.

So, with the -n option, it now works as expected with good planes, but having to count the number of frames in the source video before encoding isn't a good solution obviously. I'll keep the issue open in order to possibly know if one of the app developpers push that in the "todo queue". thanks for your answer

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants