簡體   English   中英

使用NAudio播放(無效)音頻流

[英]Play (invalid) audio stream with NAudio

我有一個DLink網絡攝像頭(DCS-932L),可通過http流視頻和音頻。

視頻為mjpeg(運動jpeg),而音頻為單聲道16位PCM wav音頻。

我可以很好地閱讀節目的視頻流,但是音頻卻有問題。 根據接收到的標頭,音頻文件只有30秒長,但這是錯誤的,因為攝像頭將繼續永遠發送數據(已通過wget檢查)。

NWA,VLC,Windows Media Player等都在30秒后停止,如wav標頭所示。 有人知道讓NAudio丟棄流頭的length屬性的方法嗎? 還是我可以使用的其他任何可以處理此問題的庫?

我今天使用的播放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標頭的流。

有些僅通過每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-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進行nAudio播放。

圍繞從網絡攝像頭獲取的內容編寫另一個Stream (子類),並就地更改WAV標頭。 可能有效!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM