0
votes

I've got a DLink web cam (DCS-932L) that stream video and audio via http.

The video is mjpeg (motion jpeg) while audio is 16 bit PCM wav audio in mono.

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

Both NAudio, VLC, windows media player etc all stop after 30 seconds as the wav headers say they should. Anyone know of a way to make NAudio discard the length property of the stream header? Or any other library that I can use that handles this?

The code I use today that plays 30 seconds is:

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);
                }
            }
        }
    }
3

3 Answers

1
votes

The easiest way would be to use RawSourceWaveStream, and then pass in a stream that has already skipped over the WAV header.

0
votes

Write another Stream (subclass) around one you get from the webcam, and alter WAV header in-place. It might work!

0
votes

Some have had success by simply starting a new request every 30 seconds.

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

So, after receiving the first 8 bytes, you can try setting the WAV file size to something much larger, say UInt32.MaxValue - 8. But this still has a limit after about 4GB, and you will eventually have to restart your request.

You will also need to add some locking around reading and writing to ms, which I assume is a 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. 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.

See the PipeStream class at http://www.codeproject.com/Articles/16011/PipeStream-a-Memory-Efficient-and-Thread-Safe-Stre.

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