0

我有一个通过 http 传输视频和音频的 DLink 网络摄像头 (DCS-932L)。

视频是 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);
                }
            }
        }
    }
4

3 回答 3

1

最简单的方法是使用 RawSourceWaveStream,然后传入一个已经跳过 WAV 标头的流。

于 2012-07-16T09:53:01.780 回答
0

有些人通过简单地每 30 秒启动一个新请求就取得了成功。

见: http://forums.ispyconnect.com/forum.aspx?g=posts& m=2717#post2717

但是,如果您想自己破解它,您可以就地编辑 WAV 标头并设置初始 8 字节标头之后的所有数据的长度。初始标头中的第二组 4 个字节基本上是(总文件长度 - 8 个字节),并以小端字节顺序存储为 32 位无符号整数。

因此,在接收到前 8 个字节后,您可以尝试将 WAV 文件大小设置为更大的值,例如UInt32.MaxValue - 8. 但这在大约 4GB 之后仍然有限制,您最终将不得不重新启动您的请求。

您还需要围绕读取和写入添加一些锁定ms,我认为这是一个MemoryStreamms.Position = 0我没有显示任何锁定,但我认为您在创建之前设置时会遇到问题,blockAlignedStream并且当 nAudio 类开始从流中读取时再次遇到问题,因为您还将同时写入请求线程中的流。这将表现为带有大量爆裂声的非常失真的声音。

如果不自行管理锁定,MemoryStream 对于多线程使用是不安全的,并且由于您无法在 NAudio 代码中控制流的锁定,因此您必须创建自己的流来分别管理读取和写入位置,并且处理内部锁定。

请参阅http://www.codeproject.com/Articles/16011/PipeStream-a-Memory-Efficient-and-Thread-Safe-StrePipeStream上的课程。

    ...
            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.

于 2012-07-14T19:49:02.847 回答
0

围绕您从网络摄像头获得的另一个(子类)编写另一个Stream(子类),并就地更改 WAV 标头。它可能会起作用!

于 2012-07-14T20:04:54.293 回答