New Year, New Dev: Video Capture and Media Editing — Part 1
Over the next two posts, we’ll show you how to properly capture video and edit it using UWP’s powerful, but easy to use, MediaCapture and MediaComposition APIs.
In this post, we’ll cover a responsible pattern for working with camera and capturing video. There are considerations when working with MediaCapture, such as needing to be dispose it when done, or you’ll encounter problems when trying to use it again.
Check out the code samples we reference right here.
Before we begin to code, let’s think about the steps that we’ll take and break them into methods. This will make the code easier to read, debug, and manage UI states.
The logic steps are:
- InitializeCameraAsync
- StartPreviewAsync
- StartRecordingAsync
- StopRecordingAsync
- StopPreviewAsync
- CleanupCameraAsync
With this pattern designed, we can get started. You can follow along using the Github repo containing this full example here.
InitializeCamera
This is where we new-up a MediaCapture object, hook into important events and assign it the camera we want to use. First, let’s find a camera on the user’s device:
var cameraDevice = await FindCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel.Back);
Next, we create a MediaCaptureInitializationSettings using that camera’s ID:
mediaCapture = new MediaCapture();// Events when video recording has reached the maximum time and when something goes wrongmediaCapture.RecordLimitationExceeded += MediaCapture_RecordLimitationExceeded;mediaCapture.Failed += MediaCapture_Failed;
Now, we take the important step of initializing the MediaCapture instance. It’s important to wrap this in a try/catch because if the app doesn’t have access, it will fail.
try{await mediaCapture.InitializeAsync(settings);isInitialized = true;}catch (UnauthorizedAccessException){Debug.WriteLine(“The app was denied access to the camera”);
If the initialization was successful, we can now call the next method: StartPreviewAsync
await StartPreviewAsync();
Note: Before calling StartPreviewAsync, you would also want to consider different camera configurations, like camera rotation and camera enclosure location. See the demo source code here for CameraRotationHelper implementation.
StartPreviewAsync
Before starting on the method, we’ll need to add a CaptureElement to the XAML. This is where the camera’s preview stream will be shown:
<CaptureElement Name=”PreviewControl” Stretch=”Uniform” />
With that taken care of, let’s work on the content of StartPreviewAsync. We want to make sure that the user’s display doesn’t turn off when previewing or recording video. UWP has a powerful API, DisplayRequest, that lets you temporarily disable the screen from locking.
At the top of the page class, define a private field:
private DisplayRequest displayRequest = new DisplayRequest();
Now at the beginning of StartPreviewAsync, you can do the following to disable screen lock:
displayRequest.RequestActive();
Next, we set the MediaCapture as the CaptureElement’s source:
PreviewControl.Source = mediaCapture;
Now, we can actually start the camera preview stream (and toggle isPreviewing):
await mediaCapture.StartPreviewAsync();isPreviewing = true;
Note: You can handle preview rotation here if your camera stream needs to be rotated. See the demo source code here for implementation.
StartRecordingAsync
In this method, the first thing we need to do is create a StorageFile object for the MediaCapture to save the video stream to (for the purposes of this demo, we’re using the LocalFolder, but you can save to the user’s Videos or Photos library as long as you’ve enabled that Capability in your AppManifest).
var videoFile = await ApplicationData.Current.LocalFolder.CreateFileAsync($”MyVideo.mp4");
Next, we set the media encoding profile to make it an mp4 file:
var encodingProfile = MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Auto);
With that handled, we can start recording (and toggle isRecording):
await mediaCapture.StartRecordToStorageFileAsync(encodingProfile, videoFile);isRecording = true;
Note: Before starting the recording, if you have handled camera rotation, you can set this to the encodingProfile. See the demo source code here for the implementation.
StopRecordingAsync
When the user is done and stops recording, you want to call your StopRecordingAsync method. In here we only need to call StopRecord on the MediaCapture and toggle your isRecording flag.
await mediaCapture.StopRecordAsync();isRecording = false;
The MediaCapture will finalize and save the file. That’s it!
StopPreviewAsync
Now, we’re moving into how to be responsible with our resources. With MediaCapture, you want to stop previewing and release the camera when the app is going to be suspended or the page is navigating away.
The first step in this process is to stop the preview stream, toggle your isPreviewing flag and null the CaptureElement’s source to clean up the UI.
await mediaCapture.StopPreviewAsync();isPreviewing = false;PreviewControl.Source = null;
Lastly, release the hold you put on the user’s screen from locking:
displayRequest.RequestRelease();
CleanUpCameraAsync
Lastly, and probably most importantly, we need to clean up the resources we’re using. Missing this step will cause you a lot of problems. The best place to call this would be in the OnPageNavigatingFrom and also ApplicationSuspending event handlers. This will ensure that no matter what the user does to leave your recording page, you’ve responsibly handled the resources.
Here’s the method (notice mediaCapture.Dispose(), this is critical):
private async Task CleanupCameraAsync(){if (isInitialized){// If a recording is in progress during cleanup, stop it to save the recordingif (isRecording){await StopRecordingAsync();}if (isPreviewing){await StopPreviewAsync();}isInitialized = false;}if (mediaCapture != null){mediaCapture.RecordLimitationExceeded -= MediaCapture_RecordLimitationExceeded;mediaCapture.Failed -= MediaCapture_Failed;mediaCapture.Dispose();mediaCapture = null;}}
Note: If you’re using the RotationHelper class, you would clean that up here as well. See demo source code here for implementation.
UI Tips
You need to also think about managing the Start Recording and Stop Recording buttons in the UI. You don’t want the button to be enabled until you’ve ensured that the preview stream is valid and is showing. You also want the appropriate button showing at the right time: record button while previewing and stop button while recording.
One approach is to have a method that does everything to the UI that’s needed depending on the state of isPreviewing and isRecording fields.
Here’s the approach we use in the demo:
private void UpdateCaptureControls(){// The buttons should only be enabled if the preview started successfullyVideoButton.IsEnabled = isPreviewing;// Update recording button to show “Stop” icon instead of red “Record” iconStartRecordingIcon.Visibility = isRecording ? Visibility.Collapsed : Visibility.Visible;StopRecordingIcon.Visibility = isRecording ? Visibility.Visible : Visibility.Collapsed;}
Here’s the button. Notice how we can use a single button but with multiple icons, thus letting us share a single click event.
Button XAML
<Button Name=”VideoButton”Click=”VideoButton_Click”IsEnabled=”False”><Grid><Ellipse x:Name=”StartRecordingIcon”Fill=”Red”Width=”20"Height=”20" /><Rectangle x:Name=”StopRecordingIcon”Fill=”White”Width=”20"Height=”20"Visibility=”Collapsed” /></Grid></Button>Button click event handlerprivate async void VideoButton_Click(object sender, RoutedEventArgs e){if (!isRecording){await StartRecordingAsync();}else{await StopRecordingAsync();}// Update the UI to reflect the MediaCapture stateUpdateCaptureControls();}
This wraps up part one, be sure to check out the full demo code here as it contains the rest of the code we didn’t show here. For example, using the RotationHelper and mirroring the preview stream in the case of a front-facing camera.
in the next post, we’ll move on to editing the video recorded in this post! We’ll show you how to create a video clip and trim it using the powerful MediaComposition APIs.