Thursday, May 29, 2008

We're just about to release a new version of Padarn, out ASP.NET Web Server for Windows CE.  This version brings SSL support as well as Basic and Digest authentication.  Another part of this release was trying to keep the footprint reasonably small.  Here's a screen shot of all of the assemblies needed for the entire engine implementation:

padarnfootprint.PNG

So yes, we have an ASP.NET web server with SSL and authentication support and it's just 305KB - and half of that is for SSL alone.

 

5/29/2008 12:26:56 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Monday, January 21, 2008
A customer asked us to integrate a new camera with Padarn recently (the D-Link DCS-900) to try to do a similar thing that I had done in my PTZ camera demo.  Well this camera was a little different and a whole lot easier to get "streaming" data from.  I ended up writing a small wrapper class that takes the MJPG data coming from the camera, parses it into in-memory Image classes, and then those are available for use.

Once I had that I figured it would be interesting to serve up a Padarn page that did nothing but return the latest frame data from that camera - after all the camera doesn't support PTZ and just a button that takes a picture didn't seem terribly new or informative. 

The problem was that the version of Padarn I was using (1.0.5020) didn't support writing a stream of bytes to the Response.  Well as of 1.0.5030 Padarn now supports WriteBytes (and a few other things like CacheBehavior) and I created a page that dynamically loads the images into an on-page IFrame and allows you to adjust the refresh rate dynamically without ever having to send a full page request in again.

I can see that this could be useful for dynamic updates to a page for something like a control system gauge or for animating building automation status items.

Check out the new sample here.

The code for the DCS-900 looks like this (notice that the ICamera interface changed a little, so the PTZ camera also had to be altered.  I simply stubbed out the new methods).

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Threading;
using System.IO;
using System.Net;
using System.Net.Sockets;
using OpenNETCF.Peripherals.Camera;

namespace OpenNETCF.Peripherals
{
  public class DCS900 : ICamera
  {
    private Image m_lastImage = null;
    private volatile bool m_stopThread = true;
    Thread m_grabberThread;
    private IPAddress m_ip;

    public DCS900()
    {
    }

    public void Initialize(IPAddress ipAddress, string userName, string password)
    {
        m_ip = ipAddress;
    }

    public bool SupportsPanTilt
    {
        get { return false; }
    }

    private int FindFrameBoundary(byte[] data, int offset, int maxLength)
    {
        byte[] frameDelimiter = Encoding.ASCII.GetBytes("--video boundary--");
        int n = 0;
        bool match;

        for (; offset < maxLength; offset++)
        {
            match = true;
            if (data[offset] == frameDelimiter[n])
            {
                match &= true;
                n++;
                if (n >= frameDelimiter.Length)
                {
                    // we've found the header
                    return offset + 1;
                }
            }
            else
            {
                match = false;
                n = 0;
            }
        }

        return -1;
    }

    private Image ImageFromFrameData(byte[] data, int frameLength)
    {
        int start = 50;

        // find the data start - it will be between 50 and 54 bytes in from the frame start
        while (data[start] != 0xFF)
        {
            start++;
            if (start > 54) return null;
            if (data[start + 1] == 0xD9) break;
        }

        try
        {
            using (MemoryStream stream = new MemoryStream(data, start, frameLength - start))
            {
                Image image = new Bitmap(stream);
                stream.Close();
                return image;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex.Message);
        }

        return null;
    }

    public void Start()
    {
        m_grabberThread = new Thread(GrabberThreadProc);
        m_grabberThread.IsBackground = true;
        m_grabberThread.Start();
    }

    public void Stop()
    {
        m_stopThread = true;
    }

    public bool IsRunning
    {
        get { return !m_stopThread; }
    }

    private void GrabberThreadProc()
    {
        lock (m_grabberThread)
        {
            m_stopThread = false;

            Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            s.Connect(new IPEndPoint(m_ip, 80));

            string header = "GET /VIDEO.CGI HTTP/1.0\r\nUser-Agent: user\r\nAuthorization: Basic YWRtaW46REVVU1Q=\r\n\r\n";
            byte[] data = Encoding.ASCII.GetBytes(header);
            byte[] image = new byte[32768]; // 32k buffer is more than adequate for this camera

            s.Send(data);

            data = new byte[2048];

            int offsetStart = 0;
            int offsetEnd = 0;
            int ptr = 0;

            int length = s.Receive(data);

            do
            {
                offsetStart = FindFrameBoundary(data, 0, length);
            } while (offsetStart < 0);

            while (!m_stopThread)
            {
                offsetEnd = FindFrameBoundary(data, offsetStart, length);

                while (offsetEnd < 0)
                {