May 2013

Volume 28 Number 05

Windows Phone - Windows Phone Video Capture: A Best-of-Breed Approach

By Chris Barker | May 2013

The Windows Phone 7.1 SDK opened up a number of new development scenarios, including the ability to access the camera of Windows Phone 7.5 devices. When Windows Phone 8 was released, it brought new capabilities such as being able to capture high-definition 1080p videos on supported hardware. While the Windows Phone 7.1 SDK could’ve been extended to support the new device features, the Windows Phone 8 release also coincided with a more architectural change to the OS—it shares a kernel with Windows 8. A further significant change was made to move a number of APIs to the Windows 8 model and use a developer surface known as the Windows Runtime (WinRT).

The Windows Phone 7.1 SDK is the version that lets developers target features of Windows Phone 7.5. From here on, I’ll refer to Windows Phone 7.5 to encompass both the OS version and the SDK. (Incidentally, the code name for Windows Phone 7.5 was “Mango,” and the code name for Windows Phone 8 was “Apollo”—you might see these names documented elsewhere.)

The Windows Runtime not only introduced a number of consistent APIs across Windows Phone and Windows 8, but it also provided a much more efficient, native runtime that allowed new Windows Phone-specific APIs to be provided using the same model. I won’t go deeply into the new changes brought about by the Windows Runtime, but be aware it did impact APIs such as those enabling video capture.

I’m going to give you an overview of what has changed between versions, but the real takeaway is that you’re going to learn how to maintain your Windows Phone 7.5 project while at the same time giving a richer experience to your Windows Phone 8 users. A huge benefit here is that the techniques discussed won’t just apply to video capture, but to any of those APIs that have been reinvented for Windows Phone 8. I’m going to use an existing public code sample to help explain the techniques you can use to introduce code reuse across your Windows Phone 7.5 and 8 projects—and even Windows 8 projects, too.

Getting Started

One of the main goals I want to illustrate is how to port a solution to Windows Phone 8 while not neglecting Windows Phone 7.5 devices. Before I get into that, it’s worth taking a step back and noting that in many cases you won’t need to do anything at all. Your existing Windows Phone 7.5 app will continue to run on Windows Phone 8, although you should test it to make sure there’s no unexpected behavior. Visual Studio 2012 will only allow you to use the supported APIs when targeting Windows Phone 7.5. And remember, you only need to worry about writing code for Windows Phone 8 if you want to take advantage of any of the new APIs and functionality it offers.

I’m going to assume you want to continue supporting your Windows Phone 7.5 users, while giving your Windows Phone 8 users access to the rich new features of their devices.

To help explain how you can get a best-of-breed solution, I’m going to use the Windows Phone (7.5) Video Recorder Sample (bit.ly/16tM2c1) as a starting point. Remember, although I happen to be using the video recorder code sample here, the approaches I discuss could apply to any number of platform features that you want to span across versions of the platform.

There are a couple of points to call out about this solution:

  • It doesn’t use the Model-View-ViewModel (MVVM) pattern.
    • The majority of the UI is in the MainPage.xaml file.
    • The majority of the logic is driven out of the MainPage.xaml.cs codebehind file.
  • It uses the Windows Phone 7.5 API to manipulate the camera—namely the System.Windows.Media.VideoCaptureDevice class. Windows Phone 8 introduces a new API in this area that I’ll talk about more, later on.

The fact that this solution isn’t using the MVVM pattern isn’t a big deal—it would be nice to have (and generally recommended for production code) because it saves you some refactoring work later on, but in itself, it’s not a compatibility issue.

The VideoCaptureDevice class, however, is going to restrict you when moving to Windows Phone 8. It’s going to run just fine against the Windows Phone 8 runtime, but it’s not going to give you maximum performance and it’s not going to allow you to target the full range of resolutions supported by the hardware (among other things).

In subsequent sections I’m going to take the following approach:

  • Create a common abstraction for functionality that will be implemented differently between platforms. This will enable you to share the code that consumes that functionality.
  • Put the abstraction for a video recorder in a Portable Class Library (PCL). This will make it easier to move more code into the PCL later on if needed.
  • Share the codebehind for MainPage via file linking (a pragmatic decision because the existing app doesn’t use the MVVM pattern—otherwise I’d recommend moving the view models to a PCL).

So, given I want to be greedy and have a best-of-breed approach, I need to think about what I can do to abstract out the Windows Phone 7.5 camera logic from the Main­Page.xaml.cs file. I’ll look to resolve this problem in the next section.

Decoupling and Abstracting Your Implementation

First, you need a place into which to factor your code. A class library would make sense, but you can use a PCL. A PCL lets you specify the platforms you wish to target and provides a nice way of restricting you to the APIs provided by the intersection of those platforms—ultimately giving you the ability to make binary references across projects (rather than simply code/project linking and rebuilding to the target platform at compile time). In this case you can create a PCL project that targets Windows Phone 7.5 or greater and Windows Store apps (that is, Windows 8 “modern apps”) and call it VideoRecorder.Shared (see Figure 1).

Configuring Your Portable Class Library
Figure 1 Configuring Your Portable Class Library

Following the abstraction pattern (bit.ly/YQwsVD), you can create a VideoRecorderCore abstract class, which will let you target platform-specific code where required. The abstract class will look something like Figure 2.

Figure 2 Creating a VideoRecorderCore Abstract Class

namespace VideoRecorder.Shared
{
  public abstract class VideoRecorderCore
  {
    public abstract void InitializeVideoRecorder();
    public abstract void StartVideoRecording();
    public abstract void StopVideoRecording();
    public abstract void StartVideoPreview();
    public abstract void DisposeVideoPlayer();
    public abstract void DisposeVideoRecorder();
    public static VideoRecorderCore Instance { get; set; }
  }
}

Note: In the example shown in Figure 2 you could just as easily use an interface, but chances are you’re going to need some common base functionality in many scenarios.

In the sdkVideoRecorderCS project you’d ideally spend some time implementing the MVVM pattern, but that can be left for another day. The refactoring for now will focus on providing a concrete implementation of the abstract class. Fortunately, you already have the concrete implementation—it’s just a little too tightly coupled in the MainPage.xaml.cs right now. To address this, you can create a WP7VideoRecorder class in the sdkVideoRecorderCS project and inherit it from VideoRecorderCore in the PCL project. All you need to do next is move the implementation out of MainPage.xaml.cs and into the appropriate overridden method. As an example, the InitializeVideoRecorder method would initially look something like Figure 3.

Figure 3 The InitializeVideoRecorder Method

public override void InitializeVideoRecorder()
{
  if (captureSource == null)
  {
    captureSource = new CaptureSource();
    fileSink = new FileSink();
    videoCaptureDevice =
       CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
    captureSource.CaptureFailed +=
    new EventHandler<ExceptionRoutedEventArgs>(OnCaptureFailed);
    if (videoCaptureDevice != null)
    {
      videoRecorderBrush = new VideoBrush();
      videoRecorderBrush.SetSource(captureSource);
      viewfinderRectangle.Fill = videoRecorderBrush;
      captureSource.Start();
      UpdateUI(ButtonState.Initialized, 
        "Tap record to start recording...");
    }
    else
    {
      UpdateUI(ButtonState.CameraNotSupported,
         "A camera is not supported on this device.");
    }
  }
}

I won’t discuss every line of code in Figure 3 here—that’s done comprehensively in the documentation (bit.ly/YVIf0I)—however, in summary, the code is initializing the VideoCaptureDevice instance and then setting up the video preview in the UI. There are a couple of issues I’ve introduced here by simply doing a copy and paste from the codebehind into the concrete implementation. The code is making references to UI elements and methods (for example, viewfinderRectangle and the UpdateUI method). You don’t want these in your concrete implementation, and if you had already introduced a view model, these would be more easily factored out. So, there’s a little bit of cleanup work required here:

  1. Move the UI code back into the respective UI method (InitializeVideoRecorder in the MainPage.xaml.cs file, in this case).
  2. Create a new abstract method for initializing the VideoRecorderBrush, as this will be required across Windows Phone 7.5 and Windows Phone 8—but the implementations might differ.

Once you’ve done some of this housekeeping, your method will look something like this:

public override void InitializeVideoRecorder()
{
  if (_captureSource == null)
  {
    _captureSource = new CaptureSource();
    _fileSink = new FileSink();
    _videoCaptureDevice = 
      CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
    _captureSource.CaptureFailed +=
       new EventHandler<ExceptionRoutedEventArgs>(OnCaptureFailed);
   }
}

You’re now able to get the benefit of a Windows Phone codebehind (MainPage.xaml.cs) that works across versions Windows Phone 7.5 and Windows Phone 8, as shown in Figure 4.

Figure 4 Codebehind That Works in Windows Phone 7.5 and Windows Phone 8

public void InitializeVideoRecorder()
{
  _videoRecorder.InitializeVideoRecorder();
  _videoRecorder.CaptureFailed += OnCaptureFailed;
  if (_videoRecorder.VideoCaptureDevice != null)
  {
    videoRecorderBrush = new VideoBrush();
    videoRecorderBrush.SetSource(
      _videoRecorder.VideoSource as CaptureSource);
    viewfinderRectangle.Fill = videoRecorderBrush;
    _videoRecorder.StartVideoSource();
    UpdateUI(ButtonState.Initialized,
      "Tap record to start recording...");
  }
  else
  {
    UpdateUI(ButtonState.CameraNotSupported,
       "A camera is not supported on this device.");
  }
}

All you need to do is initialize the VideoRecorder instance and you’re ready to begin consuming a new platform-specific implementation:

_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();

Aside from a few minor changes, you can simply go ahead and refactor the remaining methods in a similar way.

Targeting the New Windows Phone 8 APIs

As summarized earlier, porting your Windows Phone 7.5 code to Windows Phone 8 is pretty trivial—all you need to do is open the project and select “Upgrade to Windows Phone 8.0” (see Figure 5)—but what if you want to maintain both versions? Following the steps that you’ve just taken, the code is now structured in such a way as to easily support code reuse. Not only that, but because you’ve now decoupled the video recorder, you can make use of the new, more powerful WinRT API for video-camera capture on Windows Phone 8.

Upgrading Your Project to Target the Windows Phone 8 Runtime
Figure 5 Upgrading Your Project to Target the Windows Phone 8 Runtime

The next step is to create the Windows Phone 8 project, reusing as much code as possible and introducing any new functionality that you’d like.

You have two options for creating your Windows Phone 8 version at this point. You can either make a copy of your existing project and upgrade it, or you can create a new Windows Phone 8 project from scratch and add in the duplicate code files and references. The decision really comes down to how big your project is and how much code you’re likely going to want to reuse. In this example you’ll make a copy of the project and upgrade—this means you get to cherry-pick the pieces you want to reuse, and it reduces the chance of missing some key files. Here’s what you’ll need to do:

  • Copy the sdkVideoRecorderCS project and rename the sdkVideoRecorderCS.csproj to sdkVideoRecorderCS.WP8.csproj. Next, add the new project to the existing solution to help maintain both versions.
  • Upgrade the new project to Windows Phone 8. You’ll find that the upgraded project runs just fine on Windows Phone 8 without any further modification—this is thanks to the backward compatibility of the API. The advantages of this are that no code changes are required, but the downside can be poorer performance—and you aren’t going to be able to use the latest and greatest features of the platform.
  • You can now pick and choose the elements you’d like to reuse. For example, MainPage.xaml and MainPage.xaml.cs are largely going to remain the same across versions (at least in this solution), so delete the existing version of these files in the Windows Phone 8 project and add a file link to the versions in the Windows Phone 7.5 project by right-clicking on your project, selecting “Add Existing Item” and pointing at the MainPage.xaml file with “Add As Link” selected (see Figure 6).

Adding a File Link
Figure 6 Adding a File Link

Note: Instead of going through the menus as just described, you can simply drag the MainPage.xaml file from one project into the target project while holding Ctrl+Shift. This will create a file link and also move the codebehind file automatically.

You’ll need to repeat the preceding steps for all of the relevant files, but for the purpose of demonstration, this is the only file link you’ll need to add and make reuse of for now.

As a brief aside, there’s often a utopian dream when it comes to code reuse, but in the real world there are going to be times when you want to use the same code across platforms and there might be just one or two subtle differences. In these circumstances it can be impractical to maintain two sets of code, and it’s overkill to abstract the platform specifics out. In this scenario it’s often better to use preprocessor directives to target specific lines of code at a specific platform. An example will be shown of this a little later, but it’s important not to get too carried away with this technique or your code can quickly become spaghetti.

A good example of when to use platform abstraction is when you have an isolated piece of functionality that’s platform-specific. In the current example, this will be the actual video recorder logic. Currently in your Windows Phone 8 project you have a WP7VideoRecorder.cs file that works perfectly fine, but it isn’t using the new Windows Phone 8 API, so that’s what you’re going to change. Delete the WP7VideoRecorder.cs file from your Windows Phone 8 project and create a new one called WP8VideoRecorder.cs that also inherits from VideoRecorderCore.

As before, implement each of the methods. The key difference this time around is that you’re going to use the Windows.Phone.Media.Capture namespace rather than the System.Windows.Media namespace. The former is the newer WinRT namespace, which introduces a class called AudioVideoCaptureDevice. This seres a similar purpose to the VideoCaptureDevice class used previously, but it’s much richer in functionality.

There are a couple of approaches that WinRT APIs take differently from their predecessors. One such change you’ll come across here is that many of the APIs are asynchronous (more on that later). Another difference is that you deal with storage using the Windows.Storage namespace rather than using the familiar System.IO.IsolatedFileStream (although the latter will still be used to support media playback).

I’ll walk through a few of the bigger changes now to show some differences in the video recorder scenario and how you more generally approach resolving these types of differences while maintaining an element of code reuse.

In the Windows Phone 7.5 version of the video recorder code, there were several private variables defined:

private CaptureSource _captureSource;
private VideoCaptureDevice _videoCaptureDevice;
private IsolatedStorageFileStream _isoVideoFile;
private FileSink _fileSink;
private string _isoVideoFileName = "CameraMovie.mp4";

Ideally you want to keep the two codebases as similar as possible, but there’s no need for a CaptureSource or FileSink in the new API—the CaptureSource is replaced by the actual VideoCaptureDevice instance, which can act as a source, and you get the FileSink functionality for free so you just need a StorageFile for it to save to:

private AudioVideoCaptureDevice _videoCaptureDevice;
private IsolatedStorageFileStream _isoVideoFile;
private StorageFile sfVideoFile;
private string _isoVideoFileName = "CameraMovie.mp4";

The logical steps that follow are to work through the concrete implementation of the methods. I’m going to start with Initialize­VideoRecorder again. You’ve seen how this method previously looked, and you need to simply initialize the AudioVideoCaptureDevice instance in a similar way here:

public async override void InitializeVideoRecorder()
{
  CameraSensorLocation location = CameraSensorLocation.Back;
  var captureResolutions =
     AudioVideoCaptureDevice.GetAvailableCaptureResolutions(location);
  _videoCaptureDevice =
     await AudioVideoCaptureDevice.OpenAsync(location, 
       captureResolutions[0]);
  _videoCaptureDevice.RecordingFailed += OnCaptureFailed;
}

As you can see, the syntax is different, but the code is serving the same purpose. This part of the code also allows you to configure additional camera properties such as specifying the capture resolution and which camera (if there are multiple ones available) you’d like to use. The control you get over the resolutions here is already an advantage over what you get with the Windows Phone 7.5 APIs, as this will allow you to use full-scale, high-definition 1080p if your phone camera supports it. As with many of the WinRT APIs, the other welcome difference is that the methods are asynchronous. To aid manageability you can introduce the C# 5 keywords async and await. I won’t go into detail on these features here as they’ve been covered elsewhere, but they help you gain the benefits of asynchronous code while reducing the complexity of doing so.

The next method to take a look at is the InitializeFileSink method. Although you no longer have a FileSink per se, you do need to initialize the files in which you’re storing the video capture. This also happens to be where you begin using the Windows.Storage namespace with classes such as StorageFolder and StorageFile:

public async override void InitializeFileSink()
{
  StorageFolder isoStore = ApplicationData.Current.LocalFolder;
  sfVideoFile = await isoStore.CreateFileAsync(
    _isoVideoFileName, 
    CreationCollisionOption.ReplaceExisting);
}

There’s no need to go through every method as these are really the biggest differences to highlight. Where methods remain the same across both versions, you can move them into the VideoRecorderCore base class to ensure a common behavior across the subclasses.

Once the remaining changes have been made, you should notice a build error relating to the following line:

_videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();

The cause here is pretty obvious—it’s due to the Windows Phone 8 code referencing a class that no longer exists: WP7VideoRecorder. Of course, you need to reference the new WP8VideoRecorder class, but making that change in the MainPage.xaml.cs file would break the Windows Phone 7.5 code because you’re linking to the same file.

You have a few options here. You could use preprocessor directives like so:

#if WP7
  _videoRecorder = VideoRecorderCore.Instance = new WP7VideoRecorder();
#else
  _videoRecorder = VideoRecorderCore.Instance = new WP8VideoRecorder();
#endif

If you were to use preprocessor directives, a tidier approach would be to move these inside the wrapper class—for example, introduce a CreateVideoRecorder method into the VideoRecorderCore abstract base class and place the preceding #if declaration inside of this.

The route I decided to take here was to avoid using the preprocessor directives and abstract the concrete class lookup into a separate factory class—this means the code consuming the recorder is consistent across both Windows Phone 7.5 and Windows Phone 8:

_videoRecorder =
  VideoRecorderCore.Instance = VideoRecorderFactory.CreateVideoRecorder();

An alternative approach would be to use a Dependency Injection (DI) or Service Locator pattern and leave the instance resolution as a responsibility of the container or service, respectively.

A more discreet issue involves setting the VideoBrush for the camera preview. In this case you can again consider the preprocessor #if directive technique to differentiate the minor implementation details:

#if WP7
  videoRecorderBrush.SetSource(_videoRecorder.VideoSource as CaptureSource);
#else
  videoRecorderBrush.SetSource(_videoRecorder.VideoSource);
#endif

This is required because Windows Phone 8 overloads the SetSource method to allow an object to be specified as the Source object. This is why you’re able to return the AudioVideoCaptureDevice instance in the VideoSource property for Windows Phone 8. To support this approach, you’ll need to add a new conditional “WP7” compilation symbol to the Windows Phone 7.5 project under Project Properties | Build (see Figure 7).

Adding a Custom Conditional Compilation Symbol to Your Windows Phone 7.5 Project
Figure 7 Adding a Custom Conditional Compilation Symbol to Your Windows Phone 7.5 Project

As mentioned previously, the Windows Runtime relies heavily on asynchronous methods in order to improve the responsiveness of an app, but if you’re sharing code across versions of the platform, what impact does this have? Although you can write C# 5 code in a project targeting Windows Phone 7.5, there are certain implementation details that aren’t available when you want to use the async and await keywords. Consider the following method:

public async override void InitializeVideoRecorder()
{
  CameraSensorLocation location = CameraSensorLocation.Back;
  var captureResolutions =
     AudioVideoCaptureDevice.GetAvailableCaptureResolutions(location);
  _videoCaptureDevice =
     await AudioVideoCaptureDevice.OpenAsync(location, 
       captureResolutions[0]);
  _videoCaptureDevice.RecordingFailed += OnCaptureFailed;
}

This is effectively setting the VideoCaptureDevice property, but the consuming code looks like this:

_videoRecorder.InitializeVideoRecorder();
if (_videoRecorder.VideoCaptureDevice != null)
{
  ...
}
else
{
  UpdateUI(ButtonState.CameraNotSupported,
     "A camera is not supported on this device.");
}

What might not be immediately obvious is that while you’re awaiting the call to the AudioVideoCaptureDevice.OpenAsync method, the check on _videoRecorder.VideoCaptureDevice might already have happened. In other words, the VideoCaptureDevice check might evaluate to null because it hasn’t had a chance to initialize yet.

Again, you have a few choices at this stage. By making the consuming code asynchronous, it does mean retrofitting your code a little bit—and depending on your solution this could be a lot of work. If you want to avoid this, you could merely wrap up the asynchronous code inside a synchronous wrapper method. Another alternative might be to have an event-based mechanism for notifying the consumer when the initialization is complete.

Perhaps you want to use this opportunity to move to the new async model and therefore await the InitializeVideoRecorder call, but how can you do this if the Windows Phone 7.5 SDK doesn’t support the required constructs? One solution is to download the “Async for .NET Framework 4, Silverlight 4 and 5, and Windows Phone 7.5” NuGet package within Visual Studio, as shown in Figure 8.

Find the Async NuGet Package for Windows Phone 7.5 by Searching for “microsoft.bcl.async”
Figure 8 Find the Async NuGet Package for Windows Phone 7.5 by Searching for “microsoft.bcl.async”

At the time of writing this package was still in prerelease, but a stable release is expected soon. If you want to go a more tried-and-tested route, you'lll need to rework the code as described earlier and introduce an event pattern or synchronous wrapper.

Note: Make sure you have at least version 2.1 of the NuGet Package Manager installed or you’ll get unexpected error messages trying to use this package. You can download the latest version from bit.ly/dUeqlu.

Once you’ve downloaded this package, you can align the code in each project. For example, you can bake async support into your abstract base class by returning a Task type rather than a void—this will allow you to await the result rather than use the current “fire-and-forget” mechanism. It’s also a good convention to append “Async” to the names of the asynchronous method names, which makes consuming the methods more intuitive to a developer. For example, some of the methods will now have a signature like this:

public abstract Task InitializeVideoRecorderAsync();
public abstract Task InitializeFileSinkAsync();

You will, of course, then need to refactor the code in the subclasses. For example:

public override async Task InitializeVideoRecorderAsync()
{

And finally, you’ll be able to update your MainPage.xaml.cs code to support the ability to await on asynchronous operations. An example of this is:

public async void InitializeVideoRecorder()
{
  await _videoRecorder.InitializeVideoRecorderAsync();

Having made these final changes, you should now find that you can run both the Windows Phone 7.5 and Windows Phone 8 projects successfully. You have a strong element of code reuse and you’re using the latest features where they’re supported. You have a best-of-breed solution!

What About Windows 8?

Many of the partners who I work with have developed a Windows Phone app using XAML and C#. When considering developing a Windows Store app for Windows 8, one of their first technical questions is whether they should go the HTML5/JavaScript route or the XAML/C# route. A well-architected Windows Phone solution can offer a great amount of code reuse with Windows 8, so this can be the decisive factor when choosing which language you’ll use to develop your Windows Store app.

Although you’ll be able to reuse code between Windows Phone 7.5 and Windows 8, there are still a number of scenarios where you need to branch between the two. The video recorder is one such scenario, but the good news here is that the approaches you’ve taken earlier save you a lot of time and effort.

Implementing a Windows 8 version of this code is left as an exercise to the reader, or if you so choose you can download the accompanying sample that includes all of the code discussed along with a Windows 8 solution.

Note: For a Windows 8 implementation you’ll need to utilize the Windows.Media.Capture.MediaCapture class.

Going Further with MVVM

What you’ve just implemented gives you 100 percent reuse across MainPage.xaml for the two versions of Windows Phone. Given the abstraction and refactoring steps you went through, you were also able to achieve near-100 percent reuse across the accompanying codebehind file as well. You could take these techniques even further by introducing an MVVM pattern, and this would help to provide a great deal of reuse not just over Windows Phone versions, but across your Windows 8 codebase, too.

These techniques can be used to gain reuse while abstracting out platform-specific features such as the video capture I described, but also application lifecycle management (ALM), file storage and lots more.


Chris Barker is a technical evangelist for Microsoft based in Derbyshire, United Kingdom. He has spent many years developing code for both independent software vendors and enterprise organizations, running performance and scalability labs, and taking a keen interest in low-level debugging techniques. In recent years he has focused more on client development using XAML and C# and now works with global consumer partners developing for the Windows 8 and Windows Phone platforms.

Thanks to the following technical expert for reviewing this article: Daniel Plaisted (Microsoft)
Since joining Microsoft in 2008, Daniel Plaisted has worked on the Managed Extensibility Framework (MEF), Portable Class Libraries (PCL) and the Microsoft .NET Framework for Windows Store apps. He has presented at MS TechEd, BUILD and various local groups, code camps and conferences. In his free time, he enjoys computer games, reading, hiking, juggling and footbagging (hackey-sack).  His blog can be found at blogs.msdn.com/b/dsplaisted/.