ffmpeg进程如何在c#中从一个管道读取到另一个管道

问题描述 投票:0回答:1

我需要通过 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
字符串程序将按预期工作,将一堆原始数据打印到控制台。

我想在不使用任何中间文件等的情况下解决这个问题。

c# .net ffmpeg
1个回答
0
投票

我今天自己也面临这个问题,花了几个小时努力寻找解决方案。最后,我创建了一个解决方案,我不知道它是否存在隐藏的问题或缺陷,但它对我有用。意思是,这并不是您问题的确切答案,而是展示了现在在我的类似场景中有效的示例。

此代码采用

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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.