Partager via


How to record video in a camera app for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

Starting with Windows Phone OS 7.1, you can programmatically access the phone’s camera. In addition to capturing photos, you can use the phone camera to capture video. This topic describes how to create a video recorder app and demonstrates how to capture, display, and store video in your Windows Phone app. For information about how to capture photos with the phone camera, see How to create a base camera app for Windows Phone 8.

Tip

This topic describes how to use the System.Windows.Media..::.CaptureSource and FileSink classes to record video. If your app requires more control of the camera sensor than what is described in this topic, consider using the AudioVideoCaptureDevice class instead. For more info, see AudioVideoCaptureDevice.

This topic corresponds to the Video Recorder Sample.

This topic covers the following steps:

  1. Creating the app 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 app

The following image shows an example of the completed app.

During recording, this app 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 app, 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 app is implemented, see the following sections.

Creating the app and UI

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

Note

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

To create the app

  1. Using the Windows Phone SDK, create a new project with the Windows Phone App template.

  2. After your app is created, MainPage.xaml is opened in the Visual Studio designer window.

To specify app capabilities

  1. To create a camera app, the camera capability must be declared in the app manifest file. Because a video app also records sound, the microphone capability must also be declared. Without declaring these capabilities, this app 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"/>
    

    If your Windows Phone 8 app requires a back-facing camera, use the ID_REQ_BACKCAMERA hardware requirement. This will prevent users from downloading your app when they don’t have a back-facing camera. For more info about capabilities and requirements, see App capabilities and hardware requirements for Windows Phone 8.

Important Note:

Windows Phone OS 7.1 apps that use the CaptureSource class 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 app.

Although this particular app does not need any of these classes, Windows Phone OS 7.1 apps still require code that incorporates one of the classes to prompt the Windows Phone Store capability detection process to add the ID_CAP_MICROPHONE capability to the app capabilities list upon ingestion. For an example, see the CapabilityPlaceholder file in the Video Recorder Sample.

Starting with Windows Phone 8, specifying the microphone capability is sufficient for accessing the phone’s microphone.

  1. (Optional) If features of your app require a front-facing camera, consider adding the front-facing camera capability or hardware requirement to the app manifest file, depending on which Windows Phone version your app targets:

    • ID_HW_FRONTCAMERA: Windows Phone OS 7.1 apps can use this capability. With this capability, a user without a front-facing camera will be notified that some features of your app will not work on their phone, but they can still choose to download it.

    • ID_REQ_FRONTCAMERA: Windows Phone 8 apps can use this hardware requirement. With this requirement, a user without a front-facing camera won’t be able to download your app.

    For more info about capabilities and requirements, see App capabilities and hardware requirements for Windows Phone 8.

To add icons used by the app

  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 app.

  4. In the Add Existing Item window, navigate to the following path 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.

    C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\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 app 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.

Note

Images from the PhotoCamera class can be rotated. For an example of how to do this, see How to create a base camera app for Windows Phone 8.

  1. 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 app status.

  2. 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 app bar with four buttons: record, stop, play, and pause. In this app, the stop button is used to stop recording and playback. For more information about using the app bar, see App bar 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 app.

    // 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 app.

  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 app bar buttons are assigned so that they can be referenced in code. For more information about the app bar, see App bar 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 app 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.

Updating the UI

To aid the readability of the code, an enumeration and method is used to manage visibility of the app bar buttons though the various states of the app. The method, UpdateUI, also updates txtDebug. By abstracting the UI updates to a single method call, other parts of the app 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 phone.
                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 app bar buttons and app 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 app.

Initializing the camera

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

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 phone.
            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 phone.
                UpdateUI(ButtonState.CameraNotSupported, "A camera is not supported on this phone.");
            }
        }
    }
    

    This code performs a number of tasks each time the app 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 the local folder.

    The static method, GetDefaultVideoCaptureDevice, connects the captureSource to the primary camera on the phone. 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.

Tip

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

Switching camera state between preview and recording

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 the local folder. 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 the local folder.

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 the local folder, and then starts captureSource again. The StopVideoRecording method stops captureSource and disconnects fileSink from the camera and the local folder. The StartVideoPreview method reconnects captureSource to the viewfinder and then starts it.

Handling button taps

In this section, you add the code for the four app 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 app 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 app 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 app. 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 VideoPlayerMediaElement control to the video file in the local folder, 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 app returns to the preview state.

Disposing of the recording and playback objects

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 app 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.

Completing the app

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

To complete the app

  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 app. Press F5 to start debugging and test the app.