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
for further information).
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
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.
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:
- <?php
- $content = $_POST['file'];
- $sfile = fopen("test.jpg", 'wb');
- fwrite($sfile, $content);
- fclose($sfile);
- ?>
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.
- <title>Network Camera</title>
-
- <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 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.
- 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 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.
- 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, 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:
- 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 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