# Thursday, January 03, 2008
Thursday, January 03, 2008 4:51:06 PM (Central Standard Time, UTC-06:00)  #     | 
As it seems is always the case, as soon as I published the WIA library, I found some problems.  The original library worked fine in the test application, but as soon as I moved it into a larger-scale application that does a lot of wacky multi-threading, multi-AppDomain and reflection calls I started to see bad behavior.  It all worked fine if you kept the original list of cameras, but if you added or removed them they started shifting around or existing cameras no longer worked.  So I've debugged and reworked it a bit and the new version appears to be much more stable and friendly in a production environment. 

Download the code at the original blog entry.

Thursday, January 03, 2008 4:35:38 PM (Central Standard Time, UTC-06:00)  #     | 
# Monday, December 31, 2007
Last Christmas (2006) I gave my father a USB turntable so he could start ripping his hundreds upon hundreds of vinyl albums to MP3.  Of course I assumed that all was going well for most of the year, as he was using it and seemed happy with it. 

Then about 2 months ago I actually got a copy of the Simon and Garfunkel song Cecilia from him (don’t tell the RIAA) and I noticed that it didn’t show up in my Zune software or in WMP.  A little further digging and I found it down in “unknown artist, unknown album, unknown song”.  How wonderfully useful.  So I asked him why the hell it didn’t have the track info.  He replied that the software he has basically ripped the vinyl to one giant wave file, then another piece of software would divide it into MP3 tracks, but it didn’t have a good way of tagging each track efficiently.  To give it track info, he’d have to pull it into some other software like WMP and alter it there, which is a very tedious process.

Well I decided to take a look into how media playback software actually got the info about the tracks.  I assumed it was something embedded in the software so I did a little searching for a specification, and found that the info is indeed stored in what is called an ID3 header tag.  Well since I enjoy writing software from nothing but a spec (really, I do – masochistic I know) I decided that maybe I’d put together some software that makes tagging albums just a bit easier using mostly drag and drop and inferring info based on how it’s organized in the file system.

The first step, however was to create a library that allowed me to read and write the ID3 tags.  That yielded the OpenNETCF.Media.MP3 library (this library does *not* offer MP3 playback capabilities, so don’t ask).

Once I had the library mostly done (meaning coded but not heavily tested) I then started on a desktop application I called AudioTagR.  The idea behind AudioTagR is that most people organize their music on their file system.  My dad and I both have a single folder that contains a folder for each artist.  Each artist folder contains one or more folders for each album.  Each album folder contains files that are the MP3 songs, and the song filenames are in the format “NN <song name>.mp3” where NN is the track number from the album.  AudioTagR assumes that you use this hierarchy to infer a lot of the information about unknown tracks (though it allows you to turn off inferring).

So the paradigm is that on the right is a folder view of the file system, with a root being the “root” of where your music is stored.  On the left is an “organizer” that is used for nothing but tagging tracks through inference.  You drag a song from the file tree into the organizer tree and it infers artist, album, song name and track if nothing exists.  If some info exists, or you drop it onto a node in the ordanizer that already exists, it will use node info instead of inferring.  The actual algorithm is a little more complex than I feel like explaining, but it all makes logical sense when you use it, so if you really want to know how it works, try it out.


So the day before Christmas I delivered v1 to him, and less than 2 minutes later he found the first bug.  After a couple versions of deploying via FTP, I decided there had to be a better mechanism.  Since this is a desktop app (yes, sometimes I begrudgingly work on non-CE stuff – but hey, the ID3 tag library is fully CF-compatible) I decided I’d see what the whole ClickOnce deployment and publishing stuff was about.  Sure enough, it turned out to be crazy simple, so I set up a publication that allows installation from a web page and now the software auto-updates on his machine every time I make a fix.

So now  that it’s done, I guess that there may be other people out there that received these nice USB turntables and are having a similar problem, so I say Merry Christmas to you all.  I’m giving away AudioTagR, along with the full source if you want it, to everyone as another OpenNETCF shared source project (MIT X11 license).  Since it is free, you get no support, and I take no responsibility for its use or consequences.  If you screw up the titles on all of your existing music I’m sorry but it’s not my fault.  Make a backup.  If you find a bug, by all means let me know and I’ll see what I can do to fix it.  If you add a feature that you like, send me the code and I can integrate it in.
 

If you simply want to install and use AudioTagR, click here.

Download the full source (C#) here
Get the OpenNETCF.Media.MP# library source here

Monday, December 31, 2007 2:41:33 PM (Central Standard Time, UTC-06:00)  #     | 
Before I go any further, let me state for the record that this library does not provide any ability to play MP3 files, so if that's what you were thinking from the name, then don't get too excited.

The OpenNETCF.Media.MP3 library is a library that allows you to read and write ID3 tags to MP3 files.  It's both Compact Framework 2.0 and Full Framework 2.0 compliant.  For a usage sample, see the AudioTagR project.

The general object model looks like this:



It's licensed under our very open MIT X11 license.
Download the full source code (C#) here.

Monday, December 31, 2007 2:25:40 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, December 21, 2007
For the last couple weeks I've been struggling with getting camera capture inside a desktop application (yes - I lower myself to do desktop development occasionally).  The problem I'm finding is that API documentation and especially sample availability for things like DShow and Windows Image Acquisition (WIA) are pretty poor and there seem to be no reasonable, comprehensize samples on how to use any of it.  Occasionally you find something on sites like CodeProject, but IMO those projects tend to suck.  The authors typically "solve" a specific problem by writing code tightly coupled to the problem they're solving, so the code is very rarely reusable or extensible, and the quality usually is lacking as well.

Well today I finished up a general library and test harness for WIA.  It's too soon to tell if we'll actually use it in the application I'm working on, since it's missing a lot of capabilities that DShow will likely be better for, but to save a little headache in the developer community, I'm making it shared source (under our typical MIT x11 license).  It provides a simple object model for seeing what cameras you have, displaying live video from them and grabbing frames.  The test harness generates a TabControl with a page for each camera you have plugged in.  If you add or remove a camera while the app is running, tabs are added or removed.

Most importantly I designed the clas library for general use.  It can be dropped into any project and consumed for camera control without a bunch of rewrite, and while I'm not going to say it's bug free, I at least tested it though several scenarios and let it run for a fairly long time without it puking (which is more than I can say about pretty much anything else I found).

Below is the object model and a screen shot of the test app.





Download the source code here (Updated 1/3/08).
Friday, December 21, 2007 3:14:58 PM (Central Standard Time, UTC-06:00)  #     | 
# Friday, December 14, 2007
We here at OpenNETCF are huge proponents of teaching technology in an academic environment.  It still surprises me that both Mobile and Embedded computing are entwined in our everyday lives and their presence is growing rapidly, yet very, very few schools offer any sort of courses covering them. 

That's why it's always interesting to see that when mobile development is being taught, students sometimes turn to our libraries for the exact same reasons that non-academic customers do - so they can get more done in less time (I've always considered people that won't use third party tools foolish, as they spend a lot of time and opportunity cost developing something that's already been done).

If you're looking for a school that actually teaches material that is relevent in our industry, Georgia Tech seems to have a pretty strong mobile and embedded curriculum.  I just saw that a team put together a war-walking application this semester using the SDF.  We've shipped them some more software and we're keen to see how they use it in the coming semesters.

Friday, December 14, 2007 8:43:56 AM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, December 13, 2007
Today we rolled out an update to Padarn: version 1.0.5010.

"So what's new," you ask? 

  1. We added support for the Page.Request.Browser property.  On the surface it seemed quite simple, but the implementation was actually quite a challenge.
  2. We fixed a bug that Page.Request.Headers always came back null
  3. We updated the SampleSite demo including:
    1. Updates and additions to the OpenNETCF.Web.Html namespace
    2. A new Camera Demo page that controls a camera in our "server closet"
    3. A page showing hosting the WMP control service media off of Padarn
    4. A page that shows client browser capability detection
The docs and live page are updated - you can reach them both from here:  www.opennetcf.com/padarn.ocf


We are also now offering Padarn free of charge for academic use.  Contact us for details.

Thursday, December 13, 2007 6:26:16 PM (Central Standard Time, UTC-06:00)  #     | 
# Thursday, December 06, 2007

So I picked up a cheap PTZ (pan/tilt/zoom) camera off eBay a couple weeks ago for doing some R&D on a project I'm working on.  My hope was I'd be able to hook it up and talk to it from a device and get streaming video into an app.  Well it turns out that it's not so simple.  All cameras appear to have some form of proprietary interface that obviously varies from OEM to OEM.

I hooked up WireShark in hopes that I could reverse engineer the network commands fromthe packets, but it looks like it would take me weeks to get it figured out, and I don't have that kind of time (or desire) so I shot off an email to the OEM.  While I wait for them to reply (if they ever do) I went about reverse-engineering a kludge.

The device has a built in web server that allows you to control the camera, view video, etc.  So I opened up a page and looked at the source.  It contained a frameset, so I started navigating through frame pages and deconstructing the html to see what commands did what.  Unfortunately the video streaming piece appears to be in a compiled Java applet so getting at that turned out to be a dead-end, however it wasn't a total loss.  I did figure out how to send it commands to pan, tilt and capture single frames (well I figured out a lot more, but I limited my implementation to those functions for now).

So armed with what I knew, I slapped together the following class, basically simulating myself as a browser:

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

namespace OpenNETCF.Peripherals
{
  public class MegaTecCamera : ICamera
  {
    private const int CMD_UP = 1;
    private const int CMD_DOWN = 2;
    private const int CMD_LEFT = 3;
    private const int CMD_RIGHT = 4;

    private string m_ip;
    private string m_username;
    private string m_password;

    public MegaTecCamera(IPAddress address, string username, string password)
    {
        m_ip = address.ToString();
        m_username = username;
        m_password = password;
    }

    public Image GetImage()
    {
        Image img = null;

        string command = string.Format(http://{0}/pda.cgi?user={1}&password={2}&page=image&cam=1
            m_ip, m_username, m_password);

        byte[] buffer = new byte[10000];
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(command);
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        using (Stream stream = response.GetResponseStream())
        {
            try
            {
                img = new Bitmap(stream);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Capture failed: " + ex.Message);
            }

            response.Close();
        }
        return img;
    }

    public void Pan(PanDirection direction)
    {
        if (direction == PanDirection.Left)
            SendHttpCommand(GetDirectionCommand(CMD_LEFT));
        else
            SendHttpCommand(GetDirectionCommand(CMD_RIGHT));
    }

    public void Tilt(TiltDirection direction)
    {
        if (direction == TiltDirection.Up)
            SendHttpCommand(GetDirectionCommand(CMD_UP));
        else
            SendHttpCommand(GetDirectionCommand(CMD_DOWN));
    }

    private string GetDirectionCommand(int direction)
    {
        return string.Format(http://{0}/pda.cgi?user={1}&password={2}&page=execute&cam=1&command={3},
            m_ip, m_username, m_password, direction);
    }

    private void SendHttpCommand(string command)
    {
        ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object o)
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(command);
            try
            {
                request.GetResponse().Close();
            }
            catch { }
        }));
    }
  }
}

The next obvious question is "What the hell do I do with this class that is of any use?"

Well that's the fun part!  I integrated it into a sample page on our demo Padarn server, so we now have images captured from an IP camera streamed back to a 200MHz Windows CE device that in turn serves up those images (and control of the camera) using an ASP.NET server.

The next step I'll add is the ability to turn on and off the light in the room via a web page.

 

Thursday, December 06, 2007 5:15:00 PM (Central Standard Time, UTC-06:00)  #     |