简体   繁体   English

使用NAudio播放(无效)音频流

[英]Play (invalid) audio stream with NAudio

I've got a DLink web cam (DCS-932L) that stream video and audio via http. 我有一个DLink网络摄像头(DCS-932L),可通过http流视频和音频。

The video is mjpeg (motion jpeg) while audio is 16 bit PCM wav audio in mono. 视频为mjpeg(运动jpeg),而音频为单声道16位PCM wav音频。

I'm able to read the show the video stream fine, but I'm having issues with the audio. 我可以很好地阅读节目的视频流,但是音频却有问题。 According to the received headers the audio file is only 30 seconds long, but that is false as the camera continues to send data for ever (checked with wget). 根据接收到的标头,音频文件只有30秒长,但这是错误的,因为摄像头将继续永远发送数据(已通过wget检查)。

Both NAudio, VLC, windows media player etc all stop after 30 seconds as the wav headers say they should. NWA,VLC,Windows Media Player等都在30秒后停止,如wav标头所示。 Anyone know of a way to make NAudio discard the length property of the stream header? 有人知道让NAudio丢弃流头的length属性的方法吗? Or any other library that I can use that handles this? 还是我可以使用的其他任何可以处理此问题的库?

The code I use today that plays 30 seconds is: 我今天使用的播放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标头的流。

Some have had success by simply starting a new request every 30 seconds. 有些仅通过每30秒启动一个新请求就成功了。

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

However, if you want to hack it yourself, you can edit the WAV header in-place and set the length of all of the data that follows the initial 8 byte header. 但是,如果您想自己修改它,则可以就地编辑WAV标头,并设置初始8字节标头之后的所有数据的长度。 The second set of 4 bytes in the initial header is basically the (total file length - 8 bytes), and is stored as a 32-bit unsigned integer in little endian byte order. 初始标头中的第二个4字节集基本上是(总文件长度-8字节),并以小尾数字节顺序存储为32位无符号整数。

So, after receiving the first 8 bytes, you can try setting the WAV file size to something much larger, say UInt32.MaxValue - 8 . 因此,在收到前8个字节后,您可以尝试将WAV文件的大小设置为更大的值,例如UInt32.MaxValue - 8 But this still has a limit after about 4GB, and you will eventually have to restart your request. 但这大约有4GB的限制,最终您将不得不重新启动请求。

You will also need to add some locking around reading and writing to ms , which I assume is a MemoryStream ? 您还需要在读写ms上增加一些锁定,我认为这是MemoryStream吗? I haven't shown any locking, but I think you will run into problems when you set ms.Position = 0 before you create the blockAlignedStream and again when the nAudio classes start reading from the stream, because you will also be writing simultaneously to the stream in the request thread. 我没有显示任何锁定,但我认为在创建blockAlignedStream之前设置ms.Position = 0以及在nAudio类开始从流读取时再次设置时都会遇到问题,因为您还将同时向流在请求线程中。 This will manifest itself as very distorted sound with lots of pops. 这会表现为非常失真的声音,并带有很多爆破声。

MemoryStream is not safe for use by multiple threads without managing the locking yourself, and since you can't control the locking of the stream in the NAudio code, you'll have to create your own stream that manages read and write position separately, and that handles locking internally. 如果不自行管理锁定,则MemoryStream不能安全地用于多个线程,并且由于无法控制NAudio代码中流的锁定,因此必须创建自己的流来分别管理读写位置,并且内部处理锁。

See the PipeStream class at http://www.codeproject.com/Articles/16011/PipeStream-a-Memory-Efficient-and-Thread-Safe-Stre . 请参阅位于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; 
                }
            } 

    ...

Ultimately, if you want to read forever without any limits, you will have to interpret the stream's RIFF structure, pull out the sample data, and perform the nAudio playback using the raw samples instead of the WaveFormatConversionStream . 最终,如果您想永久阅读而没有任何限制,则必须解释流的RIFF结构,提取样本数据,并使用原始样本而不是WaveFormatConversionStream进行nAudio播放。

Write another Stream (subclass) around one you get from the webcam, and alter WAV header in-place. 围绕从网络摄像头获取的内容编写另一个Stream (子类),并就地更改WAV标头。 It might work! 可能有效!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM