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))'.
User contributions licensed under CC BY-SA 3.0