After creating FFmpeg processes for encoding and decoding in C#, I tried to handle the following process. I also applied Thread.Sleep(1000/30)
because I wanted to do it in 1/30 second intervals instead of 1 second.
- Get dummy raw data
- Stream to ffmpeg process for encoding
- Read encoded data and stream it to the ffmpeg process for decoding
- Outputting byte size to the console
Here's my actual code, why doesn't it work?
internal byte[] GetFrame(int width, int height)
{
int frameSize = width * height * 3;
byte[] frameData = new byte[frameSize];
Random random = new Random();
for(int i = 0; i < frameData.Length; i++)
{
frameData[i] = (byte)random.Next(256);
}
return frameData;
}
internal void EncodeAndDecode(int width, int height, int fps, int bitrate, string codec, string preset, string tune)
{
Process ffmpegEncodeProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-f rawvideo -pixel_format rgb24 -s 540x720 -r 30 " +
$"-i - -c:v libx264 -preset ultrafast -tune zerolatency -maxrate 1500k -b:v 1500k " +
$"-f h264 pipe:1",
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
Process ffmpegDecodeProcess = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "ffmpeg",
Arguments = $"-f h264 -i pipe:0 " +
$"-f rawvideo -pixel_format rgb24 -s 540x720 pipe:1",
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
//start process
ffmpegEncodeProcess.Start();
ffmpegDecodeProcess.Start();
//input stream
var ffmpegEncodeInput = ffmpegEncodeProcess.StandardInput.BaseStream;
var ffmpegDecodeInput = ffmpegDecodeProcess.StandardInput.BaseStream;
//output stream
var ffmpegEncodeOutput = ffmpegEncodeProcess.StandardOutput.BaseStream;
var ffmpegDecodeOutput = ffmpegDecodeProcess.StandardOutput.BaseStream;
new Thread(async () =>
{
try
{
while(true)
{
byte[] encodedChunk = new byte[width * height * 3];
byte[] decodedChunk = new byte[width * height * 3];
byte[] frameData = GetFrame(width, height);
Console.WriteLine($"[INFO] Original frame size: {frameData.Length}bytes.");
if(frameData.Length == 0)
{
break;
}
await ffmpegEncodeInput.WriteAsync(frameData, 0, frameData.Length);
await ffmpegEncodeInput.FlushAsync();
int encodedBytesRead = await ffmpegEncodeOutput.ReadAsync(encodedChunk, 0, encodedChunk.Length);
Console.WriteLine($"[INFO] Encoded frame size: {encodedBytesRead}bytes.");
if(encodedBytesRead > 0)
{
await ffmpegDecodeInput.WriteAsync(encodedChunk, 0, encodedBytesRead);
await ffmpegDecodeInput.FlushAsync();
int decodedBytesRead = await ffmpegDecodeOutput.ReadAsync(decodedChunk, 0, decodedChunk.Length);
Console.WriteLine($"[INFO] Decoded frame size: {decodedBytesRead}bytes.");
if(decodedBytesRead > 0)
{
//do something...
}
}
Thread.Sleep(1000 / fps); //1/30s
}
}
catch(Exception e)
{
Console.WriteLine(e);
}
finally
{
ffmpegEncodeInput.Close();
ffmpegDecodeInput.Close();
if(!ffmpegEncodeProcess.HasExited)
{
ffmpegEncodeProcess.Kill();
}
if(!ffmpegDecodeProcess.HasExited)
{
ffmpegDecodeProcess.Kill();
}
Console.WriteLine("[INFO]: FFmpeg process clean up.");
}
}).Start();
}
To be precise, it only outputs the "Original" and "Encoded" sizes once to the console and then doesn't do anything. What am I doing wrong?