Introduction
WebcamControl
is a WPF control for displaying and recording webcam videos using the Expression Encoder SDK. It also enables taking of webcam video snapshots.
Features
The control has the following features,
- Displays webcam video,
- Enables saving of webcam video (in .wmv format),
- Enables saving of video snapshots.
Requirements
NB: To use the control ensure that your project's build platform is set to x86.
Webcam
You can install the control via NuGet by running the following command in the NuGet Package Manager console,
Install-Package WpfWebcamControl -Version 3.3.1
Properties
| Name | Description |
| VideoFileFormat | Gets the format in which webcam videos will be saved. This is a dependency property of type String . |
| SnapshotFormat | Gets or sets the format used when saving snapshots of webcam preview. This is a dependency property of type ImageFormat . |
| VideoDevice | Gets or sets the webcam to be used. This is a dependency property of type Microsoft.Expression.Encoder.Devices.EncoderDevice . |
| AudioDevice | Gets or sets the microphone to be used. This is a dependency property of type Microsoft.Expression.Encoder.Devices.EncoderDevice |
| VideoName | Gets or sets the name of the video file – which should not include the file extension. This is a dependency property of type String . |
| VideoDirectory | Gets or sets the folder where the webcam video will be saved. This is a dependency property of type String . |
| ImageDirectory | Gets or sets the folder where video snapshots will be saved. This is a dependency property of type String . |
| Bitrate | Gets or sets the bitrate. This is a dependency property of type Integer . (The default value is 2000). |
| FrameRate | Gets or sets the frame rate, in frames per second. This is a dependency property of type Integer . (The default value is 15). |
| FrameSize | Gets or sets the size of the video profile. This is a dependency property of type System.Drawing.Size . (The default value is 320x240). |
| IsRecording | Gets a value indicating whether video recording is taking place. This is a dependency property of type Boolean . |
Methods
| Name | Description |
| StartPreview | Starts the display of the webcam preview. (Throws a Microsoft.Expression.Encoder.SystemErrorException if a specified device is already in use by another application) |
| StopPreview | Stops the display of the webcam preview and also stops any ongoing recording. |
| StartRecording | Starts recording of webcam video and returns the full path of the video file. |
| StopRecording | Stops the recording of webcam video. |
| TakeSnapshot | Saves a snapshot of webcam preview and returns the full path of the image file. |
Example
The following example shows how to use the control. The example contains a Webcam
control, two combo boxes for listing video and audio devices, and buttons for calling the various control functions.
<Window x:Class="WPF_Webcam_CS.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WPF_Webcam_CS"
xmlns:cam="clr-namespace:WebcamControl;assembly=WebcamControl"
Title="WPF Webcam" Height="495" Width="353">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="75"/>
<RowDefinition Height="132"/>
</Grid.RowDefinitions>
<cam:Webcam x:Name="WebcamViewer" Margin="10"
FrameRate="30"
FrameSize="640, 480"
ImageDirectory="C:\WebcamSnapshots"
VideoDirectory="C:\VideoClips"
VideoDevice="{Binding SelectedItem, ElementName=VidDevices}"
AudioDevice="{Binding SelectedItem, ElementName=AudDevices}"/>
<Grid Grid.Row="1" HorizontalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Video Device" VerticalAlignment="Center"/>
<ComboBox x:Name="VidDevices" Grid.Column="1" Margin="10,0,0,0"
Width="210" Height="24"
ItemsSource="{Binding VideoDevices}"
DisplayMemberPath="Name"
SelectedIndex="0"/>
<TextBlock Text="Audio Device" Grid.Row="1" VerticalAlignment="Center"/>
<ComboBox x:Name="AudDevices" Grid.Row="1" Grid.Column="1"
Width="210" Height="24" Margin="10,0,0,0"
ItemsSource="{Binding AudioDevices}"
DisplayMemberPath="Name"
SelectedIndex="0"/>
</Grid>
<Grid Grid.Row="2" HorizontalAlignment="Center" Margin="0,10">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Content="Start Capture"
Height="24" Width="112" HorizontalAlignment="Right" Margin="0,0,10,0"
Click="StartCaptureButton_Click"/>
<Button Grid.Column="1" Content="Stop Capture"
Height="24" Width="112" HorizontalAlignment="Left" Margin="10,0,0,0"
Click="StopCaptureButton_Click"/>
<Button Grid.Row="1" Content="Start Recording"
Height="24" Width="112" HorizontalAlignment="Right" Margin="0,0,10,0"
Click="StartRecordingButton_Click"/>
<Button Grid.Row="1" Grid.Column="1" Content="Stop Recording"
Height="24" Width="115" HorizontalAlignment="Left" Margin="10,0,0,0"
Click="StopRecordingButton_Click"/>
<Button Grid.Row="2" Grid.ColumnSpan="2" Content="Take Snapshot"
Height="24" Width="120" HorizontalAlignment="Center"
Click="TakeSnapshotButton_Click"/>
</Grid>
</Grid>
</Window>
using System.Windows;
using Microsoft.Expression.Encoder.Devices;
using System.Collections.ObjectModel;
namespace WPF_Webcam_CS
{
public partial class MainWindow : Window
{
public Collection<EncoderDevice> VideoDevices { get; set; }
public Collection<EncoderDevice> AudioDevices { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
VideoDevices = EncoderDevices.FindDevices(EncoderDeviceType.Video);
AudioDevices = EncoderDevices.FindDevices(EncoderDeviceType.Audio);
}
private void StartCaptureButton_Click(object sender, RoutedEventArgs e)
{
try
{
WebcamViewer.StartPreview();
}
catch (Microsoft.Expression.Encoder.SystemErrorException ex)
{
MessageBox.Show("Device is in use by another application");
}
}
private void StopCaptureButton_Click(object sender, RoutedEventArgs e)
{
WebcamViewer.StopPreview();
}
private void StartRecordingButton_Click(object sender, RoutedEventArgs e)
{
WebcamViewer.StartRecording();
}
private void StopRecordingButton_Click(object sender, RoutedEventArgs e)
{
WebcamViewer.StopRecording();
}
private void TakeSnapshotButton_Click(object sender, RoutedEventArgs e)
{
WebcamViewer.TakeSnapshot();
}
}
}
Imports Microsoft.Expression.Encoder.Devices
Imports System.Collections.ObjectModel
Public Class MainWindow
Public Property VideoDevices As Collection(Of EncoderDevice)
Public Property AudioDevices As Collection(Of EncoderDevice)
Public Sub New()
InitializeComponent()
DataContext = Me
VideoDevices = EncoderDevices.FindDevices(EncoderDeviceType.Video)
AudioDevices = EncoderDevices.FindDevices(EncoderDeviceType.Audio)
End Sub
Private Sub StartCaptureButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
Try
WebcamViewer.StartPreview()
Catch ex As Microsoft.Expression.Encoder.SystemErrorException
MessageBox.Show("Device is in use by another application")
End Try
End Sub
Private Sub StopCaptureButton_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
WebcamViewer.StopPreview()
End Sub
Private Sub StartRecordingButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
WebcamViewer.StartRecording()
End Sub
Private Sub StopRecordingButton_Click(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)
WebcamViewer.StopRecording()
End Sub
Private Sub TakeSnapshotButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
WebcamViewer.TakeSnapshot()
End Sub
End Class
In the code behind for MainWindow
the collection properties, VideoDevices
and AudioDevices
, are set with the available audio and video devices. The rest of the code is self-explanatory, the event handlers for the button click events call various control functions.
Webcam
The following is the XAML markup for the user control,
<UserControl x:Class="Webcam"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms"
MinHeight="100" MinWidth="100"
mc:Ignorable="d">
<Grid>
<WindowsFormsHost Name="WinFormsHost" Margin="0" Background="{x:Null}">
<wf:Panel x:Name="WebcamPanel"/>
</WindowsFormsHost>
</Grid>
</UserControl>
To display video from a webcam the user control makes use of the LiveJob
class, which is in the Microsoft.Expression.Encoder.Live
namespace. LiveJob
expose routines for encoding video and audio from a live source such as a webcam. The webcam video is displayed in a WinForms Panel
which is hosted in a WindowsFormsHost
.
Public Sub StartPreview()
Try
If isPreviewing Then StopPreview()
Job = New LiveJob
Dim frameDuration As Long = CLng(FrameRate * Math.Pow(10, 7))
deviceSource = Job.AddDeviceSource(_videoDevice, _audioDevice)
deviceSource.PickBestVideoFormat(FrameSize, frameDuration)
deviceSource.PreviewWindow = New PreviewWindow(New HandleRef(WebcamPanel, WebcamPanel.Handle))
Job.OutputFormat.VideoProfile = New AdvancedVC1VideoProfile With {.Size = FrameSize,
.FrameRate = FrameRate, .Bitrate = New ConstantBitrate(Bitrate)}
Job.ActivateSource(deviceSource)
isPreviewing = True
Catch ex As SystemErrorException
Throw New SystemErrorException
End Try
End Sub
The LiveJob
object is used to save webcam videos using the LiveJob.StartEncoding()
method.
Public Function StartRecording() As String
If Not isPreviewing Then Throw New PreviewNotStartedException("Recording can't be done without previewing.")
If String.IsNullOrEmpty(VideoDirectory) Then Throw New DirectoryNotSpecifiedException("Video directory has not been specified.")
If Not Directory.Exists(VideoDirectory) Then Directory.CreateDirectory(VideoDirectory)
If IsRecording Then StopRecording()
Dim filePath As String
If String.IsNullOrEmpty(VideoName) Then
filePath = Path.Combine(VideoDirectory, "Webcam " & TimeStamp() & VideoFileFormat)
Else
filePath = Path.Combine(VideoDirectory, VideoName & VideoFileFormat)
End If
Dim archiveFormat As New FileArchivePublishFormat(filePath)
If Job.PublishFormats.Count > 0 Then Job.PublishFormats.Clear()
Job.PublishFormats.Add(archiveFormat)
Job.StartEncoding()
SetValue(IsRecordingPropertyKey, True)
Return filePath
End Function
A snapshot of a webcam video is actually just a snapshot of the WinForms Panel
.
Public Function TakeSnapshot() As String
If Not isPreviewing Then Throw New PreviewNotStartedException("Recording can't be done before previewing.")
If String.IsNullOrEmpty(ImageDirectory) Then Throw New DirectoryNotSpecifiedException("Image directory has not been specified")
If Not Directory.Exists(ImageDirectory) Then Directory.CreateDirectory(ImageDirectory)
Dim panelWidth As Integer = WebcamPanel.Width
Dim panelHeight As Integer = WebcamPanel.Height
Dim filePath As String = Path.Combine(ImageDirectory, "Snapshot " & TimeStamp() & "." & SnapshotFormat.ToString())
Dim pnt As Point = WebcamPanel.PointToScreen(New Point(WebcamPanel.ClientRectangle.X, WebcamPanel.ClientRectangle.Y))
Using bmp As New Bitmap(panelWidth, panelHeight)
Using grx As Graphics = Graphics.FromImage(bmp)
grx.CopyFromScreen(pnt, Point.Empty, New Size(panelWidth, panelHeight))
End Using
bmp.Save(filePath, SnapshotFormat)
End Using
Return filePath
End Function
Public Function TakeSnapshot(ByVal name As String) As String
If String.IsNullOrEmpty(name) Then Throw New ArgumentNullException()
If Not isPreviewing Then Throw New PreviewNotStartedException("Recording can't be done before previewing.")
If String.IsNullOrEmpty(ImageDirectory) Then Throw New DirectoryNotSpecifiedException("Image directory has not been specified")
If Not Directory.Exists(ImageDirectory) Then Directory.CreateDirectory(ImageDirectory)
Dim panelWidth As Integer = WebcamPanel.Width
Dim panelHeight As Integer = WebcamPanel.Height
Dim filePath As String = Path.Combine(ImageDirectory, name & "." & SnapshotFormat.ToString())
Dim pnt As Point = WebcamPanel.PointToScreen(New Point(WebcamPanel.ClientRectangle.X, WebcamPanel.ClientRectangle.Y))
Using bmp As New Bitmap(panelWidth, panelHeight)
Using grx As Graphics = Graphics.FromImage(bmp)
grx.CopyFromScreen(pnt, Point.Empty, New Size(panelWidth, panelHeight))
End Using
bmp.Save(filePath, SnapshotFormat)
End Using
Return filePath
End Function
If you want to take a look at the rest of the code for the usercontrol check out the user control library project in the source download.
Conclusion
I hope you found this article useful. In case of any queries you can leave a comment and I'll do my best to answer.
History
- 18th Nov, 2011: Initial post
- 19th Nov, 2011: Updated code
- 31st Mar, 2012: Added snapshot feature.
- 17th Nov, 2012: Added
FrameRate
and FrameSize
properties.
- 30th Oct, 2013: v3.0
- 24th July, 2014: v3.1,
- Webcam preview now resizes with control,
StartCapture()
renamed to StartPreview()
,
StopCapture()
renamed to StopPreview()
.
- 6th May, 2016: Added
Bitrate
and VideoName
properties.
- 24th Sep, 2017: Updated code –
StartRecording()
and TakeSnapshot()
return full file path.