I recently purchased the Logitech Alert 750n Outdoor security system. The cameras are wonderful 720p cameras with night vision. My favorite thing about the cameras is their ability to run independently. They run a full linux OS and the software onboard is capable of motion detection, automatic night vision, cycling recordings on the provided 2GB microSD, and of course hosting a couple of RTSP streams. I currently use 6 Logitech 700e cameras on powerline. I run the Logitech Alert desktop app on Windows Server 2008 R2 which simply grabs the recordings as they come up and saves them to a local Samba share.

My problem with the cameras started when I opened the Logitech Alert app on iOS. The app, quite frankly, is horrible. The design is fine but the cameras are always “Loading…” even inside the house on the same LAN and the situation doesn’t get better outside the home. The next problem was to view recent recordings on iOS you needed to pay $80/yr which I personally cannot justify since it uses my own bandwidth to access the SD card on the cameras to play the clips.

So, to get around all of these things I had to make my own solution. The first problem was easily solved by using the RTSP streams already available. I noticed OPlayer HD on iOS can open RTSP streams with ease so I created a website with links to the cameras using jQuery mobile and told the browser to cache the page. Of course this only works on the LAN since I do not open the streams out over the WAN. This was an easy fix however since I already have a VPN server running I can just turn on VPN in iOS and the streams work perfectly afterwards. The jQuery mobile site was a very basic listview and thus will not be shared here since its just my private RTSP streams. However, the URL for the stream is as follows:

rtsp://admin:[email protected]:554/LowResolutionVideo

The IP is DHCP however I personally wrote down all the MAC addresses from the cameras before plugging them in and set static IPs through my router. This ensures my links are never broken. Also, I stream the LowResolutionVideo because the iPad seems to have less issues with that (could be a wifi issue).

Here is a webapp screenshot (I have spent 0 time improving the layout/look for now)
IMG_0334

The second thing was accessing the recordings. I noticed Logitech organizes the videos very well (eg. C:\Cameras\Camera #1 (MAC ADDRESS)\2013\01\14\LMR S102345.3289083-600 D2389 A893 E33.mp4) so I simply opted to use AirVideo server to stream the Camera folder and since the camera saves the files as H.264 encoded mp4 the work great on iOS. This worked however after a while it was hard to read the timestamps on the filenames so I wrote a php script that automatically runs every 5 minutes. The script does two things. First, it generates a relative Today symlink to each camera root directory linking to the latest day available. Second, it creates a Latest directory and symlinks 31 of the most recent clips and names them in the same format as the Logitech Alert iOS app (eg. 10.23.45 AM, Mon, Jan 14, 2013.mp4). I also modify the access times of each symlink to the original video time allowing a sort by time on the AirVideo client. The following is the PHP currently used to create the symlinks:

#!/usr/bin/php5
<?php

#Set base directory to camera storage
$base_dir = "/mnt/av/Cameras";

#Switch to the base directory
if (!chdir($base_dir)) exit();

#Get camera list
$cameras = directoryToArray(".",false,true,false);

#Cycle through every camera
foreach ($cameras as $camera) {

    #Change into camera directory
    if (!chdir($camera)) continue;

    #Set Today symlink location
    $today = "./Today";

    #Set Latest directory location
    $latest = "./Latest";

    #If Today is already set then remove it
    if (is_link($today)) unlink($today);

    #Get all files in camera directory excluding xml and new incoming recordings
    $files = directoryToArray(".",true,false,true,'/\.media$|\.xml$/');

    #Get all directory structure for camera
	$tree = directoryToArray(".",true,true,false,'/Latest/');

    #Sort directory trees from high to low
	rsort($tree); rsort($files);

    #Symlink the Today directory to the newest day available
	symlink($tree[0],$today);

    #If Latest directory exists then empty it otherwise create it
    if (is_dir($latest)) {
        empty_dir($latest);
    } else {
        mkdir($latest);
    }

    #Set latest count to 0
    $x = 0;

    #Cycle through every file
    foreach ($files as $path) {

        #If 30 entries have been written then exit
        if ($x>30) break;

        #Explode directory to file
        $file = explode("/", $path);

        #Get total subdirectory count
        $i = count($file);

        #Get extension (should be .mp4 but just in case)
        $extension = ".".end(explode(".", $file[$i-1]));

        #Set the filename to the last entry
        $name = explode(" ", $file[$i-1]);

        #Check if we have an LMR
        if ($name[0] !== "LMR") continue;

        #Set the time to the 2nd entry of the file but subtract the leading S
        $time = substr($name[1], 1);

        #Set the day to the directory prior
        $day = $file[$i-2];

        #Set the month from two directories prior
        $month = $file[$i-3];

        #Set the year from three directories prior
        $year = $file[$i-4];

        #Get unix timestamp from date and time
        $timestamp = strtotime("$month/$day/$year ".substr($time, 0, 2).":".substr($time, 2, 2).":".substr($time, 4));

        #Set the new filename
        $new_name = date('h.i.s A D, M d, Y', $timestamp).$extension;

        #Symlink file to Latest
        symlink(".".$path, $latest."/$new_name");

        #Update the modified time to time of video
        touch($latest."/$new_name", $timestamp);

        #Increment counter
        $x++;
    }

    #Return to root camera directory
    if (!chdir("..")) exit();
}

/**
 * Empties all symlinks from a directory
 * @param string $directory     Directory path
 */
function empty_dir($dir) {
    foreach(scandir($dir) as $file) {
        if ('.' === $file || '..' === $file || is_dir("$dir/$file")) continue;
        if (is_link("$dir/$file")) unlink("$dir/$file");
    }
}

/**
 * Get an array that represents directory tree
 * @param string $directory     Directory path
 * @param bool $recursive         Include sub directories
 * @param bool $listDirs         Include directories on listing
 * @param bool $listFiles         Include files on listing
 * @param regex $exclude         Exclude paths that matches this regex
 */
function directoryToArray($directory, $recursive = true, $listDirs = false, $listFiles = true, $exclude = '') {
    $arrayItems = array();
    $skipByExclude = false;
    $handle = opendir($directory);
    if ($handle) {
        while (false !== ($file = readdir($handle))) {
        preg_match("/(^(([\.]){1,2})$|(\.(svn|git|md))|(Thumbs\.db|\.DS_STORE))$/iu", $file, $skip);
        if($exclude){
            preg_match($exclude, $file, $skipByExclude);
        }
        if (!$skip && !$skipByExclude) {
            if (is_dir($directory. DIRECTORY_SEPARATOR . $file)) {
                if($recursive) {
                    $arrayItems = array_merge($arrayItems, directoryToArray($directory. DIRECTORY_SEPARATOR . $file, $recursive, $listDirs, $listFiles, $exclude));
                }
                if($listDirs){
                    $file = $directory . DIRECTORY_SEPARATOR . $file;
                    $arrayItems[] = $file;
                }
            } else {
                if($listFiles){
                    $file = $directory . DIRECTORY_SEPARATOR . $file;
                    $arrayItems[] = $file;
                }
            }
        }
    }
    closedir($handle);
    }
    return $arrayItems;
}
?>

This is what AirVideo client looks like on the iPad:
IMG_0337

This is what OPlayer HD looks like playing the RTSP stream:
IMG_0335

Lastly, a lot of people might ask why not just replace the Logitech software completely and use BlueIris since I have a dedicated box. The reason is simple. Currently, the cameras run over powerline which results in small artifacts from the live RTSP stream however the camera records video directly to itself first before any aspect of the network is involved. This allows guaranteed quality video every time. It also allows my server to go down and the cameras to continue functioning just fine. Also, letting the cameras process the video for motion, etc saves my server a ton of CPU cycles which is important since it is a fairly old machine.

Edit: If anyone is wondering the camera locations are temporary during testing and setup (want to make sure they work 100% before mounting them out of reach).


Leave a Reply

Your email address will not be published. Required fields are marked *