Monday 19 January 2015

Advanced Media Capture

In my previous two posts I went over a very simple way to offload media capture to the CameraCaptureUI, which is basically telling our system to go capture our image or video then return a storage file that we can work with. the only tricky part was to remember to add the webcam capability for capturing images, and to both the webcam and microphone capabilities for capturing videos.

this time around let's handle everything inside of our application, let's start by adding our capabilities

this time we've also added access to our picture and videos library, we're also going to save our files, how exciting.

let's start with our UI

<Page
    x:Class="pc.Media.MediaCaptureExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:pc.Media.MediaCaptureExample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Page.Resources>
        <Style TargetType="Button">
            <Setter Property="HorizontalAlignment" Value="Stretch" />
        </Style>
    </Page.Resources>
   
    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <StackPanel VerticalAlignment="Center">
            <Button x:Name="CaptureImage_BUTTON" Content="Capture Image"/>
            <Button x:Name="CaptureVideo_BUTTON" Content="Capture Video"/>
            <Button x:Name="CompleteVideo_BUTTON" Content="Complete Video"/>
        </StackPanel>

        <Border Grid.Column="1" Margin="50 100"  BorderBrush="Black" BorderThickness="5" >
            <CaptureElement x:Name="PreviewWindow" />
        </Border>
    </Grid>
</Page>


Rendered it should look something like this

Next lets activate our webcam and stream our media into our MediaPreview element

using System;
using Windows.Media.Capture;
using Windows.UI.Xaml.Controls;

namespace pc.Media.MediaCaptureExample
{
    public sealed partial class MainPage : Page
    {
        MediaCapture _mediaCapture = new MediaCapture();

        public MainPage()
        {
            this.InitializeComponent();
            this.Init();
        }

        private async void Init() {
            await this._mediaCapture.InitializeAsync();
            this.PreviewWindow.Source = this._mediaCapture;
            await this._mediaCapture.StartPreviewAsync();
        }
    }
}


Now if we run our application our CaptureElement gets a live stream from our web cam


Nothing special other then my web cam being broken (probably has something to do with my laptop being 6 years old).

Now the init method being called from the constructor, and then having no exception handling is not the best, but it demonstrates that getting media from the webcam is really straight forward.

but for fun let's add some exception handling inside the init method

using System;
using Windows.Media.Capture;
using Windows.UI.Popups;
using Windows.UI.Xaml.Controls;

namespace pc.Media.MediaCaptureExample
{
    public sealed partial class MainPage : Page
    {
        MediaCapture _mediaCapture = new MediaCapture();

        public MainPage()
        {
            this.InitializeComponent();
            this.Init();
        }

        private async void Init()
        {
            try
            {
                await this._mediaCapture.InitializeAsync();
                   
                this._mediaCapture.RecordLimitationExceeded += s => {
                    this.ShowError("recorder image exceeded");
                };

                this._mediaCapture.Failed += (s, errorArgs) => {
                    this.ShowError(errorArgs.Message);
                };

                this.PreviewWindow.Source = this._mediaCapture;
                await this._mediaCapture.StartPreviewAsync();
            }
            catch (UnauthorizedAccessException uaex)
            {
                //fired if capabilities not specified
                this.ShowError(uaex.Message);
            }
            catch (Exception ex)
            {
                //any sort of error
                this.ShowError(ex.Message);
            }
        }

        private async void ShowError(string message)
        {
            await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
            {
                await new MessageDialog(message).ShowAsync();
            });
        }
    }
}


now that's a bit more robust, next let's add some code for our image capture
public MainPage()
{
    this.InitializeComponent();
    this.Init();

    this.CaptureImage_BUTTON.Click += CaptureImage_BUTTON_Click;
}

async void CaptureImage_BUTTON_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    //create a storage file for the image and give it a name
    StorageFile imageFile = await KnownFolders.PicturesLibrary.CreateFileAsync("image.png",
        CreationCollisionOption.GenerateUniqueName); 
  
    // create image properties
    var imageProperties = ImageEncodingProperties.CreatePng();
           
    //save the contents of the media capture elment to our storage file,
    //with our defined image properties
    await _mediaCapture.CapturePhotoToStorageFileAsync(imageProperties, imageFile);

}

easy right? now just navigate to your Images library and there's your picture.

Finally lets make a movie,

public MainPage()
{
    this.InitializeComponent();
    this.Init();

    this.CaptureImage_BUTTON.Click += CaptureImage_BUTTON_Click;

    //Event receivers for Video capture
    this.CaptureVideo_BUTTON.Click += CaptureVideo_BUTTON_Click;
    this.CompleteVideo_BUTTON.Click += CompleteVideo_BUTTON_Click;
}

async void CaptureVideo_BUTTON_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    //create file
    StorageFile videoFile = await KnownFolders.VideosLibrary.CreateFileAsync("video.mp4",
        CreationCollisionOption.GenerateUniqueName);

    var recodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);

    await _mediaCapture.StartRecordToStorageFileAsync(recodingProfile, videoFile);
}

async void CompleteVideo_BUTTON_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    //just encase we try to stop our recording before we start it
    try
    {
        await _mediaCapture.StopRecordAsync();
    }
    catch (Exception ex)
    {
        this.ShowError(ex.Message);
    }

}

just as before, with the exception that we have to stop our recording, but that's really straight forward, no.