Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / multimedia / DirectX

Cam Alarm 2.0 - Alarm system run from a web camera

4.86/5 (33 votes)
16 Jun 2010CPOL3 min read 88.2K   9.5K  
Simple alarm system for those on a budget..

main.jpg

Just a quickie..

I wrote the original version of this in VB6 about 4 years ago, and published it as freeware (might even be floating around the wild somewhere still..), and decided it was a good starter app for learning C#, when I began migrating my skills almost two years ago. So, the uncompleted version of this has been sitting in a folder on my dev box for some time, just waiting for a few tweaks - which I got around to the other day. I thought this would be a good target freeware, because unfortunately, many of us have had the misfortune of being robbed at some point in our lives, and students, young working people, or someone in an apartment may not be able to afford or install a proper alarm system.

I use a Creative 5.1 speaker rig, and with the amp cranked, the siren this sounds is at least as loud as a conventional alarm system, and would probably cause an intruder to bolt rather than attempt to find the source and disarm.

The original used the avicap32.dll library, which seemed to work fine on my older Logitech camera, but will not work on newer devices (this was tested with both an Acer notebook camera, and an MS Lifecam). At this point, I realized I had to go to the DirectShow library to capture video from a wider range of input devices. A search on CodeProject turned up the Motion Detection project by Andrew Kirillov. I removed dependencies on the AForge library, and reworked the AVI reader/writer classes, but the DirectShow wrappers and detection algorithms are his work (thanks Andrew).

The highlights

When the application loads, it tests for the WMV AVI recording codec by enumerating the installed codecs; if it isn't found, a dialog is launched, asking the user to install it. The installation executable is an embedded resource, which is pulled from the resource assembly and recreated as a file in the temp directory, then launched with the ShellExecute API.

C#
private void codecTest()
{
    FilterCollection filters = new FilterCollection(FilterCategory.VideoCompressorCategory);
    bool found = false;
    foreach (Filter filter in filters)
    {
        found = filter.Name.Contains("Media Video 9");
        if (found)
            break;
    }
    if (!found)
    {
        if (MessageBox.Show("The video capture feature requires the Microsoft " + 
            "WMV9 codec. Do you want to install this now?", 
            "Codec Installation Required",
            MessageBoxButtons.OKCancel, 
            MessageBoxIcon.Question) == DialogResult.OK)
        {
            executeCodec();
        }
        else
        {
            _bCanRecord = false;
        }
    }
}

private bool executeCodec()
{
    try
    {
        Assembly assembly = Assembly.GetExecutingAssembly();
        using (Stream stream = assembly.GetManifestResourceStream(
                               "CamAlarm.Resources.wmv9VCMsetup.exe"))
        {
            if (stream != null)
            {
                BinaryReader br = new BinaryReader(stream);
                using (FileStream fs = new FileStream(
                       Environment.GetEnvironmentVariable("TEMP") + 
                       "\\wmv9VCMsetup.exe", FileMode.Create))
                {
                    BinaryWriter bw = new BinaryWriter(fs);
                    byte[] bt = new byte[stream.Length];
                    stream.Read(bt, 0, bt.Length);
                    bw.Write(bt);
                    br.Close();
                    bw.Close();
                    fs.Close();
                }
                executeProgram(Environment.GetEnvironmentVariable("TEMP") + 
                               "\\wmv9VCMsetup.exe");
            }
        }
        return true;
    }
    catch { return false; }
}

A timer is run that waits for the camera to focus in, then the control panels are activated. Settings like the keycode and option control states are stored in the application's default properties. When first run, or if the keycode property is cleared, the Arm/Disarm button acts as the keycode set button. The sounds used in the application are instances of the System.Media.SoundPlayer class, instanced and loaded with a sound file when the app initializes:

C#
private void loadWarning()
{
    if (_cWarningSound != null)
        _cWarningSound.Dispose();
    _cWarningSound = new SoundPlayer(CamAlarm.Resource1.beep);
    _cWarningSound.Load();
}

The camera's motion trigger-state is polled periodically using a custom timer class; if motion has occurred, an alarm timer is fired, which will loop until the alarm is deactivated, or the max alarm timeout occurs:

C#
private void _cAlarmTimer_Tick(object sender)
{
    // timer starts 5 states: standby, armed, arming, triggered, sounding
    //1) if arming warning period elapses, optional beeps
    //2) if not arming, warning sounds and counts to min elapse
    //3) if min elapse and _activetick over threshhold, loops again
    //4) if standby -stop timer
    if (_eAlarmState == AlarmState.Arming)
    {
        _iTimerTick++;
        if (_iTimerTick > stringToInt(txtArmingDelay.Text))
            armAlarm();
        else
            updateStatus();
    }
    else if (_eAlarmState == AlarmState.Armed)
    {
        if (_iActiveTick > 5)
            triggerAlarm();
    }
    else if (_eAlarmState == AlarmState.Sounding)
    {
        _iTimerTick++;
        if (_iTimerTick > stringToInt(txtDuration.Text))
        {
            if (_iActiveTick < 5)
                disarmAlarm();
            else
                //loop again
                soundAlarm();
        }
        else
        {
            soundAlarm();
        }
    }
    else if (_eAlarmState == AlarmState.Standby)
    {
        stopAlarm();
    }
    else if (_eAlarmState == AlarmState.Triggered)
    {
        _iTimerTick++;
        if (_iTimerTick > stringToInt(txtAlarmDelay.Text))
        {
            soundAlarm();
        }
        else
        {
            if (!chkSilent.Checked)
                playWarning();
        }
    }
}

When the alarm is in Armed mode, all but the keypad and disarm switch are disabled. This is done by disabling the GroupBox that houses the controls. The main form close button also needs to be deactivated. This is done both by cancelling the form's exit in the FormClosing event, and by disabling the Close button using the GetSystemMenu/EnableMenuItem API:

C#
private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
{
    if (_bCloseDisabled)
    {
        e.Cancel = true;
    }
    else
    {
        closeCamera();
        closeFile();
        saveSettings();
        if (_cAlarmTimer != null)
            _cAlarmTimer.Dispose();
    }
}

private void enableClose(bool enable)
{
    if (enable)
        EnableMenuItem(GetSystemMenu(this.Handle, false), SC_CLOSE, MF_ENABLED);
    else
        EnableMenuItem(GetSystemMenu(this.Handle, false), SC_CLOSE, MF_GRAYED);
    
    _bCloseDisabled = !enable;
}

All in all, fairly simple approach; though I wasn't all that generous with comments in code, it is fairly straightforward..

Using the application..

Keep the camera view up where pets won't trigger it, and away from windows. Crank the volume on your speakers, and adjust application volumes through the mixer; CamAlarm has an option to push to the maximum volume when the alarm is activated, but this won't mean much if the master volume is down low.

Well.. enjoy! If you find any bugs, be sure to leave a message.

Change log

May 17, 2010

  • First time loading of help page creates page in user app folder.
  • Alarm volume raised after triggering warning beeps.
  • Fixed lock when capturing video, changed from Monitor.Enter to TryEnter in Camera.cs.
  • Added tooltip to Clear and Video buttons.
  • Added menu buttons to disabled items when alarm is activated.

June 16, 2010

  • Fixed codec URL in executeCodec().
  • Fixed disabled OK button in device selection form.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)