I am implementing a function to merge audio and video into a single stream using FFmpeg tool.
The problem I have right now is that FFmpeg is not connecting to the pipes and I have no idea why. Can someone help me with this issue?
Questions I already read:
- ffmpeg process how to read from pipe to pipe in c#
- ffmpeg output pipeing to named windows pipe
- How to use pipe in ffmpeg within c#
- Using pipes with FFMpeg as an input
Arguments:
-i "\.pipeFFmpeg_8daf7" -i "\.pipeFFmpeg_46100" -c:v copy -map 0:v:0 -map 1:a:0 -shortest -f mp4 pipe:1
The error I am receiving:
[in#0 @ 0000015395b97600] Error opening input: No such file or directory
Error opening input file \.pipeFFmpeg_8daf7.
Error opening input files: No such file or directory
My code:
public static async Task<MemoryStream> OverwriteAudioInVideo(
Stream video, Stream audio, CancellationToken cancellationToken = default)
{
var videoPipeName = GetPipeName();
var audioPipeName = GetPipeName();
var arguments = GetOverwriteAudioInVideoCommand(audioPipeName, videoPipeName);
var videoPipeTask = CreatePipeAndWriteDataAsync(videoPipeName, video, cancellationToken);
var audioPipeTask = CreatePipeAndWriteDataAsync(audioPipeName, audio, cancellationToken);
using var process = CreateFFmpegProcess(arguments);
process.OutputDataReceived += OnDataReceived;
process.ErrorDataReceived += OnDataReceived;
process.Start();
process.BeginOutputReadLine(); // For debugging purposes only
// Will cause exception in
// 'ReadProcessResultToMemoryStreamAsync'
process.BeginErrorReadLine();
await Task.WhenAll(videoPipeTask, audioPipeTask);
await process.WaitForExitAsync(cancellationToken);
process.ErrorDataReceived -= OnDataReceived;
process.OutputDataReceived -= OnDataReceived;
return await ReadProcessResultToMemoryStreamAsync(process, cancellationToken);
}
private static async Task CreatePipeAndWriteDataAsync(
string name, Stream input, CancellationToken cancellationToken)
{
try
{
using var pipe = new NamedPipeServerStream(
name,
PipeDirection.Out,
1,
PipeTransmissionMode.Byte,
PipeOptions.Asynchronous);
Debug.WriteLine($"Waiting for connection on pipe {name}");
await pipe.WaitForConnectionAsync(cancellationToken);
Debug.WriteLine($"Connected to pipe {name}");
await input.CopyToAsync(pipe, cancellationToken);
pipe.Close();
Debug.WriteLine($"Data written to pipe {name}");
}
catch (Exception ex)
{
Debug.WriteLine($"Error creating or writing to pipe {name}: {ex.Message}");
throw;
}
}
private static async Task<MemoryStream> ReadProcessResultToMemoryStreamAsync(
Process process, CancellationToken cancellationToken)
{
var result = new MemoryStream();
var buffer = new byte[4096];
int bytesRead;
do
{
bytesRead = await process.StandardOutput.BaseStream.ReadAsync(buffer, cancellationToken);
await result.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken);
} while (bytesRead > 0);
return result;
}
private static string GetOverwriteAudioInVideoCommand(string audioPipeName, string videoPipeName)
{
const string CommandFormat
= "-i "{0}" -i "{1}" -c:v copy -map 0:v:0 -map 1:a:0 -shortest -f mp4 pipe:1";
var result = string.Format(CommandFormat, audioPipeName, videoPipeName);
Debug.WriteLine(result);
return result;
}
private const string ExecutablePath = "ExternalBinaries\ffmpeg.exe";
private static Process CreateFFmpegProcess(string arguments) => new()
{
StartInfo = new ProcessStartInfo(ExecutablePath, arguments)
{
#if !DEBUG
CreateNoWindow = true,
#endif
RedirectStandardError = true,
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false
}
};
private static string GetPipeName()
{
var name = $"FFmpeg_{Guid.NewGuid().ToString("N")[..5]}";
Debug.WriteLine(name);
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? $"\\.\pipe\{name}"
: $"unix:{Path.Combine(Path.GetTempPath(), $"CoreFxPipe_{name}")}";
}
private static void OnDataReceived(object sender, DataReceivedEventArgs e)
=> Debug.WriteLine(e.Data);