如何使用NAudio播放AAC音频流?

时间:2018-10-09 14:54:46

标签: c# naudio naudio-framework

在我目前正在处理的应用程序中,我想播放AAC音频流。 例如,此网络广播:http://stream.bauermedia.fi/iskelma/kem_64.aac。我想使用NAudio框架,因为该项目已经使用了它。

首先,我检查了NAudioDemo中的Mp3StreamingDemo。我创建了一个ADTSFrame类,在其中处理了ADTS帧头(https://wiki.multimedia.cx/index.php/ADTS)。从ADTS帧头中,我得到了下一个信息:采样频率,通道数,帧长。然后,我创建了一个单独的类ADTSWaveFormat,其中包含一个私有字段waveFormatTag和两个属性Channels和SampleRate。 WafeFormatTag设置为WaveFormatEncoding.MPEG_ADTS_AAC。从这里-https://docs.microsoft.com/en-us/windows/desktop/DirectShow/audio-subtypes可以理解,不需要更多信息。但是,当我想获取PCM格式时,AcmInterop.acmFormatSuggest()返回了“ NoDriver”。所以我被困在这里,因为我不知道我是否朝着正确的方向前进。

然后我在Internet上搜索并找到信息,即NAudio中有一个StreamMediaFoundationReader类。 StreamMediaFoundationReader想要在其构造函数中获取流。首先,将此流转换为ComStream,然后转换为IMFByteStream。当ComStream转换为IMFByteStream时,将执行Seek操作。所以我从response.GetResponseStream()获得的ConnectStream不适合提供给StreamMediaFoundationReader的构造函数,因为它是不可搜索的。所以我决定使用内存流。

直到现在,我编写的最成功的代码是:

WebResponse response = WebRequest.Create(InputPath).GetResponse();
using (Stream rs = response.GetResponseStream())
{
  using (MemoryStream ms = new MemoryStream())
  {
    byte[] buffer = new byte[16384 * 4];
    int read = rs.Read(buffer, 0, buffer.Length);
    ms.Write(buffer, 0, read);
    using (var streamMfReader = new StreamMediaFoundationReader(rs))
    using (WaveOut waveOut = new WaveOut())
    {
      waveOut.Init(streamMfReader);
      waveOut.Play();
      while (waveOut.PlaybackState == PlaybackState.Playing)
        Thread.Sleep(100);
    }
  }
}

如您所见,此代码仅在片刻内播放,因此不适用于任何地方。

我也尝试过:

MemoryStream memoryStream = new MemoryStream();
new Thread(delegate (object o)
{
  WebResponse response = WebRequest.Create(InputPath).GetResponse();
  using (Stream responseStream = response.GetResponseStream())
  {
    byte[] buffer = new byte[65536];
    int read;
    while ((read = responseStream.Read(buffer, 0, buffer.Length)) > 0)
    {
      long pos = memoryStream.Position;
      memoryStream.Position = memoryStream.Length;
      memoryStream.Write(buffer, 0, read);
      memoryStream.Position = pos;
    }
  }
}).Start();

while (memoryStream.Length < 65536 * 10)
  Thread.Sleep(1000);

memoryStream.Position = 0;
using (WaveStream streamMediaFoundationReader = new 
                                StreamMediaFoundationReader(memoryStream))
{
  using (WaveOut waveOut = new 
                             WaveOut(WaveCallbackInfo.FunctionCallback()))
  {
    waveOut.Init(streamMediaFoundationReader);
    waveOut.Play();
    while (waveOut.PlaybackState == PlaybackState.Playing)
    {
       Thread.Sleep(100);
    }
  }
}

但是在这里我得到了ContextSwitchDeadlock,因为memoryStream在不同的线程中使用。

到目前为止,我没有太多的编程经验。

能否请任何人给我建议,如何使用NAudio播放音频流?

今天,我还尝试用以下方式编辑ReadFullyStream:

public class ReadFullyStream : Stream
    {
        private readonly Stream sourceStream;
        private long readPos;
        private long seekPos;
        private readonly byte[] readAheadBuffer;
        private int readAheadLength;
        private int readAheadOffset;

        public ReadFullyStream(Stream sourceStream)
        {
            this.sourceStream = sourceStream;
            readAheadBuffer = new byte[4096];
        }
        public override bool CanRead
        {
            get { return true; }
        }

        public override bool CanSeek
        {
            get { return true; }
        }

        public override bool CanWrite
        {
            get { return true; }
        }

        public override void Flush()
        {
            throw new InvalidOperationException();
        }

        public override long Length
        {
            get { return readPos; }
        }

        public override long Position
        {
            get => seekPos;
            set => seekPos = value;
        }


        public override int Read(byte[] buffer, int offset, int count)
        {
            int bytesRead = 0;
            while (bytesRead < count)
            {
                int readAheadAvailableBytes = readAheadLength - readAheadOffset;
                int bytesRequired = count - bytesRead;
                if (readAheadAvailableBytes > 0)
                {
                    int toCopy = Math.Min(readAheadAvailableBytes, bytesRequired);
                    Array.Copy(readAheadBuffer, readAheadOffset, buffer, offset + bytesRead, toCopy);
                    bytesRead += toCopy;
                    readAheadOffset += toCopy;
                }
                else
                {
                    readAheadOffset = 0;
                    readAheadLength = sourceStream.Read(readAheadBuffer, 0, readAheadBuffer.Length);
                    //Debug.WriteLine(String.Format("Read {0} bytes (requested {1})", readAheadLength, readAheadBuffer.Length));
                    if (readAheadLength == 0)
                    {
                        break;
                    }
                }
            }
            readPos += bytesRead;
            Position = readPos;
            return bytesRead;
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            Position = offset;
            return Position;
        }

        public override void SetLength(long value)
        {
            throw new InvalidOperationException();
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            throw new InvalidOperationException();
        }
    }

我的意图是使ReadFullyStream易于查找,但实际上我了解到我不了解重要的内容。在ReadFullyStream类中进行了这些更改之后,我尝试了以下操作:

WebRequest request = WebRequest.Create(InputPath);
WebResponse response = request.GetResponse();

using (Stream responseStream = response.GetResponseStream())
{
  ReadFullyStream readFullyStream = new ReadFullyStream(responseStream);
  using (WaveStream streamMediaFoundationReader = new 
                                 StreamMediaFoundationReader(readFullyStream))
  {
    using (WaveOut waveOut = new WaveOut())
    {
      waveOut.Init(streamMediaFoundationReader);
      waveOut.Play();
      while (waveOut.PlaybackState == PlaybackState.Playing)
      {
        Thread.Sleep(100);
      }
     }
   }
}

现在,我在MediaFoundationApi CreateSourceReaderFromByteStream(IMFByteStream byteStream)中收到以下错误:

System.Runtime.InteropServices.COMException:“灾难性失败(HRESULT的异常:0x8000FFFF(E_UNEXPECTED))”。

0 个答案:

没有答案