我需要通过 ffmpeg 将数据从流 1 读取到流 2。 当我从文件输入数据并输出到管道时,它效果很好:
Process? CreateStream()
{
return Process.Start(new ProcessStartInfo
{
FileName = @"sources\ffmpeg",
Arguments = @"-i input.mp3 -f s16le pipe:1",
UseShellExecute = false,
RedirectStandardOutput = true
});
}
或者当我从管道输入数据并输出到文件时:
Process? CreateStream()
{
return Process.Start(new ProcessStartInfo
{
FileName = @"sources\ffmpeg",
Arguments = @"-i pipe: -f s16le output.file",
UseShellExecute = false,
RedirectStandardInput = true
});
}
但是如果我尝试两者都做:
Process? CreateStream()
{
return Process.Start(new ProcessStartInfo
{
FileName = @"sources\ffmpeg",
Arguments = @"-i pipe:0 -f s16le pipe:1",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true
});
}
运行时将挂起就地打印:
从“pipe:0”输入#0、matroska、webm: 元数据: 编码器:谷歌/视频文件 持续时间:00:04:15.38,开始:-0.007000,比特率:N/A 流 #0:0(eng):音频:opus、48000 Hz、立体声、fltp(默认) 流映射: 流#0:0 -> #0:0(opus(本机)-> pcm_s16le(本机))
输出#0,s16le,到“管道:1”: 元数据: 编码器:Lavf59.27.100 流 #0:0(eng):音频:pcm_s16le、48000 Hz、立体声、s16、1536 kb/s(默认) 元数据: 编码器:Lavc59.37.100 pcm_s16le
主要功能代码(所有示例都相同):
async Task Do()
{
using (var ffmpeg = CreateStream())
{
if (ffmpeg == null) return;
using (var audioStream = GetAudioStream())
{
await audioStream.CopyToAsync(ffmpeg.StandardInput.BaseStream);
ffmpeg.StandardInput.Close();
}
//runtime will hang in here
Console.WriteLine("\n\ndone\n\n"); //this won't be printed
using (var outputStream = CreatePCMStream())
{
try
{
await ffmpeg.StandardOutput.BaseStream.CopyToAsync(outputStream);
}
finally
{
await outputStream.FlushAsync();
}
}
}
}
最有趣的是,如果我删除
RedirectStandardOutput = true
字符串程序将按预期工作,将一堆原始数据打印到控制台。
我想在不使用任何中间文件等的情况下解决这个问题。
我今天自己也面临这个问题,花了几个小时努力寻找解决方案。最后,我创建了一个解决方案,我不知道它是否存在隐藏的问题或缺陷,但它对我有用。意思是,这并不是您问题的确切答案,而是展示了现在在我的类似场景中有效的示例。
此代码采用
byte[]
数组,使用 ffmpeg 对其进行转码,然后将其实时传输到名为 sink
的流。我的具体 ffmpeg 参数在这里将原始 PCM 从单声道转换为环绕声。
private static async Task TransmitAudioData(byte[] audioData, VoiceTransmitSink sink)
{
var startInfo = new ProcessStartInfo("ffmpeg")
{
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
Arguments = string.Join(" ", [
"-loglevel panic",
"-f s16le",
"-ac 1",
"-ar 48000",
"-i pipe:0",
"-f s16le",
"-ac 2",
"-ar 48000",
"pipe:1"
])
};
using var process = Process.Start(startInfo)!;
process.EnableRaisingEvents = true;
var stdin = process.StandardInput.BaseStream;
var stdout = process.StandardOutput.BaseStream;
process.ErrorDataReceived += (_, args)
=> Debug.WriteLine($"Error while transcoding audio data: {args.Data}");
try
{
var writer = Task.Run(async () =>
{
await stdin.WriteAsync(audioData);
await stdin.FlushAsync();
stdin.Close();
});
var reader = Task.Run(async () =>
{
const int chunkSize = 8192;
var buffer = new byte[chunkSize];
int l;
while ((l = await stdout.ReadAsync(buffer)) > 0)
{
await sink.WriteAsync(buffer.AsMemory(0, l));
}
});
await process.WaitForExitAsync();
await Task.WhenAll(writer, reader);
}
finally
{
stdin.Close();
stdout.Close();
}
}