To use FFmpeg with a stream as input in C#, you must launch the process, wait for standard input to become available, and then write bytes directly to
Process.StandardInput.BaseStream. This approach avoids temporary files by piping video data from memory or other sources directly into the encoder. Proper synchronization is critical to prevent race conditions where writes occur before the process initializes.
Setting Up the Process Arguments
The foundation of streaming video into FFmpeg lies in constructing the correct command-line arguments. You need to specify the input format as a raw byte stream and define the output destination. Instead of pointing to a file path, you use the special value
-i pipe:0 to tell FFmpeg to read from its standard input handle.
For example, if you are converting a raw video frame buffer to an MP4 file, your argument string might look like this:
string args = "-f rawvideo -pix_fmt bgr24 -s 1920x1080 -r 30 -i pipe:0 -c:v libx264 output.mp4";
Here,
-f rawvideo forces FFmpeg to interpret the incoming stream as raw video data rather than trying to guess the container format. The
-pix_fmt bgr24 flag specifies the pixel format (common in .NET bitmap operations), and
-s 1920x1080 sets the resolution. Adjust these parameters to match your specific source data dimensions and color space. Without these explicit flags, FFmpeg may fail to parse the binary stream correctly, resulting in errors or corrupted output.
Initializing the Process Safely
A common pitfall in C# interop with external processes is failing to manage the
Process object lifecycle. Always wrap the process instantiation in a
using statement to ensure handles are released even if exceptions occur. Additionally, you must configure the process to redirect standard input, output, and error streams.
using var process = new Process();
process.StartInfo.FileName = "ffmpeg.exe"; // Or full path
process.StartInfo.Arguments = args;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
Setting
UseShellExecute to false is mandatory when redirecting streams. If you do not set
RedirectStandardInput to true, attempting to access the stream later will throw an exception. It is also advisable to suppress the console window by setting
process.StartInfo.CreateNoWindow = true, which keeps your application interface clean during background processing.
Synchronizing Input and Output
The most critical aspect of this implementation is avoiding race conditions. You cannot write to the standard input immediately after calling
Start(). The operating system needs time to allocate buffers and initialize the child process. Writing too early can result in lost data or deadlocks.
The recommended pattern is to start the process, then asynchronously read the error stream while writing to the input stream. Reading the error stream concurrently prevents the process buffer from filling up, which would otherwise cause FFmpeg to hang indefinitely.
process.Start();
// Start reading error output asynchronously to prevent deadlock
var errorTask = process.StandardError.ReadToEndAsync();
// Now it is safe to write to the input stream
using var inputStream = process.StandardInput.BaseStream;
// Write your video frames here...
// Wait for input to finish
inputStream.Flush();
await process.WaitForExitAsync();
// Get any error messages
var errorMessage = await errorTask;
By using
ReadToEndAsync on the error stream, you ensure that FFmpeg’s diagnostic messages are consumed without blocking the main thread. This technique is robust against buffering limits. If you were to read the error synchronously while writing to the input, you might hit a deadlock if the error buffer fills up before the input write completes.
Piping Video Data Efficiently
Once the process is started and synchronized, you can write your video frames directly to the
BaseStream. For large video files, avoid loading entire frames into memory if possible. Instead, write chunks of data sequentially.
If you are working with
Bitmap objects in .NET, you must first lock the bits to access the underlying memory, copy that data into a byte array, and then write that array to the stream. Ensure the byte order matches the pixel format specified in your FFmpeg arguments (e.g., BGR vs RGB).
byte[] frameData = GetFrameBytes(bitmap);
await inputStream.WriteAsync(frameData, 0, frameData.Length);
After all frames have been written, call
Flush() to ensure all data is pushed to the process. Then, close the stream to signal EOF to FFmpeg. Finally, wait for the process to exit to ensure the output file is fully written and closed by the encoder. This sequence guarantees that no data is left in intermediate buffers and that the output file is valid.
For more advanced scenarios involving multiple inputs, such as adding audio tracks, see
How to Add Random Music (Defined Folder) for Multiple Video with FFMPEG. If you encounter issues displaying the resulting video in web browsers, check out
Troubleshooting: Unable to Display FFMPEG-Created Video in HTML.
Frequently Asked Questions
Why does my application hang when writing to FFmpeg?
This usually happens because the error buffer is full and blocking the process. You must read the standard error stream asynchronously while writing to the input stream. If you ignore the error output, FFmpeg will eventually stall waiting for the buffer to be cleared.
Can I use StandardInput.WriteLine instead of BaseStream?
No. Video data is binary, not text. Using
WriteLine will corrupt the data by adding newline characters and potentially misinterpreting bytes. Always use
BaseStream and write raw byte arrays to preserve the integrity of the video frames.
What happens if I don't flush the stream?
If you do not flush the stream before closing it, some data may remain in the internal buffers and never reach FFmpeg. This results in truncated output files or missing final frames. Always call
Flush() to push remaining data to the pipe.
Is it safe to run multiple FFmpeg processes simultaneously?
Yes, but each process requires its own independent stream and synchronization logic. Do not share the same
Process object across threads. Each instance should be managed independently to avoid conflicts over standard input and output handles.
How do I handle different pixel formats in C#?
You must convert your image data to the format FFmpeg expects. Common formats include
bgr24 for standard bitmaps and
rgba for images with alpha channels. Use
Marshal.Copy to extract pixel data from a
Bitmap and ensure the stride (row length) matches the width multiplied by bytes per pixel.