
Introduction
I have on several occasions scoured the net for a simple to use WPF webcam control and either my search queries were awful or I just wasn't comfortable with whatever I found. This webcam interest was recently increased when I read an article, here on CodeProject, that referred to an application that made use of a webcam. It was a Silverlight article and the ease with which one could utilize a webcam in Silverlight made me envious of WPF's little brother (or is it sister?). The VideoBrush in Silverlight is especially a nice touch.
It is with this pain and envy in mind that I decided to try my hand at creating a WPF control that could;
- Display webcam video with little coding effort,
- Allow saving of webcam video to harddisk, with little coding effort,
- Save the webcam video in a variety of video formats
Background
With the previously mentioned goals in mind, I created a WPF UserControl that has the following features,
- Displays webcam video,
- Enables saving of webcam videos to harddisk,
- Enables saving of webcam videos in either .wmv, .mp4, or .flv format
The UserControl also enables you to take a snapshot of the live webcam video, and save it as a Jpeg, Png, Gif, or any other of the ImageFormat properties.
The Webcam control makes heavy use of the Expression Encoder 4 SDK. You therefore need to have the SDK installed on your machine to make use of the UserControl. You can download the SDK from here.
Requirements
To make use of the Webcam control, you require;
- .NET Framework 4.0
- Expression Encoder SDK
Using the Webcam Control
To use the Webcam control in your WPF application add a reference to WebcamControl.dll and a using/Imports statement for WebcamControl at the top of your class.
Add a reference to Microsoft.Expression.Encoder.dll . Do this by using the Add Reference dialog box, selecting the .NET tab, and selecting Microsoft.Expression.Encoder from the listbox. The Expression Encoder assemblies should be available if you have installed Expression Encoder 4.
The following example, which is the downloadable sample application, shows the use of the Webcam control. The Webcam control is defined in the Window's XAML markup.
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cam="clr-namespace:WebcamControl;assembly=WebcamControl"
...
>
<Grid>
<cam:Webcam Name="WebCamCtrl" Margin="12,12,12,204"></cam:Webcam>
...
</Grid>
</Window>
VB.NET
Imports Microsoft.Expression.Encoder.Devices
Imports WebcamControl
Imports System.IO
Imports Microsoft.Expression.Encoder.Live
Imports Microsoft.Expression.Encoder
Imports System.Drawing
Imports System.Drawing.Imaging
Class MainWindow
Private Sub MainWindow_Initialized(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Initialized
Dim bndg_1 As New Binding("SelectedValue")
bndg_1.Source = VidDvcsComboBox
WebCamCtrl.SetBinding(Webcam.VideoDeviceProperty, bndg_1)
Dim bndg_2 As New Binding("SelectedValue")
bndg_2.Source = AudDvcsComboBox
WebCamCtrl.SetBinding(Webcam.AudioDeviceProperty, bndg_2)
End Sub
Private Sub MainWindow_Loaded(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
FindDevices()
Dim vidPath As String = "C:\VideoClips"
Dim imgPath As String = "C:\WebcamSnapshots"
If Directory.Exists(vidPath) = False Then
Directory.CreateDirectory(vidPath)
End If
If Directory.Exists(imgPath) = False Then
Directory.CreateDirectory(imgPath)
End If
WebCamCtrl.VideoDirectory = vidPath
WebCamCtrl.VidFormat = VideoFormat.mp4
WebCamCtrl.ImageDirectory = imgPath
WebCamCtrl.PictureFormat = ImageFormat.Jpeg
WebCamCtrl.FrameRate = 30
WebCamCtrl.FrameSize = New Size(320, 240)
VidDvcsComboBox.SelectedIndex = 0
AudDvcsComboBox.SelectedIndex = 0
End Sub
Private Sub FindDevices()
Dim vidDevices = EncoderDevices.FindDevices(EncoderDeviceType.Video)
Dim audDevices = EncoderDevices.FindDevices(EncoderDeviceType.Audio)
For Each dvc In vidDevices
VidDvcsComboBox.Items.Add(dvc.Name)
Next
For Each dvc In audDevices
AudDvcsComboBox.Items.Add(dvc.Name)
Next
End Sub
Private Sub StartButton_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles StartButton.Click
Try
WebCamCtrl.StartCapture()
Catch ex As Microsoft.Expression.Encoder.SystemErrorException
MessageBox.Show("Device is in use by another application")
End Try
End Sub
Private Sub EndButton_Click(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles EndButton.Click
WebCamCtrl.StopCapture()
End Sub
Private Sub RecordButton_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles RecordButton.Click
WebCamCtrl.StartRecording()
End Sub
Private Sub StopRecordButton_Click(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles StopRecordButton.Click
WebCamCtrl.StopRecording()
End Sub
Private Sub SnapshotButton_Click(ByVal sender As System.Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles SnapshotButton.Click
WebCamCtrl.TakeSnapshot()
End Sub
End Class
C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Expression.Encoder.Devices;
using WebcamControl;
using System.IO;
using System.Drawing.Imaging;
namespace WPF_Webcam
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Binding bndg_1 = new Binding("SelectedValue");
bndg_1.Source = VidDvcsComboBox;
WebCamCtrl.SetBinding(Webcam.VideoDeviceProperty, bndg_1);
Binding bndg_2 = new Binding("SelectedValue");
bndg_2.Source = AudDvcsComboBox;
WebCamCtrl.SetBinding(Webcam.AudioDeviceProperty, bndg_2);
string vidPath = @"C:\VideoClips";
if (Directory.Exists(vidPath) == false)
{
Directory.CreateDirectory(vidPath);
}
string imgPath = @"C:\WebcamSnapshots";
if (Directory.Exists(imgPath) == false)
{
Directory.CreateDirectory(imgPath);
}
WebCamCtrl.VideoDirectory = vidPath;
WebCamCtrl.VidFormat = VideoFormat.mp4;
WebCamCtrl.ImageDirectory = imgPath;
WebCamCtrl.PictureFormat = ImageFormat.Jpeg;
WebCamCtrl.FrameRate = 30;
WebCamCtrl.FrameSize = new System.Drawing.Size(320, 240);
FindDevices();
VidDvcsComboBox.SelectedIndex = 0;
AudDvcsComboBox.SelectedIndex = 0;
}
private void FindDevices()
{
var vidDevices = EncoderDevices.FindDevices(EncoderDeviceType.Video);
var audDevices = EncoderDevices.FindDevices(EncoderDeviceType.Audio);
foreach (EncoderDevice dvc in vidDevices)
{
VidDvcsComboBox.Items.Add(dvc.Name);
}
foreach (EncoderDevice dvc in audDevices)
{
AudDvcsComboBox.Items.Add(dvc.Name);
}
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
try
{
WebCamCtrl.StartCapture();
}
catch (Microsoft.Expression.Encoder.SystemErrorException ex)
{
MessageBox.Show("Device is in use by another application");
}
}
private void EndButton_Click(object sender, RoutedEventArgs e)
{
WebCamCtrl.StopCapture();
}
private void RecordButton_Click(object sender, RoutedEventArgs e)
{
WebCamCtrl.StartRecording();
}
private void StopRecordButton_Click(object sender, RoutedEventArgs e)
{
WebCamCtrl.StopRecording();
}
private void SnapshotButton_Click(object sender, RoutedEventArgs e)
{
WebCamCtrl.TakeSnapshot();
}
}
}
As you can see from the sample code using the Webcam control is not a hard affair once you have the necessary references and using/Imports statements.
Webcam
The following are the members of interest in class Webcam,
Properties
|
Name |
Description |
Type |
 |
VideoDirectory |
Gets or Sets the folder where the recorded webcam video will be saved. This is a dependency property. |
String |
 |
VidFormat |
Gets or Sets the video format in which the webcam video will be saved. This is a dependency property. (The default format is .wmv) |
VideoFormat |
 |
VideoDevice |
Gets or Sets the name of the video device to be used. This is a dependency property. |
String |
 |
AudioDevice |
Gets or Sets the name of the audio device to be used. This is a dependency property. |
String |
 |
IsRecording |
Gets a value indicating whether video recording is taking place. This is a read-only property. |
Boolean |
 |
ImageDirectory |
Gets or Sets the folder where a snapshot of the webcam video will be saved. This is a dependency property. |
String |
 |
PictureFormat |
Gets or Sets the format in which a snapshot of the webcam video will be saved. This is a dependency property. (The default format is Jpeg). |
ImageFormat |
 |
FrameRate |
Gets or sets the frame rate in frames per second. This is a dependency property. (The default value is 15). |
Integer |
 |
FrameSize |
Gets or sets the size of the video profile. This is a dependency property. (The default size is 320x240). |
System.Drawing.Size |
Methods
|
Name |
Description |
 |
StartCapture |
Displays webcam video on control. (Throws a Microsoft.Expression.Encoder.SystemErrorException if a specified device is already in use by another application) |
 |
StopCapture |
Stops the capturing/display of webcam video. (Stops any current recording of webcam video) |
 |
StartRecording |
Starts the recording of webcam video to a video file. (Throws a DirectoryNotFoundException if the directory specified in the VideoDirectory property does not exist) |
 |
StopRecording |
Stops the recording of webcam video. |
 |
TakeSnapshot |
Saves a snapshot of the webcam video. (Throws a DirectoryNotFoundException if the directory specified in the ImageDirectory property does not exist) |
The Code
The XAML markup for the UserControl is,
<UserControl x:Class="Webcam"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="Auto" Width="Auto" MinHeight="100" MinWidth="100"
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"
mc:Ignorable="d" d:DesignWidth="320" d:DesignHeight="240" Name="Webcam">
<Grid>
<WindowsFormsHost Margin="0,0,0,0" Name="WinFormHost" Background="{x:Null}">
<wf:Panel x:Name="WebcamPanel" Size="320,240" />
</WindowsFormsHost>
</Grid>
</UserControl>
The
VideoDevice dependency property is defined as follows,
Public Property VideoDevice() As String
Get
Return CType(GetValue(VideoDeviceProperty), String)
End Get
Set(ByVal value As String)
SetValue(VideoDeviceProperty, value)
End Set
End Property
Public Shared VideoDeviceProperty As DependencyProperty = _
DependencyProperty.Register("VideoDevice", GetType(String), GetType(Webcam), _
New FrameworkPropertyMetadata(New PropertyChangedCallback( _
AddressOf VidDeviceChange)))
Private Shared Sub VidDeviceChange(ByVal source As DependencyObject, _
ByVal e As DependencyPropertyChangedEventArgs)
Dim deviceName As String = CType(e.NewValue, String)
Dim eDev = EncoderDevices.FindDevices(EncoderDeviceType.Video).Where _
(Function(dv) dv.Name = deviceName)
If (eDev.Count > 0) Then
CType(source, Webcam).vidDevice = eDev.First
Try
CType(source, Webcam).Display()
Catch ex As Microsoft.Expression.Encoder.SystemErrorException
Exit Sub
End Try
End If
End Sub
The StartCapture() method displays the webcam video in the WinForm Panel, if the necessary properties are set,
Public Sub StartCapture()
If (canCapture = False) Then
canCapture = True
Try
Display()
Catch ex As Microsoft.Expression.Encoder.SystemErrorException
canCapture = False
Throw New Microsoft.Expression.Encoder.SystemErrorException
End Try
Else
Exit Sub
End If
End Sub
Private Sub Display()
If (canCapture = True) Then
If (vidDevice IsNot Nothing) Then
StopRecording()
Dispose()
job = New LiveJob
Dim frameDuration As Long = CLng(_frameRate * Math.Pow(10, 7))
deviceSource = job.AddDeviceSource(vidDevice, audDevice)
deviceSource.PickBestVideoFormat(_frameSize, frameDuration)
WebcamPanel.Size = _frameSize
job.OutputFormat.VideoProfile.Size = _frameSize
deviceSource.PreviewWindow = New PreviewWindow(New HandleRef(WebcamPanel, WebcamPanel.Handle))
job.ActivateSource(deviceSource)
isCapturing = True
End If
End If
End Sub
The StartRecording() method records video from the webcam to the harddisk,
Public Sub StartRecording()
If (vidDirectory <> String.Empty AndAlso job IsNot Nothing) Then
If (Directory.Exists(vidDirectory) = False) Then
Throw New DirectoryNotFoundException("The specified directory does not exist")
Exit Sub
End If
If (isCapturing = True) Then
StopRecording()
job.PublishFormats.Clear()
Dim timeStamp As String = DateTime.Now.ToString
timeStamp = timeStamp.Replace("/", "-")
timeStamp = timeStamp.Replace(":", ".")
Dim filePath As String = vidDirectory & "\WebcamVid " & timeStamp & "." & _vidFormat.ToString
Dim fileArchFormat As New FileArchivePublishFormat(filePath)
job.PublishFormats.Add(fileArchFormat)
job.StartEncoding()
_isRecording = True
End If
End If
End Sub
The VideoFormat enumeration contains three members,
Public Enum VideoFormat
wmv
mp4
flv
End Enum
The TakeSnapshot() method saves a snapshot of the webcam video. The image generated is actually a snapshot of the WinForms Panel, WebcamPanel. The size of the image will depend on the size of the Webcam control.
Public Sub TakeSnapshot()
If (imgDirectory <> String.Empty AndAlso job IsNot Nothing) Then
If (Directory.Exists(imgDirectory) = False) Then
Throw New DirectoryNotFoundException("The specified directory does not exist")
Exit Sub
End If
If (isCapturing = True) Then
Dim panelWidth As Integer = CInt(Me.ActualWidth)
Dim panelHeight As Integer = CInt(Me.ActualHeight)
Dim timeStamp As String = DateTime.Now.ToString
timeStamp = timeStamp.Replace("/", "-")
timeStamp = timeStamp.Replace(":", ".")
Dim filePath As String = imgDirectory & "\Snapshot " & timeStamp & "." & imgFormat.ToString
Dim pnlPnt As Point = WebcamPanel.PointToScreen(New Point(WebcamPanel.ClientRectangle.X, _
WebcamPanel.ClientRectangle.Y))
Using bmp As New Bitmap(panelWidth, panelHeight)
Using gcs As Graphics = Graphics.FromImage(bmp)
gcs.CopyFromScreen(pnlPnt, Point.Empty, New Size(panelWidth, panelHeight))
End Using
bmp.Save(filePath, imgFormat)
End Using
End If
End If
End Sub
You can take a look at the other properties and methods defined in class Webcam by downloading the source files from the download link at the top of this article.
Conclusion
I hope that you picked up something useful from this article. I'm a novice in audio-video-encoding-decoding matters so if you have any questions regarding such technicalities, please try to post them in the associated forums here on CodeProject. Suggestions will be beneficial, as well as answers you receive to any technical queries that may be associated with this article's content. Thanks.
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.