Cyber-Monday deals on Netcams compatible with Sensr

Just a quick post today. Amazon has some great deals on two cameras that work well with Sensr.

I’ve written about using the D-Link DCS-920 on a number of occasions.  Today it is available for $49 on Amazon.

http://www.amazon.com/D-Link-DCS-920-Wireless-G-Internet-Camera/dp/B001764PVQ/ref=pd_cp_p_2

The Trendnet TV-IP110W is another camera that works great with Sensr.  This one has a rock-solid WiFi implementation and we’ve been running it at the Sunfire offices.  It’s available on Amazon today for $59.

http://www.amazon.com/TRENDnet-SecurView-Wireless-Surveillance-TV-IP110W/dp/B00125KR1E/ref=pd_bxgy_p_text_b

UVC Webcam: Creative Live! Cam Socialize HD

This post is about two things: “Universal Video Class” (UVC) USB Webcams, and my specific experiences with the “Creative Live! Cam Socialize HD” – a great camera with a completely unwieldy name.

UVC

Webcams and drivers: what horrible experiences most of us remember.  And if you’re running camera software on Linux the driver problems are only magnified.

UVC (“Universal Video Class” or “USB Video Class”) is a standard that describes capabilities of generic video devices like webcams.  UVC drivers are now built into Linux (Linux 2.6.26 on), Mac and Windows.  UVC webcams can be used directly with Linux and get mounted as /dev/video0 automatically.  No drivers to install!

UVC cameras have been around for quite a while.  (I have an old Aiptek pocket video recorder that can be used as a UVC webcam.)  It seems that more UVC cameras are showing up on the market, but they aren’t always clearly marked. If the box says support for “Linux Kernel 2.6” then it’s UVC.

I found an off-brand UVC webcam at OfficeMax for $20 and brought it to the lab to test with our embedded Linux video server.  Yuck.  The picture jittered occasionally as if there was a horizontal-sync problem.  You get what you pay for!

There is a site dedicated to UVC webcams.  It has not been a great resource for me, but it’s something.

http://www.uvccompatiblewebcams.com/

Creative Live! Cam Socialize

Thus, it was with great pleasure I found a brand-name UVC webcam for a reasonable price.  I found the “Creative Live! Cam Socialize” at Fry’s for $69.  It’s HD with a 720p sensor.  It works great with Linux.  It has a rock steady picture, and automatically adjusts color and lightlevel.  I have been loving this little camera and use it as a stationary surveillance device: I don’t use it to chat.

http://www.engadget.com/2010/11/16/creative-hits-1080p-with-its-live-cam-socialize-hd-webcam-laun/

This week that I found out that Creative had expanded the line and reduced the prices.  The names of the products have only gotten worse though.  Here they are.

  • Creative Live! Cam Socialize HD 1080
  • Creative Live! Cam Socialize AF
  • Creative Live! Cam Socialize Chat

The “HD 1080” is a true 1080p sensor, has stereo microphones and lists for $89.  The “AF” model has a 720p sensor.  The “AF” stands for “autofocus.” It lists for $69.  The “Chat” model also has a 720p sensor and it lists for $39.  I’ll probably be using a lot of the “Chat” models.

This announcement seems to have driven a price-drop on the current model at Amazon.  The old Socialize HD model is available for $37 today.

http://www.amazon.com/Creative-Labs-Live-Socialize-Webcam/dp/B002OAVQJ8

I just ordered two of them.

Python multithreaded daemon with SIGTERM support (a recipe)

I like to use Python to stitch together little programs into services that run as daemons.  Python makes it easy to run multiple threads with modern synchronization constructs and to run Unix commands and pipes using the “subprocess” module.  However, I was having trouble cleanly killing my threads and subprocesses when I wanted to restart the daemon.  Some of the reports I had read on the web warned against using multithreading, subprocesses, daemonization and signals together, but it’s all working fine for me.  I thought I’d share my recipe and some of what I had learned here.

Python does not have a standard daemon module

Creating a daemon on Unix requires a “double-fork” recipe that I can only say is partly magic.  The recipe cited here seems to be the most-often cited resource for how to fork a daemon.  It dates from 2001.

http://code.activestate.com/recipes/66012/

One may wonder why this recipe hasn’t been turned into a standard Python daemon package.  I can think of two reasons.  One: the code is small enough that it doesn’t deserve to be a package.  Two: a good package would handle many possible daemonization needs.  This recipe imposes restrictions on the daemon and only works for simple cases.

If you’re will to accept its restrictions (like only the normal IN/OUT/ERR file descriptors open) then it seems to be fine.  I was quite happy to keep my service simple.  Stdout and stderr are the only two streams used, and the only signal handled is SIGTERM.

In the end, I chose to use the variant posted by Clark Evans here that added the daemon start/stop/restart behavior.

http://code.activestate.com/recipes/66012/#c9

Python masks signals to subprocesses

This is where I got a little hung-up.  My service was running subprocesses: both as simple commands and as long-running threads. I was having a hard time terminating sub-programs when I wanted my service to stop.

I found a great resource in this article:

http://www.doughellmann.com/PyMOTW/subprocess/

Python masks the signals to subprocesses it starts.  (Here “masks” means “does not propagate”) If you use the subprocess module to start a long-running job like this “sleep” command:

 import subprocess
 job = subprocess.Popen(["sleep", "6000"])
 (se, so) = job.communicate()

and then kill the parent Python process with a SIGTERM, the child “sleep” process will keep on running.  The article referenced above explains how to set a Unix “session ID” (os.setsid) and how to send a signal to a “process group” (os.killpg).  I modified the technique a little bit in the interest of simplification.

Use the threading.setDaemon() flag

Python will not exit if normal threads are still running: you must shut them down explicitly.  However, Python will exit if only “daemon” threads are running.  A daemon thread is simply something you deem unimportant enough that it does not require an explicit shut-down step.  Write a daemon thread like this:

class myThread(threading.Thread):
  def __init__(self, args ...):
  ...
  threading.Thread.__init__(self)
  self.setDaemon(True)

Putting it all together

SIGTERM_SENT = False

def sigterm_handler(signum, frame):
    print >>sys.stderr, "SIGTERM handler.  Shutting Down."

    global SIGTERM_SENT
    if not SIGTERM_SENT:
        SIGTERM_SENT = True
        print >>sys.stderr, "Sending TERM to PG"
        os.killpg(0, signal.SIGTERM)

    sys.exit()

def main():
    # set session ID to this process so we can kill group in sigterm handler
    os.setsid()
    signal.signal(signal.SIGTERM, sigterm_handler)

    while True:
        # ... run daemon threads and subprocesses with impunity.

    # ... this function never returns ... 

from daemonize import startstop

if __name__== "__main__":
    startstop(stdout="/var/log/example.log",
              pidfile="/var/run/example.pid")
    main()

Here’s what we came up with.  (Start at the bottom.)  The startstop function does the daemonization double-fork.  Thus, the PID of the daemon is a grandchild of starting Python process.  When main() is called, the effective PID is that of the daemon.  Calling os.setsid() sets the session id to the PID of the daemon.  (The PID is the one written to the pid-file).

The sigterm handler is called when a SIGTERM arrives.  Its main purpose is to send SIGTERM to the process group of the session.  (Recall that Python has masked the signals to its subprocess children, so we have manage sending the signals.)  This step terminates the child processes.  However, it also re-sends SIGTERM to the main daemon process – which we didn’t want.  We use the SIGTERM_SENT flag so that on the second receipt of the TERM signal, we ignore it.

In the end, this recipe is fairly simple.  Your mileage may vary, but if keep your use of subprocesses and threads simple, this might work for you too.