Get Automatic Pictures From Device and Upload to Web Server

Scope

In this article, we'll see how to develop a Windows Phone app capable of automatically taking photos using the device's camera and uploading them to a web server by which they can be seen from a browser. In other words, we'll develop a sort of network camera / webcam that could be run from a smartphone or tablet to monitor a certain place from elsewhere. We'll use Visual Basic .NET for the app part, whereas the server-side coding will be done in PHP, as we've done in a previous article about geolocalization (refer to Geolocalize a device and store coordinates on Webserver for further information).

Prerequisites

To use the code presented in the article, you'll need:
  • Windows 8.1
  • Visual Studio with Windows Phone 8.1 SDK
  • A local or hosted web server, with Apache with PHP support enabled. Alternatively, the web server part could be written in ASP/ASP.NET.
Configuring each one of the preceding prerequisites goes beyond the scope of the article, so I will not address that point, assuming that everything was previously successfully configured and running. If you need a brief reference on how to install an Apache plus PHP/MySQL Server, you can refer to my article Quickly installing LAMP server on Debian or Ubuntu Linux  

Introduction

In this article I'll present a simple method to implement a sort of smartphone/tablet based webcam, useful to set up scenarios that can be evolved into a surveillance application and the like. A growing attention to the Iot application field causes us to be increasingly concerned and aware of the possibilities of interconnectivity of our daily use devices and I think the present could be a nice (though really basic) exercise in that direction.

What we will do here is to use a Windows device as an automatic camera, to take pictures as a data stream at every predetermined second, to send them remotely to a PHP script that will consolidate them as a file on the remote webserver, making it available to be seen when standing anywhere that allows internet connections.

Let's start from the server part, to do some initial assumptions that will make clearer the app development.

Server-side scripts

As we've previously stated, we'll use PHP (and an Apache webserver) to setup a simple script capable of receiving a file that will be sent using the POST method, to store it and thus make it subsequently accessible to further HTTP requests, such as a browser navigation. The script is the most simple possible. It consists of the reception of a POST content (namely, "file" parameter), proceding then in opening an arbitrary file (that I've named "test.jpg"), writing in it what was sent in binary mode. Let's see it:
  1. <?php  
  2.    $content = $_POST['file'];  
  3.    $sfile   = fopen("test.jpg"'wb');   // 'wb' parameter means "write binary"  
  4.    fwrite($sfile$content);   
  5.    fclose($sfile);  
  6. ?> 
Pretty self-explanatory with a minimum of PHP knowledge. Next, we will prepare another page that I've named "index.html", a plain HTML file in which our "test.jpg" will be shown.
  1. <title>Network Camera</title>  
  2. <!-- Omissis: styles will be present in the full code -->  
  3. <h1>Network Camera</h1>  
  4. <div id="imgwrapper">  
  5.    <img src="test.jpg">  
  6. </div>  
To sum up this part of our project, we are speaking here about having a web server equipped by two simple scripts. The first one will be exploited by our Windows Phone app and it will receive a file remotely the data stream sent by the app itself, when the second one is simply a viewer for those data, encapsulating the sent data (in other words, a photo) in an IMG tag, to allow users to see it. Now we can implement our app to complete the work.

Automatic camera app for Windows Phone

In this section, we will develop our Windows Phone app that will consist of the following:
  • Automatic camera shooting
  • Automatic photo upload (in other words submitting the photo to our upload.php page)
We will make use the Windows.Media.MediaProperties namespace to access our device's camera capabilities.

First, open Visual Studio and select from the Visual Basic template menù the Store Apps / Blank App (Windows Phone) template. It will create for us a simple layout based on a single page, namely MainPage.xaml, on that we will apply the necessary controls and business logic.



Let's see what elements we will include in our XAML.



First we have a CaptureElement, in other words a control in which we will render the camera preview, to observe what will be acquired as the image. A TextBox named txtRemUP will contain the remote address of our upload.php (useful in case our app must communicate with various hosts, instead of hardcoding an URI). A ToggleButton will allow us to start and stop image acquisition and last we have a couple of TextBlock for log purposes (the date and time of the last shot and upload), and a Canvas that will show us the last picture taken.

App prerequisites

Since our application must require the use of a hardware device and network communications, we must set the app's Capabilities and Requirements accordingly. Thus, we must double-click on the Package.appxmanifest file (that was created by Visual Studio), accessing both the Capabilities and Requirements tabs, setting the camera and network flag as follows:




Camera initialization

On the the Code-side, the first thing to do is to initialize our camera when the app starts, setting the CaptureElement Source property accordingly. Please note that nearly all the events that will be presented will be asynchronous. Let's start with the OnNavigatedTo event that defines the moment in which a certain Page will be shown.
  1. Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)  
  2.     cam = New Windows.Media.Capture.MediaCapture  
  3.     Await cam.InitializeAsync()  
  4.     mCanvas.Source = cam  
  5.     Await cam.StartPreviewAsync()  
  6. End Sub  
I've declared a MediaCapture variable, cam, as visible to the entire scope. Here we'll use the method InitializeAsync on it, to initialize our camera, binding the MediaCapture variable as the source of our CaptureElement control. A further call to the StartPreviewAsync method will result in a graphical rendering of the camera acquisition on the control.

On the Toggled event of our ToggleButton, we will control a Timer launch or its disposition, depending on the ToggleButton status. 
  1. If toggler.IsOn  Then  
  2.     t = New System.Threading.Timer(AddressOf Shoot, Nothing, TimeSpan.Zero, New TimeSpan(0, 0, 10))  
  3.     t.Change(30000, Threading.Timeout.Infinite)  
  4. Else  
  5.     If Not (t Is NothingThen t.Dispose()  
  6. End If  
If the ToggleButton is active, we will create a new instance of a t Timer, using 30 seconds as its interval. For each interval, a call to the subroutine Shoot will be made. In that sub we will control the picture acquisition, plus its uploading.

Photo capturing

The Shoot subroutine is as follows: 
  1. Public Async Sub Shoot(sender  As Object)  
  2.         Dim imgFormat  As ImageEncodingProperties = ImageEncodingProperties.CreateJpeg()  
  3.   
  4.         stream = New Streams.InMemoryRandomAccessStream  
  5.   
  6.         Await cam.CapturePhotoToStreamAsync(imgFormat, stream)  
  7.         Await stream.FlushAsync  
  8.         stream.Seek(0)  
  9.   
  10.         Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf SetCanvas)  
  11.         Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf MarshalToggler)  
  12. End Sub  
In it, we define a RandomAccessStream that will be initialized by calling the method CapturePhotoToStreamAsync (from the MediaCapture variable). Simply as that, the calling to that method will access the camera to take a shot, proceeding to copy the image to a stream. At the end of our routine, you can see two Dispatchers towards two subroutines, the latter being simply a check on the ToggleButton status. The first one, though, calls upon the SetCanvas routine that will be used to save the image to our Canvas, but more importantly, to upload it on our webserver, using a POST towards our upload.php URI.
  1. Private Async Sub SetCanvas()  
  2.         tmpLab.Text = DateTime.Now.ToString  
  3.         Dim b As New BitmapImage()  
  4.         Await b.SetSourceAsync(stream)  
  5.         pCanvas.Source = b  
  6.   
  7.         Dim io As Stream = stream.AsStreamForRead  
  8.         Dim buf(io.Length)  As Byte  
  9.         io.Position = 0  
  10.         Await io.ReadAsync(buf, 0, io.Length)  
  11.   
  12.         Dim httpClient  As New HttpClient  
  13.         Dim form = New MultipartFormDataContent  
  14.         form.Add(New ByteArrayContent(buf, 0, buf.Length), "file")  
  15.   
  16.         Dim response = Await httpClient.PostAsync(txtRemUP.Text, form)  
  17.         response.EnsureSuccessStatusCode()  
  18.         httpClient.Dispose()  
  19.         Dim sd As String = response.Content.ReadAsStringAsync().Result  
  20.         lastSent.Text = Date.Now.ToString  
  21. End Sub  
Apart from the TextBlocks used for logging purposes, what this routine will do is as follows:
  • Creation of a BitmapImage object, using the method SetSourceAsync, a new Bitmap is made from our RandomAccessStream, using it as the Source of our Canvas, in order to show the last picture taken.
  • Creation of a simple Stream to access a Byte array from our original RandomAccessStream (our image). That will be needed to upload our data correctly
  • Creation of a MultipartFormDataContent (in other words a "simulated" HTTP form, with the web awaited variable that we've named "file" when we've created our upload.php page)
  • Physical upload of our image
The complete MainPage code-behind will be as follows: 
  1. Imports System.Net.Http  
  2. Imports Windows.Storage  
  3. Imports Windows.Media.MediaProperties  
  4.   
  5. Public NotInheritable Class MainPage  
  6.     Inherits Page  
  7.   
  8.     Dim t As System.Threading.Timer  
  9.     Dim cam As Windows.Media.Capture.MediaCapture  
  10.     Dim stream As Streams.InMemoryRandomAccessStream  
  11.   
  12.     Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)  
  13.         cam = New Windows.Media.Capture.MediaCapture  
  14.         Await cam.InitializeAsync()  
  15.         mCanvas.Source = cam  
  16.         Await cam.StartPreviewAsync()  
  17.     End Sub  
  18.   
  19.     Private Async Sub SetCanvas()  
  20.         tmpLab.Text = DateTime.Now.ToString  
  21.         Dim b As New BitmapImage()  
  22.         Await b.SetSourceAsync(stream)  
  23.         pCanvas.Source = b  
  24.   
  25.         Dim io As Stream = stream.AsStreamForRead  
  26.         Dim buf(io.Length)  As Byte  
  27.         io.Position = 0  
  28.   
  29.         Await io.ReadAsync(buf, 0, io.Length)  
  30.         Dim httpClient  As New HttpClient  
  31.         Dim form = New MultipartFormDataContent  
  32.         form.Add(New ByteArrayContent(buf, 0, buf.Length), "file")  
  33.         Dim response = Await httpClient.PostAsync(txtRemUP.Text, form)  
  34.         response.EnsureSuccessStatusCode()  
  35.         httpClient.Dispose()  
  36.         Dim sd As String = response.Content.ReadAsStringAsync().Result  
  37.         lastSent.Text = Date.Now.ToString  
  38.     End Sub  
  39.   
  40.     Private Sub MarshalToggler()  
  41.         If toggler.IsOn  Then  
  42.             t = New System.Threading.Timer(AddressOf Shoot, Nothing, TimeSpan.Zero, New TimeSpan(0, 0, 10))  
  43.             t.Change(30000, Threading.Timeout.Infinite)  
  44.         Else  
  45.             If Not (t Is NothingThen t.Dispose()  
  46.         End If  
  47.     End Sub  
  48.   
  49.     Public Async Sub Shoot(sender  As Object)  
  50.         Dim imgFormat  As ImageEncodingProperties = ImageEncodingProperties.CreateJpeg()  
  51.         stream = New Streams.InMemoryRandomAccessStream  
  52.         Await cam.CapturePhotoToStreamAsync(imgFormat, stream)  
  53.         Await stream.FlushAsync  
  54.         stream.Seek(0)  
  55.         Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf SetCanvas)  
  56.         Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf MarshalToggler)  
  57.     End Sub  
  58.   
  59.     Private Sub toggler_Toggled(sender  As Object, e As RoutedEventArgs)  
  60.         MarshalToggler()  
  61.     End Sub  
  62. End Class  
Upon the completion of those procedure, our "test.jpg" file will be uploaded and ready to be viewed by knowing the remote URI and being able to access it from any device.
Let's see an example of the execution of the project.

An overall test

A simple test will consist of setting up the webserver and the device, launch the Windows Phone app awaiting it to take and upload photos, checking for them later using a browser, visiting the HTML page we've created before:

Source Code

The complete source code used in the present article can be downloaded at: https://code.msdn.microsoft.com/Get-Automatic-Pictures-3b3875dd

Bibliography