2 out of 4 rated this helpful Rate this topic

How to: Record Video in a Camera Application for Windows Phone

Windows Phone

January 13, 2012

With Windows Phone OS 7.1, you can programmatically access the device camera. In addition to capturing pictures, you can use the device camera to capture video. This topic describes how to create a video recorder application and demonstrates how to capture, display, and store video in your Windows Phone application. For information about how to capture pictures with the device camera, see How to: Create a Base Camera Application for Windows Phone.

NoteNote:

This topic corresponds to the Video Recorder Sample. To download the complete project, see Code Samples for Windows Phone.

This topic covers the following steps:

  1. Creating the Application and UI

  2. Updating the UI

  3. Initializing the Camera

  4. Switching Camera State between Preview and Recording

  5. Handling Button Taps

  6. Disposing the Recording and Playback Objects

  7. Completing the Application

The following image shows an example of the completed application.

AP_Con_CameraVideoHowTo

During recording, this application uses the FileSink class to pass video from a CaptureSource object and save it to isolated storage via an IsolatedStorageFileStream object. To provide a camera viewfinder for the application, the same CaptureSource is also used as a source for a VideoBrush control. The CaptureSource cannot be running when it is connected to or removed from the FileSink. Video recording occurs when the CaptureSource runs while it is attached to the FileSink.

For playback of the recorded video, an IsolatedStorageFileStream object reads the recorded video file and presents it as a source to a MediaElement control. For more information about how this application is implemented, see the following sections.

In this section, you create the application and prepare it for development.

Note Note:

When upgrading Windows Phone OS 7.0 applications to use the capabilities in Windows Phone OS 7.1, the camera capability ID_CAP_ISV_CAMERA is not automatically added to the application manifest file, WMAppManifest.xml. Without ID_CAP_ISV_CAMERA, applications using the camera API will not function. In new Windows Phone OS 7.1 projects, this capability is included in the application manifest file.

The steps in the following procedure are for Visual Studio 2010 Express for Windows Phone. You may see some minor variations in menu commands or window layouts when you are using the add-in for Visual Studio 2010 Professional or Visual Studio 2010 Ultimate.

To create the application

  1. In Visual Studio 2010 Express for Windows Phone, create a new project by selecting the File | New Project menu command.

  2. The New Project window is displayed. Expand the Visual C# templates, and then select the Silverlight for Windows Phone templates.

  3. Select the Windows Phone Application template. Fill in the Name box with a name of your choice.

  4. Click OK. The New Windows Phone Application window is displayed.

  5. In the Target Windows Phone Version menu, ensure that Windows Phone 7.1 is selected.

  6. Click OK. A new project is created, and MainPage.xaml is opened in the Visual Studio designer window.

To specify application capabilities

  1. To create a camera application, the camera capability must be declared in the application manifest file. Because a video application also records sound, the microphone capability must also be declared. Without declaring these capabilities, this application will not function. Open WMAppManifest.xml and confirm that the following capability elements are present.

    <Capability Name="ID_CAP_ISV_CAMERA"/>
    <Capability Name="ID_CAP_MICROPHONE"/>
    
    

    Important noteImportant Note:

    In Windows Phone applications that use the CaptureSource class, you must also use the Microsoft.Devices.Camera, Microsoft.Devices.PhotoCamera, or Microsoft.Xna.Framework.Audio.Microphone class to enable audio capture and accurate capability detection in the application.

    Although this particular application does not need any of these classes, code that incorporates one of the classes is still required to prompt the Marketplace capability detection process to add the ID_CAP_MICROPHONE capability to the application capabilities list upon ingestion. For an example, see the CapabilityPlaceholder file in the Video Recorder Sample at Code Samples for Windows Phone.

  2. (Optional) If you want your application to require a front-facing camera, additionally add the front-facing camera capability to the Capabilities element in the application manifest file.

    <Capability Name="ID_HW_FRONTCAMERA"/>
    

    This capability will not be automatically added to new projects; it must be added manually. A user without a front-facing camera will be notified that their device does not meet the requirements of your application, but they can still choose to download it. This application does not use the front-facing camera specifically; this capability is not required for this application. For more information, see the Capabilities section in Camera and Photos Overview for Windows Phone.

To add icons used by the application

  1. In Solution Explorer, right-click your project and select Add, then New Folder.

  2. Name the new folder Icons.

  3. In Solution Explorer, right-click the Icons folder and select Add, then Existing Item. This opens the Add Existing Item menu, from which you can select the icons used by the application.

  4. In the Add Existing Item window, navigate to one of the following paths to select the icons. This step assumes a default installation of Visual Studio. If you installed it in a different location, find the icon in the corresponding location.

    • 64-bit Operating Systems: C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Icons\dark

    • 32-bit Operating Systems: C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Icons\dark

    From the folder, select the following icons:

    • appbar.feature.video.rest.png

    • appbar.stop.rest.png

    • appbar.transport.pause.rest.png

    • appbar.transport.play.rest.png

    These icons are designed for a dark background and are colored white; the icons may appear to be blank on the white background of the Add Existing Item window.

  5. In Solution Explorer, right-click each icon and set the file properties so that the icon is built as Content and always copied to the output directory (Copy always).

To build the UI

  1. On MainPage.xaml, update the phone:PhoneApplicationPage element as shown in the following code.

    SupportedOrientations="Landscape" Orientation="LandscapeLeft"
        shell:SystemTray.IsVisible="False"
    
    

    This code hides the system tray and keeps the application in a landscape orientation. The video from the CaptureSource object cannot be rotated; if it is captured upside-down, the video will be recorded upside-down.

    NoteNote:

    Images from the PhotoCamera class can be rotated. For an example of how to do this, see How to: Create a Base Camera Application for Windows Phone.

  2. On MainPage.xaml, replace the Grid named LayoutRoot with the following code.

        <!--LayoutRoot is the root grid where all page content is placed-->
        <Canvas x:Name="LayoutRoot" Background="Transparent">
    
            <!--Camera viewfinder >-->
            <Rectangle 
                x:Name="viewfinderRectangle"
                Width="640" 
                Height="480" 
                HorizontalAlignment="Left" 
                Canvas.Left="80"/>
    
            <MediaElement 
                x:Name="VideoPlayer" 
                Width="640" 
                Height="480"
                AutoPlay="True" 
                RenderTransformOrigin="0.5, 0.5" 
                VerticalAlignment="Center" 
                HorizontalAlignment="Center" 
                Stretch="Fill"
                Canvas.Left="80"/>
    
            <!--Used for debugging >-->
            <TextBlock 
                Height="40" 
                HorizontalAlignment="Left" 
                Margin="100,428,0,0"
                Name="txtDebug" 
                VerticalAlignment="Top"
                Width="626"
                FontSize="24" 
                FontWeight="ExtraBold"/>
    
        </Canvas>
    
    

    This code specifies a Rectangle named viewfinderRectangle, a MediaElement named VideoPlayer, and a TextBlock named txtDebug. viewfinderRectangle is used to display the video images from the CaptureSource object, VideoPlayer is used for playback of the recorded video after it has been saved to isolated storage, and txtDebug is used to communicate application status.

  3. On MainPage.xaml, below the Grid named LayoutRoot, add the following code.

        <phone:PhoneApplicationPage.ApplicationBar>
            <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True" x:Name="PhoneAppBar" Opacity="0.0">
                <shell:ApplicationBarIconButton IconUri="/Icons/appbar.feature.video.rest.png" Text="record"  x:Name="StartRecording" Click="StartRecording_Click" />
                <shell:ApplicationBarIconButton IconUri="/Icons/appbar.stop.rest.png" Text="stop" x:Name="StopPlaybackRecording" Click="StopPlaybackRecording_Click"/>
                <shell:ApplicationBarIconButton IconUri="/Icons/appbar.transport.play.rest.png" Text="play" x:Name="StartPlayback" Click="StartPlayback_Click"  />
                <shell:ApplicationBarIconButton IconUri="/Icons/appbar.transport.pause.rest.png" Text="pause" x:Name="PausePlayback" Click="PausePlayback_Click"/>
            </shell:ApplicationBar>
        </phone:PhoneApplicationPage.ApplicationBar>
    
    

    This code specifies an Application Bar with four buttons: record, stop, play, and pause. In this application, the stop button is used to stop recording and playback. For more information about using the Application Bar, see Application Bar Overview for Windows Phone.

To prepare the code-behind file

  1. In the code-behind file for the main page, MainPage.xaml.cs, add the following directives to the application.

    // Directives
    using System.ComponentModel;
    using System.Threading;
    using System.IO;
    using System.IO.IsolatedStorage;
    using Microsoft.Phone.Shell;
    using System.Windows.Navigation;
    using Microsoft.Devices;
    
    
  2. In MainPage.xaml.cs, add the following variables above the MainPage constructor.

    // Viewfinder for capturing video.
    private VideoBrush videoRecorderBrush;
    
    // Source and device for capturing video.
    private CaptureSource captureSource;
    private VideoCaptureDevice videoCaptureDevice;
    
    // File details for storing the recording.        
    private IsolatedStorageFileStream isoVideoFile;
    private FileSink fileSink;
    private string isoVideoFileName = "CameraMovie.mp4";
    
    // For managing button and application state.
    private enum ButtonState { Initialized, Ready, Recording, Playback, Paused, NoChange, CameraNotSupported };
    private ButtonState currentAppState;
    
    

    This code declares the variables that are used later in the application.

  3. In MainPage.xaml.cs, add the following code to the MainPage constructor, just below the call to InitializeComponent.

    // Prepare ApplicationBar and buttons.
    PhoneAppBar = (ApplicationBar)ApplicationBar;
    PhoneAppBar.IsVisible = true;
    StartRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[0]);
    StopPlaybackRecording = ((ApplicationBarIconButton)ApplicationBar.Buttons[1]);
    StartPlayback = ((ApplicationBarIconButton)ApplicationBar.Buttons[2]);
    PausePlayback = ((ApplicationBarIconButton)ApplicationBar.Buttons[3]);
    
    

    In this code, the Application Bar buttons are assigned so that they can be referenced in code. For more information about the Application Bar, see Application Bar Overview for Windows Phone.

  4. In MainPage.xaml.cs, add the following code to the MainPage class.

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
    
        // Initialize the video recorder.
        InitializeVideoRecorder();
    }
    
    protected override void OnNavigatedFrom(NavigationEventArgs e)
    {
        // Dispose of camera and media objects.
        DisposeVideoPlayer();
        DisposeVideoRecorder();
    
        base.OnNavigatedFrom(e);
    }
    

    It is important to dispose of the camera-related objects and event-handlers when the page is navigated away from. This helps free memory when the application is not being used. Each time the page is navigated to, the record action worker thread and the camera objects need to be started if they are not running.

To aid the readability of the code, an enumeration and method is used to manage visibility of the Application Bar buttons though the various states of the application. The method, UpdateUI, also updates txtDebug. By abstracting the UI updates to a single method call, other parts of the application require less code.

To update the UI

  • In the code-behind file for the main page, MainPage.xaml.cs, add the following code to the MainPage class.

    // Update the buttons and text on the UI thread based on app state.
    private void UpdateUI(ButtonState currentButtonState, string statusMessage)
    {
        // Run code on the UI thread.
        Dispatcher.BeginInvoke(delegate
        {
    
            switch (currentButtonState)
            {
                // When the camera is not supported by the device.
                case ButtonState.CameraNotSupported:
                    StartRecording.IsEnabled = false;
                    StopPlaybackRecording.IsEnabled = false;
                    StartPlayback.IsEnabled = false;
                    PausePlayback.IsEnabled = false;
                    break;
    
                // First launch of the application, so no video is available.
                case ButtonState.Initialized:
                    StartRecording.IsEnabled = true;
                    StopPlaybackRecording.IsEnabled = false;
                    StartPlayback.IsEnabled = false;
                    PausePlayback.IsEnabled = false;
                    break;
    
                // Ready to record, so video is available for viewing.
                case ButtonState.Ready:
                    StartRecording.IsEnabled = true;
                    StopPlaybackRecording.IsEnabled = false;
                    StartPlayback.IsEnabled = true;
                    PausePlayback.IsEnabled = false;
                    break;
    
                // Video recording is in progress.
                case ButtonState.Recording:
                    StartRecording.IsEnabled = false;
                    StopPlaybackRecording.IsEnabled = true;
                    StartPlayback.IsEnabled = false;
                    PausePlayback.IsEnabled = false;
                    break;
    
                // Video playback is in progress.
                case ButtonState.Playback:
                    StartRecording.IsEnabled = false;
                    StopPlaybackRecording.IsEnabled = true;
                    StartPlayback.IsEnabled = false;
                    PausePlayback.IsEnabled = true;
                    break;
    
                // Video playback has been paused.
                case ButtonState.Paused:
                    StartRecording.IsEnabled = false;
                    StopPlaybackRecording.IsEnabled = true;
                    StartPlayback.IsEnabled = true;
                    PausePlayback.IsEnabled = false;
                    break;
    
                default:
                    break;
            }
    
            // Display a message.
            txtDebug.Text = statusMessage;
    
            // Note the current application state.
            currentAppState = currentButtonState;
        });
    }
    
    

    This code updates the Application Bar buttons and application status. It uses the BeginInvoke method so that the statements can be executed on the UI thread. To simplify the code further, an enumeration named ButtonState is used to specify the state of the application.

In this section, you initialize the CaptureSource and FileSink objects, and display a message that describes how to get started with the application.

To initialize the camera

  • In the code-behind file for the main page, MainPage.xaml.cs, add the following code to the MainPage class.

    public void InitializeVideoRecorder()
    {
        if (captureSource == null)
        {
            // Create the VideoRecorder objects.
            captureSource = new CaptureSource();
            fileSink = new FileSink();
    
            videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
    
            // Add eventhandlers for captureSource.
            captureSource.CaptureFailed += new EventHandler<ExceptionRoutedEventArgs>(OnCaptureFailed);
    
            // Initialize the camera if it exists on the device.
            if (videoCaptureDevice != null)
            {
                // Create the VideoBrush for the viewfinder.
                videoRecorderBrush = new VideoBrush();
                videoRecorderBrush.SetSource(captureSource);
    
                // Display the viewfinder image on the rectangle.
                viewfinderRectangle.Fill = videoRecorderBrush;
    
                // Start video capture and display it on the viewfinder.
                captureSource.Start();
    
                // Set the button state and the message.
                UpdateUI(ButtonState.Initialized, "Tap record to start recording...");
            }
            else
            {
                // Disable buttons when the camera is not supported by the device.
                UpdateUI(ButtonState.CameraNotSupported, "A camera is not supported on this device.");
            }
        }
    }
    
    

    This code performs a number of tasks each time the application is navigated to. Although fileSink is created, it is not connected to captureSource until the user wants to record video. Until then, no video is saved to isolated storage.

    The static method, GetDefaultVideoCaptureDevice, connects the captureSource to the primary camera on the device. To provide a camera viewfinder, captureSource is set as the source of a VideoBrush that is used to fill viewfinderRectangle. It is not until captureSource is started that video images are displayed on the viewfinder.

    TipTip:

    If the device supports multiple cameras, use the GetAvailableVideoCaptureDevices method to obtain a collection of video capture devices. For example, some devices may have a front-facing camera and another camera on the back of the device. It is also possible that the device does not have a camera. In that case, the CameraNotSupported button state is used to disable the Application Bar buttons.

In this section, you add the code that changes the state of the camera between preview and recording. In the context of this topic, recording video means streaming video from the camera to a file in isolated storage. To do that, connect the fileSink to captureSource and then run the captureSource during the period of time that you want persisted in the video file. When captureSource is not running or the fileSink is not connected to captureSource, video will not stream to the file in isolated storage.

To switch camera state between preview and recording

  • In MainPage.xaml.cs, add the following code to the MainPage class.

    // Set recording state: start recording.
    private void StartVideoRecording()
    {
        try
        {
            // Connect fileSink to captureSource.
            if (captureSource.VideoCaptureDevice != null
                && captureSource.State == CaptureState.Started)
            {
                captureSource.Stop();
    
                // Connect the input and output of fileSink.
                fileSink.CaptureSource = captureSource;
                fileSink.IsolatedStorageFileName = isoVideoFileName;
            }
    
            // Begin recording.
            if (captureSource.VideoCaptureDevice != null
                && captureSource.State == CaptureState.Stopped)
            {
                captureSource.Start();
            }
    
            // Set the button states and the message.
            UpdateUI(ButtonState.Recording, "Recording...");
        }
    
        // If recording fails, display an error.
        catch (Exception e)
        {
            this.Dispatcher.BeginInvoke(delegate()
            {
                txtDebug.Text = "ERROR: " + e.Message.ToString();
            });
        }
    }
    
    // Set the recording state: stop recording.
    private void StopVideoRecording()
    {
        try
        {
            // Stop recording.
            if (captureSource.VideoCaptureDevice != null
            && captureSource.State == CaptureState.Started)
            {
                captureSource.Stop();
    
                // Disconnect fileSink.
                fileSink.CaptureSource = null;
                fileSink.IsolatedStorageFileName = null;
    
                // Set the button states and the message.
                UpdateUI(ButtonState.NoChange, "Preparing viewfinder...");
    
                StartVideoPreview();
            }
        }
        // If stop fails, display an error.
        catch (Exception e)
        {
            this.Dispatcher.BeginInvoke(delegate()
            {
                txtDebug.Text = "ERROR: " + e.Message.ToString();
            });
        }
    }
    
    // Set the recording state: display the video on the viewfinder.
    private void StartVideoPreview()
    {
        try
        {
            // Display the video on the viewfinder.
            if (captureSource.VideoCaptureDevice != null
            && captureSource.State == CaptureState.Stopped)
            {
                // Add captureSource to videoBrush.
                videoRecorderBrush.SetSource(captureSource);
    
                // Add videoBrush to the visual tree.
                viewfinderRectangle.Fill = videoRecorderBrush;
    
                captureSource.Start();
    
                // Set the button states and the message.
                UpdateUI(ButtonState.Ready, "Ready to record.");
            }
        }
        // If preview fails, display an error.
        catch (Exception e)
        {
            this.Dispatcher.BeginInvoke(delegate()
            {
                txtDebug.Text = "ERROR: " + e.Message.ToString();
            });
        }
    }
    

    This code changes the recording state of the camera. The StartVideoRecording method stops captureSource, connects fileSink to the camera and isolated storage, and then starts captureSource again. The StopVideoRecording method stops captureSource and disconnects fileSink from the camera and isolated storage. The StartVideoPreview method reconnects captureSource to the viewfinder and then starts it.

In this section, you add the code for the four Application Bar buttons: record, stop, play, and pause. To avoid duplicate calls to these methods from double-tapping, each method immediately disables the corresponding button.

Only one video stream at a time can be displayed in the application UI. Before video playback, the video preview control is removed from the visual tree and vice versa before video preview. The removal is performed by methods created later in this topic, DisposeVideoPlayer and DisposeVideoRecorder.

To handle button taps

  • In MainPage.xaml.cs, add the following code to the MainPage class.

    // Start the video recording.
    private void StartRecording_Click(object sender, EventArgs e)
    {
        // Avoid duplicate taps.
        StartRecording.IsEnabled = false;
    
        StartVideoRecording();
    }
    
    // Handle stop requests.
    private void StopPlaybackRecording_Click(object sender, EventArgs e)
    {
        // Avoid duplicate taps.
        StopPlaybackRecording.IsEnabled = false;
    
        // Stop during video recording.
        if (currentAppState == ButtonState.Recording)
        {
            StopVideoRecording();
    
            // Set the button state and the message.
            UpdateUI(ButtonState.NoChange, "Recording stopped.");
        }
    
        // Stop during video playback.
        else
        {
            // Remove playback objects.
            DisposeVideoPlayer();
    
            StartVideoPreview();
    
            // Set the button state and the message.
            UpdateUI(ButtonState.NoChange, "Playback stopped.");
        }
    }
    
    // Start video playback.
    private void StartPlayback_Click(object sender, EventArgs e)
    {
        // Avoid duplicate taps.
        StartPlayback.IsEnabled = false;
    
        // Start video playback when the file stream exists.
        if (isoVideoFile != null)
        {
            VideoPlayer.Play();
        }
        // Start the video for the first time.
        else
        {
            // Stop the capture source.
            captureSource.Stop();
    
            // Remove VideoBrush from the tree.
            viewfinderRectangle.Fill = null;
    
            // Create the file stream and attach it to the MediaElement.
            isoVideoFile = new IsolatedStorageFileStream(isoVideoFileName,
                                    FileMode.Open, FileAccess.Read,
                                    IsolatedStorageFile.GetUserStoreForApplication());
    
            VideoPlayer.SetSource(isoVideoFile);
    
            // Add an event handler for the end of playback.
            VideoPlayer.MediaEnded += new RoutedEventHandler(VideoPlayerMediaEnded);
    
            // Start video playback.
            VideoPlayer.Play();
        }
    
        // Set the button state and the message.
        UpdateUI(ButtonState.Playback, "Playback started.");
    }
    
    // Pause video playback.
    private void PausePlayback_Click(object sender, EventArgs e)
    {
        // Avoid duplicate taps.
        PausePlayback.IsEnabled = false;
    
        // If mediaElement exists, pause playback.
        if (VideoPlayer != null)
        {
            VideoPlayer.Pause();
        }
    
        // Set the button state and the message.
        UpdateUI(ButtonState.Paused, "Playback paused.");
    }
    
    
    

    This code handles the button taps on the Application Bar. The StartRecording_Click method starts the recording by calling the StartVideoRecording method.

    The StopPlaybackRecording_Click method stops either video recording or video payback, depending on the state of the application. During video recording, this method calls the StopVideoRecording method to disconnect the fileSink from captureSource. During video playback, this method disposes of the video playback objects and calls the StartVideoPreview method to display video on the viewfinder.

    The StartPlayback_Click method stops captureSource and then connects the VideoPlayer MediaElement control to the video file in isolated storage, CameraMovie.mp4. The video appears on the screen when the Play method of VideoPlayer is called. When playback of the video ends, an event handler for the MediaEnded event ensures that the application returns to the preview state.

In this section, you add the code that disposes of the video playback and video recording objects. Only one video stream can run at a time in the application UI. These methods are used to stop one stream so that the other can be started. They also remove event handlers for the corresponding objects to help free memory when they are not in use.

To dispose of the recording and playback objects

  • In MainPage.xaml.cs, add the following code to the MainPage class.

    private void DisposeVideoPlayer()
    {
        if (VideoPlayer != null)
        {
            // Stop the VideoPlayer MediaElement.
            VideoPlayer.Stop();
    
            // Remove playback objects.
            VideoPlayer.Source = null;
            isoVideoFile = null;
    
            // Remove the event handler.
            VideoPlayer.MediaEnded -= VideoPlayerMediaEnded;
        }
    }
    
    private void DisposeVideoRecorder()
    {
        if (captureSource != null)
        {
            // Stop captureSource if it is running.
            if (captureSource.VideoCaptureDevice != null
                && captureSource.State == CaptureState.Started)
            {
                captureSource.Stop();
            }
    
            // Remove the event handler for captureSource.
            captureSource.CaptureFailed -= OnCaptureFailed;
    
            // Remove the video recording objects.
            captureSource = null;
            videoCaptureDevice = null;
            fileSink = null;
            videoRecorderBrush = null;
        }
    }
    
    

    Although these objects do not implement the Dispose method, setting them equal to null will allow the garbage collector to reclaim memory used by these objects when it is needed.

In this section, you add the remaining code and start the application.

To complete the application

  1. In MainPage.xaml.cs, add the following code to the MainPage class.

    // If recording fails, display an error message.
    private void OnCaptureFailed(object sender, ExceptionRoutedEventArgs e)
    {
        this.Dispatcher.BeginInvoke(delegate()
        {
            txtDebug.Text = "ERROR: " + e.ErrorException.Message.ToString();
        });
    }
    
    // Display the viewfinder when playback ends.
    public void VideoPlayerMediaEnded(object sender, RoutedEventArgs e)
    {
        // Remove the playback objects.
        DisposeVideoPlayer();
    
        StartVideoPreview();
    }
    
    

    This code handles the CaptureFailed event from the CaptureSource and the MediaEnded event from the MediaPlayer object, respectively.

  2. You have now completed the application. Press F5 to start debugging and test the application.

Did you find this helpful?
(1500 characters remaining)