【问题标题】:Play audio from a stream using C#使用 C# 从流中播放音频
【发布时间】:2010-09-16 03:04:51
【问题描述】:

在 C# 中有没有办法直接从 System.IO.Stream 播放音频(例如 MP3),例如从 WebRequest 返回而不将数据临时保存到磁盘?


解决方案NAudio

在NAudio 1.3 的帮助下,可以:

  1. 将 MP3 文件从 URL 加载到 MemoryStream 中
  2. 将 MP3 数据完全加载后转换为波形数据
  3. 使用NAudio的 WaveOut 类播放波形数据

如果能够播放半载的 MP3 文件本来就不错,但由于 NAudio 库的设计,这似乎是不可能的。

这是可以完成工作的函数:

    public static void PlayMp3FromUrl(string url)
    {
        using (Stream ms = new MemoryStream())
        {
            using (Stream stream = WebRequest.Create(url)
                .GetResponse().GetResponseStream())
            {
                byte[] buffer = new byte[32768];
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
            }

            ms.Position = 0;
            using (WaveStream blockAlignedStream =
                new BlockAlignReductionStream(
                    WaveFormatConversionStream.CreatePcmStream(
                        new Mp3FileReader(ms))))
            {
                using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                {
                    waveOut.Init(blockAlignedStream);
                    waveOut.Play();                        
                    while (waveOut.PlaybackState == PlaybackState.Playing )                        
                    {
                        System.Threading.Thread.Sleep(100);
                    }
                }
            }
        }
    }

【问题讨论】:

  • 很高兴看到你成功了。在流式传输时正确播放它不会有太多工作。主要问题是 Mp3FileReader 目前希望提前知道长度。我会考虑为下一个版本的 NAudio 添加一个演示
  • @Mark Heath 您是否已经解决了问题并在当前 NAudio 版本中添加了演示,还是仍在您的管道中?
  • 恐怕还没有,尽管在 NAudio 1.3 中进行了更改,它不需要太多调整即可使其正常工作。
  • 马克:我是否需要在 NAudio 中进行修改才能使其正常工作,因为我刚刚下载了 NAudio1.3,但它接受了上面的代码而不做任何更改,但另一方面抛出了类似于“无法进行 ACM 转换”。
  • 顺便说一句,我正在尝试关注translate.google.com/translate_tts?q=I+love+techcrunch

标签: .net audio stream mp3 naudio


【解决方案1】:

SoundPlayer 类可以做到这一点。看起来您所要做的就是将其 Stream 属性设置为流,然后调用 Play。

编辑
我不认为它可以播放 MP3 文件。它似乎仅限于.wav。我不确定框架中是否有任何东西可以直接播放 MP3 文件。我发现的所有内容都涉及使用 WMP 控件或与 DirectX 交互。

【讨论】:

  • 多年来我一直在努力寻找一个可以进行 MP3 编码和解码的 .NET 库,但我认为它不存在。
  • NAudio 应该为您完成这项工作 - 至少在解码方面(见第一篇文章)
  • SoundPlayer 有严重的 GC 问题,会随机播放垃圾,除非你使用微软官方的解决方法(点击解决方法):connect.microsoft.com/VisualStudio/feedback/…
  • SoundPlayer + memoryStream 有设计错误。当您第一次播放第二次或第三次时,它会在您的音频中间添加奇怪的噪音。
  • 解决方法链接不再起作用,但它在 Wayback Machine 上:web.archive.org/web/20120722231139/http://connect.microsoft.com/…
【解决方案2】:

编辑:已更新答案以反映 NAudio 最新版本的变化

可以使用我编写的NAudio 开源.NET 音频库。它会在您的 PC 上查找 ACM 编解码器来进行转换。 NAudio 提供的 Mp3FileReader 当前期望能够在源流中重新定位(它预先构建 MP3 帧的索引),因此它不适合通过网络进行流式传输。但是,您仍然可以使用 NAudio 中的 MP3Frame 和 AcmMp3FrameDecompressor 类即时解压缩流式 MP3。

我在我的博客上发表了一篇文章,解释了how to play back an MP3 stream using NAudio。本质上,您有一个线程下载 MP3 帧,将它们解压缩并将它们存储在 BufferedWaveProvider 中。然后另一个线程使用BufferedWaveProvider 作为输入进行回放。

【讨论】:

  • NAudio 库现在附带一个名为 Mp3StreamingDemo 的示例应用程序,它应该提供从网络直播 MP3 所需的一切。
  • 是否可以使用您的库将输入的麦克风/线路直播到 android 设备?
【解决方案3】:

NAudio 封装了 WaveOutXXXX API。我还没有查看源代码,但是如果 NAudio 以一种不会在每次调用时自动停止播放的方式公开 waveOutWrite() 函数,那么您应该能够做您真正想做的事情,即开始播放接收所有数据之前的音频流。

使用 waveOutWrite() 函数允许您“预读”并将较小的音频块转储到输出队列中 - Windows 将自动无缝播放这些块。您的代码必须获取压缩的音频流并将其动态转换为小块 WAV 音频;这部分真的很难——我见过的所有库和组件都一次将整个文件进行 MP3 到 WAV 的转换。可能您唯一现实的机会是使用 WMA 而不是 MP3 来执行此操作,因为您可以围绕多媒体 SDK 编写简单的 C# 包装器。

【讨论】:

    【解决方案4】:

    Bass 可以做到这一点。从内存中的 Byte[] 播放或通过文件委托播放您返回数据的位置,这样您就可以在有足够的数据开始播放时立即播放..

    【讨论】:

      【解决方案5】:

      我没有在 WebRequest 中尝试过,但 Windows Media Player ActiveX 和 MediaElement(来自 WPF)组件都能够播放和缓冲 MP3 流。

      我用它来播放来自SHOUTcast 流的数据,效果很好。但是,我不确定它是否适用于您提出的方案。

      【讨论】:

        【解决方案6】:

        我一直将 FMOD 用于此类事情,因为它可以免费用于非商业用途并且效果很好。

        也就是说,我很乐意改用更小(FMOD 约为 300k)和开源的东西。如果它是完全管理的,那么我可以将它与我的 .exe 编译/合并,而不必特别注意获得对其他平台的可移植性......

        (FMOD 也具有可移植性,但您显然需要针对不同平台使用不同的二进制文件)

        【讨论】:

          【解决方案7】:

          我打包了 MP3 解码器库,并以 mpg123.net 的形式提供给 .NET 开发人员。

          包括将 MP3 文件转换为PCM 和读取ID3 标签的示例。

          【讨论】:

          • 太棒了!我期待着这个周末去看看。
          【解决方案8】:

          我稍微修改了主题启动器源,现在它可以播放未完全加载的文件。在这里(注意,这只是一个示例,是一个起点;您需要在此处进行一些异常和错误处理):

          private Stream ms = new MemoryStream();
          public void PlayMp3FromUrl(string url)
          {
              new Thread(delegate(object o)
              {
                  var response = WebRequest.Create(url).GetResponse();
                  using (var stream = response.GetResponseStream())
                  {
                      byte[] buffer = new byte[65536]; // 64KB chunks
                      int read;
                      while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                      {
                          var pos = ms.Position;
                          ms.Position = ms.Length;
                          ms.Write(buffer, 0, read);
                          ms.Position = pos;
                      }
                  }
              }).Start();
          
              // Pre-buffering some data to allow NAudio to start playing
              while (ms.Length < 65536*10)
                  Thread.Sleep(1000);
          
              ms.Position = 0;
              using (WaveStream blockAlignedStream = new BlockAlignReductionStream(WaveFormatConversionStream.CreatePcmStream(new Mp3FileReader(ms))))
              {
                  using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                  {
                      waveOut.Init(blockAlignedStream);
                      waveOut.Play();
                      while (waveOut.PlaybackState == PlaybackState.Playing)
                      {
                          System.Threading.Thread.Sleep(100);
                      }
                  }
              }
          }
          

          【讨论】:

          • 你测试你的代码了吗?如果是这样,它适用于哪个版本的 NAudio?
          • 此外 - 您的代码看起来确实很容易出现线程问题。你确定你知道自己在做什么吗?
          • 1) 是的,我做到了。 2) 使用最新版本。 3) 正如我在之前的消息中所说,这只是一个概念证明。
          • 你如何定义“最新版本”?是版本 1.3 还是版本 68454 的源代码?
          • @Martin 抱歉,好久没来了。我使用的 NAudio.dll 版本为 1.3.8。
          【解决方案9】:

          为了回答问题here,我已经调整了问题中发布的来源以允许使用 Google 的 TTS API:

          bool waiting = false;
          AutoResetEvent stop = new AutoResetEvent(false);
          public void PlayMp3FromUrl(string url, int timeout)
          {
              using (Stream ms = new MemoryStream())
              {
                  using (Stream stream = WebRequest.Create(url)
                      .GetResponse().GetResponseStream())
                  {
                      byte[] buffer = new byte[32768];
                      int read;
                      while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                      {
                          ms.Write(buffer, 0, read);
                      }
                  }
                  ms.Position = 0;
                  using (WaveStream blockAlignedStream =
                      new BlockAlignReductionStream(
                          WaveFormatConversionStream.CreatePcmStream(
                              new Mp3FileReader(ms))))
                  {
                      using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
                      {
                          waveOut.Init(blockAlignedStream);
                          waveOut.PlaybackStopped += (sender, e) =>
                          {
                              waveOut.Stop();
                          };
                          waveOut.Play();
                          waiting = true;
                          stop.WaitOne(timeout);
                          waiting = false;
                      }
                  }
              }
          }
          

          调用:

          var playThread = new Thread(timeout => PlayMp3FromUrl("http://translate.google.com/translate_tts?q=" + HttpUtility.UrlEncode(relatedLabel.Text), (int)timeout));
          playThread.IsBackground = true;
          playThread.Start(10000);
          

          终止于:

          if (waiting)
              stop.Set();
          

          请注意,我在上面的代码中使用了ParameterizedThreadDelegate,并且线程以playThread.Start(10000); 开始。 10000 表示最多播放 10 秒的音频,因此如果您的流播放时间超过播放时间,则需要对其进行调整。这是必要的,因为当前版本的 NAudio (v1.5.4.0) 在确定流何时完成播放时似乎存在问题。它可能会在以后的版本中得到修复,或者可能有我没有花时间找到的解决方法。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-09-20
            • 2012-05-22
            • 1970-01-01
            • 2023-03-23
            • 1970-01-01
            • 2017-06-20
            相关资源
            最近更新 更多