How to play AAC audio stream using NAudio?

0

In the application I am currently working on I would like to play AAC audio stream. For example, this internet radio: http://stream.bauermedia.fi/iskelma/kem_64.aac. I would like to use NAudio framework as it is already used in this project.

To start with, I examined Mp3StreamingDemo in NAudioDemo. I created a class ADTSFrame where I processed ADTS frame header (https://wiki.multimedia.cx/index.php/ADTS). From ADTS frame header I got the next information: sampling frequency, number of channels, frame length. Then I created a separate class ADTSWaveFormat with one private field waveFormatTag and two properties Channels and SampleRate. WafeFormatTag is set to WaveFormatEncoding.MPEG_ADTS_AAC. As I have understood from here - https://docs.microsoft.com/en-us/windows/desktop/DirectShow/audio-subtypes, no more information is needed. However, when I wanted to get PCM format, AcmInterop.acmFormatSuggest() returned "NoDriver". So I am stuck here since I do not understand if I am moving in the right direction.

Then I searched in the Internet and found the information, that there is a class StreamMediaFoundationReader in NAudio. StreamMediaFoundationReader wants to get a stream in its constructor. First of all this stream is converted to ComStream and then to IMFByteStream. When ComStream is converted to IMFByteStream, Seek operation is performed. So ConnectStream that I get from response.GetResponseStream() does not suit to give to StreamMediaFoundationReader's constructor as it is not seekable. So I decided to use memory stream.

Until now the most successful code I had written was:

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);
    }
  }
}

As you see, this code does not suit anywhere as it plays only for a moment.

I also have tried this:

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);
    }
  }
}

But here I get ContextSwitchDeadlock as memoryStream is used in different threads.

By now I do not have much working experience in programming.

Could please anybody give me an advice how playing audio stream can be done using NAudio?

Today I have also tried to edit ReadFullyStream in the next way:

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();
        }
    }

My intention was to make ReadFullyStream seekable, but actually I understand that I do not understand something important. After I had made these changes in the ReadFullyStream class, I tried the following:

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);
      }
     }
   }
}

Now I get the following error in the MediaFoundationApi CreateSourceReaderFromByteStream(IMFByteStream byteStream):

System.Runtime.InteropServices.COMException: 'Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))'.

c#
naudio
naudio-framework
asked on Stack Overflow Oct 9, 2018 by Liidia • edited Oct 10, 2018 by Liidia

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0