在我目前正在处理的应用程序中,我想播放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))”。