我有一个DLink网络摄像头(DCS-932L(,可以通过http流式传输视频和音频。
视频是 mjpeg(运动 jpeg(,而音频是单声道的 16 位 PCM wav 音频。
我能够很好地阅读视频流,但我的音频有问题。根据收到的标头,音频文件只有 30 秒长,但这是错误的,因为摄像机会继续永远发送数据(使用 wget 检查(。
NAudio,VLC,Windows 媒体播放器等都在 30 秒后停止,因为 wav 标头说它们应该停止。有谁知道让 NAudio 丢弃流标头的长度属性的方法?或者我可以使用的任何其他库来处理这个问题?
我今天使用的播放 30 秒的代码是:
public void PlayWaveFromUrl(string url)
{
new Thread(delegate(object o)
{
var req = WebRequest.Create(url);
req.Credentials = GetCredential(url);
req.PreAuthenticate = true;
var response = req.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 WaveFileReader(ms))))
{
using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
{
waveOut.Init(blockAlignedStream);
waveOut.Play();
while (waveOut.PlaybackState == PlaybackState.Playing)
{
System.Threading.Thread.Sleep(100);
}
}
}
}
最简单的方法是使用 RawSourceWaveStream,然后传入已跳过 WAV 标头的流。
围绕您从网络摄像头获得的Stream
(子类(编写另一个(子类(,并就地更改 WAV 标头。 它可能会起作用!
有些人通过每 30 秒启动一个新请求而取得了成功。
参见:http://forums.ispyconnect.com/forum.aspx?g=posts&m=2717#post2717
但是,如果您想自己破解它,您可以就地编辑 WAV 标头并设置初始 8 字节标头后面的所有数据的长度。 初始标头中的第二组 4 个字节基本上是 (总文件长度 - 8 个字节(,并以小端字节顺序存储为 32 位无符号整数。
因此,在收到前 8 个字节后,您可以尝试将 WAV 文件大小设置为更大的大小,例如 UInt32.MaxValue - 8
. 但是在大约 4GB 之后这仍然有限制,您最终将不得不重新启动您的请求。
您还需要在读取和写入ms
中添加一些锁定,我认为这是一个MemoryStream
? 我没有显示任何锁定,但我认为在创建blockAlignedStream
之前设置ms.Position = 0
时以及当 nAudio 类开始从流中读取时再次遇到问题,因为您还将同时写入请求线程中的流。 这将表现为非常失真的声音,并带有大量爆裂声。
如果不自己管理锁定,MemoryStream 对于多个线程使用是不安全的,并且由于您无法在 NAudio 代码中控制流的锁定,因此您必须创建自己的流来分别管理读取和写入位置,并在内部处理锁定。
请参阅 http://www.codeproject.com/Articles/16011/PipeStream-a-Memory-Efficient-and-Thread-Safe-Stre 的PipeStream
类。
...
byte[] buffer = new byte[65536]; // 64KB chunks
int read;
long totalRead;
int bufferPosition = 0;
bool changedLength = false;
while ((read = stream.Read(buffer, bufferPosition, buffer.Length)) > 0)
{
totalRead += read;
if (!changedLength)
{
if (totalRead < 8)
{
// need more bytes
bufferPosition += read;
continue;
}
else
{
const uint fileLength = uint.MaxValue - 8;
buffer[4] = (byte)(fileLength && 0xFF);
buffer[5] = (byte)((fileLength >> 8) && 0xFF);
buffer[6] = (byte)((fileLength >> 16) && 0xFF);
buffer[7] = (byte)((fileLength >> 24) && 0xFF);
changedLength = true;
bufferPosition = 0;
var pos = ms.Position;
ms.Position = ms.Length;
ms.Write(buffer, 0, (int)totalRead);
ms.Position = pos;
}
}
else
{
var pos = ms.Position;
ms.Position = ms.Length;
ms.Write(buffer, 0, read);
ms.Position = pos;
}
}
...
最终,如果您想不受任何限制地永久阅读,则必须解释流的 RIFF 结构,提取示例数据,并使用原始样本而不是WaveFormatConversionStream
执行 nAudio 播放。