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 through the device's camera, uploading them on a web server through 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, while 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 above prerequisites goes beyond the scope of the article, so i will not address this 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 could 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 base webcam, useful to set-up scenarios that can be evolved into surveillance application, and the like. A growing attention to the Iot application field bring us to be increasingly concerned and aware of the possibilities of inter-connectivity 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 data stream every predetermined seconds, 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 while standing at any place that allows Internet connections.
Let's start from the server part, in order 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, which will be sent through POST method, to store it and thus making it subsequently accessible to further HTTP requests, such as a browser navigation. The script is the most simple possible: it consist in the reception of a POST content (namely, "file" parameter), proceeding then in opening an arbitrary file (which i've named "test.jpg"), writing in it what was sent in binary mode. Let's see it:
<?php
$content = $_POST['file'];
$sfile = fopen("test.jpg", 'wb'); // 'wb' parameter means "write binary"
fwrite($sfile, $content);
fclose($sfile);
?>
Pretty self explanatory with minimum PHP knowledge.
Next, we will prepare another page, which I've named "index.html": a plain HTML file in which our "test.jpg" will be shown.
<title>Network Camera</title>
<!-- Omissis: styles will be present in the full code -->
<h1>Network Camera</h1>
<div id="imgwrapper">
<img src="test.jpg">
</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 and file remotely the data stream sent by the app itself, while the second one is simply a viewer for those data, encapsulating the sent data (i.e., 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, which will consist of the following:
- Automatic camera shooting
- Automatic photo upload (i.e. submitting the photo to our upload.php page)
We will make use of Windows.Media.MediaProperties namespace to access our device's camera capabilities.
First, open Visual Studio and select from 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 which we will apply the needed controls and business logic.
Let's see what elements we will include in our XAML
First we have a CaptureElement, i.e. a control in which we will render the camera preview, in order to observe what will be acquired as image. A TextBox named txtRemUP will contain the remote address of our upload.php (useful in case our app must communicate with different hosts, instead of hardcoding un URI). A ToggleButton will allow us to start and stop image acquisition, and last we have a couple of TextBlock for log purposes (date and time of the last shoot and upload), and a Canvas that will show us the last picture taken.
App prerequisites
Since our application must require the use of hardware device and network communications, we must set the app's Capabilities and Requirements accordingly.
Thus, we must double-click on Package.appxmanifest file (which was created by Visual Studio), accessing both the Capabilities and Requirements tabs, setting the camera and network flag as follows:
Camera initialization
Code-side, the first thing to do is to initialize our camera when the app starts, setting the CaptureElement Source property accordingly. Please note almost all the events that will be presented will be asynchronous. Let's start with the OnNavigatedTo event, which defines the moment in which a certain Page will be shown.
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
cam = New Windows.Media.Capture.MediaCapture
Await cam.InitializeAsync()
mCanvas.Source = cam
Await cam.StartPreviewAsync()
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.
If toggler.IsOn Then
t = New System.Threading.Timer(AddressOf Shoot, Nothing, TimeSpan.Zero, New TimeSpan(0, 0, 10))
t.Change(30000, Threading.Timeout.Infinite)
Else
If Not (t Is Nothing) Then t.Dispose()
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:
Public Async Sub Shoot(sender As Object)
Dim imgFormat As ImageEncodingProperties = ImageEncodingProperties.CreateJpeg()
stream = New Streams.InMemoryRandomAccessStream
Await cam.CapturePhotoToStreamAsync(imgFormat, stream)
Await stream.FlushAsync
stream.Seek(0)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf SetCanvas)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf MarshalToggler)
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 shoot, proceeding in 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, which will be used to save the image to our Canvas, but - more importantly - to upload it on our webserver, through a POST towards our upload.php URI.
Private Async Sub SetCanvas()
tmpLab.Text = DateTime.Now.ToString
Dim b As New BitmapImage()
Await b.SetSourceAsync(stream)
pCanvas.Source = b
Dim io As Stream = stream.AsStreamForRead
Dim buf(io.Length) As Byte
io.Position = 0
Await io.ReadAsync(buf, 0, io.Length)
Dim httpClient As New HttpClient
Dim form = New MultipartFormDataContent
form.Add(New ByteArrayContent(buf, 0, buf.Length), "file")
Dim response = Await httpClient.PostAsync(txtRemUP.Text, form)
response.EnsureSuccessStatusCode()
httpClient.Dispose()
Dim sd As String = response.Content.ReadAsStringAsync().Result
lastSent.Text = Date.Now.ToString
End Sub
Apart from the TextBlocks used for logging purposes, what this routine will do is as follows:
- Creation of a BitmapImage object: through 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 taken picture.
- Creation of a simple Stream to access to a Byte array from our original RandomAccessStream (our image). That will be needer to upload correctly our data
- Creation of a MultipartFormDataContent (i.e. a "simulated" HTTP form, with the web awaited variable, which 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:
Imports System.Net.Http
Imports Windows.Storage
Imports Windows.Media.MediaProperties
Public NotInheritable Class MainPage
Inherits Page
Dim t As System.Threading.Timer
Dim cam As Windows.Media.Capture.MediaCapture
Dim stream As Streams.InMemoryRandomAccessStream
Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
cam = New Windows.Media.Capture.MediaCapture
Await cam.InitializeAsync()
mCanvas.Source = cam
Await cam.StartPreviewAsync()
End Sub
Private Async Sub SetCanvas()
tmpLab.Text = DateTime.Now.ToString
Dim b As New BitmapImage()
Await b.SetSourceAsync(stream)
pCanvas.Source = b
Dim io As Stream = stream.AsStreamForRead
Dim buf(io.Length) As Byte
io.Position = 0
Await io.ReadAsync(buf, 0, io.Length)
Dim httpClient As New HttpClient
Dim form = New MultipartFormDataContent
form.Add(New ByteArrayContent(buf, 0, buf.Length), "file")
Dim response = Await httpClient.PostAsync(txtRemUP.Text, form)
response.EnsureSuccessStatusCode()
httpClient.Dispose()
Dim sd As String = response.Content.ReadAsStringAsync().Result
lastSent.Text = Date.Now.ToString
End Sub
Private Sub MarshalToggler()
If toggler.IsOn Then
t = New System.Threading.Timer(AddressOf Shoot, Nothing, TimeSpan.Zero, New TimeSpan(0, 0, 10))
t.Change(30000, Threading.Timeout.Infinite)
Else
If Not (t Is Nothing) Then t.Dispose()
End If
End Sub
Public Async Sub Shoot(sender As Object)
Dim imgFormat As ImageEncodingProperties = ImageEncodingProperties.CreateJpeg()
stream = New Streams.InMemoryRandomAccessStream
Await cam.CapturePhotoToStreamAsync(imgFormat, stream)
Await stream.FlushAsync
stream.Seek(0)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf SetCanvas)
Await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, AddressOf MarshalToggler)
End Sub
Private Sub toggler_Toggled(sender As Object, e As RoutedEventArgs)
MarshalToggler()
End Sub
End Class
Upon the completion of those steps, 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 in setting up the webserver and the device, launch the Windows Phone app awaiting it to take and upload photos, checking for them later through 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
- HttpClient class
- InMemoryRandomAccessStream class
- MultipartFormDataContent class
- Windows.Media.Capture namespace
Other Languages
The present article is available in the following localizations: