Coding Security Camera Monitoring Software – Part 2

Introduction

We’re picking up where we left off last week in creating our own CCTV monitoring software in C#.  If you missed part 1 then you should probably read that first to get up to speed.

In the first part we got as far as creating the video feeds but still need to create the control interface for the application allowing you to change whether your feeds are full-screen or not.  We want to end up with a title above each feed, the ability to click a video to toggle its size and a status message that can be displayed in lieu of a video feed if it is loading or if there is an error.

With the Vlc.Net components that we have selected we encounter a quirk of the software and WPF.  We cannot draw anything on top of the video feed nor can we intercept mouse presses.  This is down to how LibVLC renders the video, how the library was created and numerous other issues.  There are a few ways I considered solving this – one was to actually run and capture four VLC windows within this application and control them remotely (too much effort for a quick win), another was to recode Vlc.Net and the last option I considered was doing everything in WinForms.  Since none of these options suited my quick-and-easy approach which was why I ultimately selected Vlc.Net in the first place I decided to go for the dirty-hack route.

We are going to create a separate control window which will have a transparent background.  The size of this window will be matched to that of the video window and they will always be set to move together.  As far as the user will be concerned this will look and act like a single window but as far as it is concerned technically it is two windows on top of each other.

Let the dirty hacks begin!

Part 2: The Control Window

There’s no escaping that this application is going to be less-than-elegant in its implementation so let’s jump straight in and get our hands murky.

Load the Visual Studio solution created in Part 1

Create a new WPF Window called ControlWindow.xaml

Edit the new ControlWindow.xaml

  • Set the following properties on the Window
    • Title = CCTV Monitor
    • ResizeMode = CanResize
    • WindowStyle = None
    • WindowState = Normal
  • Create these event handlers and leave their implementations empty for now
    • Loaded = OnLoaded
    • Closing = OnWindowClosing
  • Add two row definitions of height * and two column definitions of width * to the default grid that came with your window
  • Add the following 8 TextBlocks to your Window – these are two TextBlocks for each video feed – one for the title and one for a status message

<TextBlock Grid.Row=”0″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel1″ FontSize=”20″ />
<TextBlock Grid.Row=”0″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 1″ Name=”nameLabel1″ FontSize=”24″ />
<TextBlock Grid.Row=”0″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel2″ FontSize=”20″ />
<TextBlock Grid.Row=”0″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 2″ Name=”nameLabel2″ FontSize=”24″ />
<TextBlock Grid.Row=”1″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel3″ FontSize=”20″ />
<TextBlock Grid.Row=”1″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 3″ Name=”nameLabel3″ FontSize=”24″ />
<TextBlock Grid.Row=”1″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel4″ FontSize=”20″ />
<TextBlock Grid.Row=”1″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 4″ Name=”nameLabel4″ FontSize=”24″ />

If you run the application now you will notice nothing will happen – so we need to get it to actually show this new window

  • Open the VideoStreamWindow.xaml.cs
  • Add a private member variable of type ControlWindow called _control window to the class

private readonly ControlWindow _controlWindow;

  • Now modify the VideoStreamWindow() constructor and get it to create a new ControlWindow, to store it locally, show it and then give it focus by calling the Activate method

ControlWindow controlWindow = new ControlWindow(this);
_controlWindow = controlWindow;
controlWindow.Show();
controlWindow.Activate();

  • If we re-run the application we see our control window but now it covers the other window and has a black background whereas we wanted it to be transparent
  • Go back in to ControlWindow.xaml and add the following properties
    1. Background = Transparent
    2. AllowsTransparency = True
  • Notice that if we don’t add the AllowsTransparency option, even though we’ve set a transparent background, it won’t work – feel free to test it out
  • For good measure also set the following property so that we only see one window in the task bar
    1. ShowInTaskBar = False
  • Re-run the application now and you’ll see a single window in the taskbar (just the video feed) and the overlay window will have a transparent background

We now need to start wiring up the ControlWindow’s code

  • Open the ControlWindow.xaml.cs file
  • Create three private member variables to store handles to the video stream window, the error labels we’ve placed on the XAML window and the name labels that are on the XAML window.  We could access these TextBlocks in a different way but having them in an array makes it much easier for us to access them numerically

<TextBlock Grid.Row=”0″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel1″ FontSize=”20″ />
<TextBlock Grid.Row=”0″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 1″ Name=”nameLabel1″ FontSize=”24″ />
<TextBlock Grid.Row=”0″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel2″ FontSize=”20″ />
<TextBlock Grid.Row=”0″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 2″ Name=”nameLabel2″ FontSize=”24″ />
<TextBlock Grid.Row=”1″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel3″ FontSize=”20″ />
<TextBlock Grid.Row=”1″ Grid.Column=”0″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 3″ Name=”nameLabel3″ FontSize=”24″ />
<TextBlock Grid.Row=”1″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Center” VerticalAlignment=”Center” Foreground=”White” Name=”errorLabel4″ FontSize=”20″ />
<TextBlock Grid.Row=”1″ Grid.Column=”1″ Margin=”5″ HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch” Foreground=”White” Text=”Camera 4″ Name=”nameLabel4″ FontSize=”24″ />

  • Modify the constructor so that it accepts a handle to the video stream window.  We need this so that we can link up the events between both windows so that we can treat them as a single entity as far as the user is concerned.  Simply add a VideoStreamWindow parameter to the construct and store it in the member variable we just created.

    We also want the constructor to setup our error and name label arrays we’ve just created and whilst we’re at it attach to the Closing and Activated events on the VideoStreamWindow for good measure (just leave their implementations blank for now).

/// <summary>
/// Defines a handle to all the error labels shown on screen.
/// </summary>
private TextBlock[] _errorLabels;

/// <summary>
/// Defines a handle to all the name labels shown on screen.
/// </summary>
private TextBlock[] _nameLabels;

/// <summary>
/// Defines a handle to the main window containing the video feeds.
/// </summary>
private VideoStreamWindow _videoStreamWindow;

  • The OnWindowClosing event is now hooked up to the control window (from XAML) and the video stream window (from the code we just added in the constructor).  In here we want to ensure that if either of the windows is closed the entire application closes rather than just one window or part of the application.  We can do that by adding the following line of code which shuts down the entire WPF application.

public ControlWindow(VideoStreamWindow videoStreamWindow)
{
// Hookup to XAML and create components.
InitializeComponent();

// Store our UI elements for later access
_errorLabels = new TextBlock[] { errorLabel1, errorLabel2, errorLabel3, errorLabel4 };
_nameLabels = new TextBlock[] { nameLabel1, nameLabel2, nameLabel3, nameLabel4 };

// Store the video stream window for later use
_videoStreamWindow = videoStreamWindow;
_videoStreamWindow.Closing += OnWindowClosing;
_videoStreamWindow.Activated += VideoStreamWindowOnActivated;
}

  • We then want to write some code for the VideoStreamWindowOnActivated event stub we just created.  If the video stream window gets focus it means out control labels will vanish and we’ll lose all control of the application.  This could happen if a user alt-tabs or even selects the application on the taskbar.  We always want to ensure the control window is placed directly above the video feed so whenever it is selected we want to then give focus to the control window.  We can do this with just one line of code in the VideoStreamWindowOnActivated event handler:

Application.Current.Shutdown();

  • Now we want to make the camera areas clickable so that we can maximise or restore them when clicked.  To do this we have to do two things.  First let’s add an event handler to each of the four “Camera #” TextBlocks on the ControlWindow.xaml and leave its implementation blank for now

Activate();

  • Then we have to deal with another quirk of WPF.  In order to help us WPF will not handle clicks on a transparent area.  Rather cleverly it says “Oh that click must be for whatever is behind the transparent area and that’s why the user wants to click on it”.  Sadly in our case that means our clicks will do nothing and never be handled so we need yet more dirty hackery.  To do this we’re going to set a very-nearly-but-technically-not transparent background – with an alpha of just 1% (where 0% is transparent and 100% is solid).  On each of those four TextBlocks we just added an event handler to add this setting for the background colour:

MouseLeftButtonDown=”CameraAreaOnMouseLeftButtonClick”

  • Now go back to the ControlWindow.xaml.cs and inside the CameraAreaOnMouseLeftButtonClick event handler simply add a single line that will display a test message when we click it:

MessageBox.Show(“Camera clicked!”);

  • Open up VideoStreamWindow.xaml.cs and modify the construct so that instead of calling new ControlWindow() it passes itself into the new control window:

ControlWindow controlWindow = new ControlWindow(this);

Run your application again and this time the control window will have a transparent background, will let you click in any of the areas and whatever you do to focus the camera feed window will always bring the labels on top. You can test closing the app as well by pressing ALT+F4 on either window – debugging should stop for the whole application.

The last thing we’re going to do today is get this window to display full screen on a specific monitor.

  • Create a new class called WIndowHelper.cs – this will add extra functionality to all windows using an extension method.
  • Change the class definition to static so that it is used as an extension class
  • We want to create a new method that will read the display monitor number from the config file, look to see where that monitor’s bounds are (left, top, width and height), move the specified window to that location and then set the window to be maximised and not resizeable.

internal static void MoveToAssignedMonitor(this Window window)
{
int monitorNumber = int.Parse(ConfigurationManager.AppSettings[“DisplayMonitor”]);
Screen screen = Screen.AllScreens[monitorNumber];
window.Left = screen.Bounds.Left;
window.Top = screen.Bounds.Top;
window.Height = screen.Bounds.Height;
window.Width = screen.Bounds.Width;
window.WindowState = WindowState.Maximized;
window.ResizeMode = ResizeMode.NoResize;
}

  • Next we add a single line of code to bot the ControlWindow and VideoStreamWindow’s OnLoaded events to call this extension method.  This has the net effect of moving both windows to the specified monitor and maximising them.

this.MoveToAssignedMonitor();

If you run the application now your CCTV application is looking a lot more real – with four videos displayed (assuming all your feeds are valid) and an overlay label.  They should be full screen on a specified monitor and both windows should act as a single window even if many dirty hacks have been used.

Coming Up Next Time…

The last part of the video will see us manage the size toggling on each of the video feeds, dealing with status messages and adding basic retry capabilities for when a CCTV stream fails.

I hope you enjoyed part 2 of this tutorial and I look forward to seeing you next week!

 

Advertisements

One thought on “Coding Security Camera Monitoring Software – Part 2

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s