Click here to Skip to main content
11,494,964 members (35,523 online)
Click here to Skip to main content

Yet another Web Camera control

, 9 Apr 2015 CPOL 67.9K 15.1K 125
Rate this:
Please Sign up or sign in to vote.
In this article you will find yet another implementation of a web camera control.


330177/screen.jpg

Introduction

In this article you will find yet another implementation of a web camera control. The control is a simple and easy to use one: no additional dependencies and a minimalistic interface.

The control provides the following functionalities:

  1. Gets a list of available web camera devices on a system.
  2. Displays a video stream from a web camera device.
  3. Gets the current image being captured.

Requirements

  1. The WinForms version of the control is implemented using .NET Framework 2.0.
  2. The WPF version of the control is implemented using .NET Framework 4 Client Profile.
  3. The control uses the VMR-9 renderer filter available since Windows XP SP2.

The control supports both x86 and x64 platform targets.

Background

There are a number of ways to capture a video stream in Windows. Not mentioning all of them, the basic are DirectShow framework and AVICap library. We will use the DirectShow based approach, as it is more powerful and flexible.

The DirectShow framework operates using such concepts as a graph, filters and pins. The filters form a capture graph, through which a media stream flows. The filters in the graph are connected to each other using pins. A web camera is the capture filter a video stream starts from. The control’s window is passed to a renderer filter, which receives and shows the video stream. There are other in-the-middle filters possible, for example, color space conversion filters. That is all about the capture graph. See MSDN DirectShow documentation for more information.

Implementation Details

If you are not interested in implementation details, then you can skip this section.

The implementation is divided into three layers.

  1. The bottom layer is implemented as a native DLL module, which forwards our calls to the DirectShow framework.
  2. For distribution convenience, the native DLL module is embedded into the control’s assembly as a resource. On runtime stage, the DLL module will be extracted to a temporary file on disk and used via late binding technique. Once the control is disposed, the temporary file will be deleted. In other words, the control is distributed as a single file. All those operations are implemented by the middle layer.
  3. The top layer implements the control class itself and the WebCameraId class used to identify a web camera device.

The following diagram shows a logical structure of the implementation.

Only the top layer is supposed to be used by clients.

The Bottom Layer

The bottom layer implements the following utilities to work with the capture graph.

/// <summary>
/// Enumerates video input devices in a system.
/// </summary>
/// <param name="callback">A callback method.</param>
DSUTILS_API void __stdcall EnumVideoInputDevices(EnumVideoInputDevicesCallback callback);

/// <summary>
/// Builds a video capture graph.
/// </summary>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall BuildCaptureGraph();

/// <summary>
/// Adds a renderer filter to a video capture graph,
/// which renders a video stream within a container window.
/// </summary>
/// <param name="hWnd">A container window that video should be clipped to.</param>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall AddRenderFilter(HWND hWnd);

/// <summary>
/// Adds a video stream source to a video capture graph.
/// </summary>
/// <param name="devicePath">A device path of a video capture filter to add.</param>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall AddCaptureFilter(BSTR devicePath);

/// <summary>
/// Removes a video stream source from a video capture graph.
/// </summary>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall ResetCaptureGraph();

/// <summary>
/// Runs all the filters in a video capture graph. While the graph is running,
/// data moves through the graph and is rendered. 
/// </summary>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall Start();

/// <summary>
/// Stops all the filters in a video capture graph.
/// </summary>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall Stop();

/// <summary>
/// Retrieves the current image being displayed by the renderer filter.
/// </summary>
/// <param name="ppDib">Address of a pointer to a BYTE that will receive the DIB.</param>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall GetCurrentImage(BYTE **ppDib);

/// <summary>
/// Retrieves the unstretched video size.
/// </summary>
/// <param name="lpWidth">A pointer to a LONG that will receive the width.</param>
/// <param name="lpHeight">A pointer to a LONG that will receive the height.</param>
/// <returns>If the function succeeds, the return value is zero.</returns>
DSUTILS_API int __stdcall GetVideoSize(LONG *lpWidth, LONG *lpHeight);

/// <summary>
/// Destroys a video capture graph.
/// </summary>
DSUTILS_API void __stdcall DestroyCaptureGraph();

The Middle Layer

The middle layer is implemented in the DirectShowProxy class.

First, what we should do is extract the capture graph utilities DLL module from the resources and save it to a temporary file.

_dllFile = Path.GetTempFileName();
using (FileStream stream = new FileStream(_dllFile, FileMode.Create, FileAccess.Write))
{
    using (BinaryWriter writer = new BinaryWriter(stream))
    {
        writer.Write(IsX86Platform ?
            Resources.DirectShowFacade : Resources.DirectShowFacade64);
    }
}

Then we load our DLL module into the address space of the calling process.

_hDll = LoadLibrary(_dllFile);
if (_hDll == IntPtr.Zero)
{
    throw new Win32Exception(Marshal.GetLastWin32Error());
}

And bind the DLL module functions to the class instance methods.

private delegate Int32 BuildCaptureGraphDelegate();
private BuildCaptureGraphDelegate _buildCaptureGraph;

// ...

IntPtr pProcPtr = GetProcAddress(_hDll, "BuildCaptureGraph");
_buildCaptureGraph =
    (BuildCaptureGraphDelegate)Marshal.GetDelegateForFunctionPointer(pProcPtr, 
     typeof(BuildCaptureGraphDelegate));

When the control is being disposed, we unload the DLL module and delete it.

public void Dispose()
{
    if (_hDll != IntPtr.Zero)
    {
        FreeLibrary(_hDll);
        _hDll = IntPtr.Zero;
    }

    if (File.Exists(_dllFile))
    {
        File.Delete(_dllFile);
    }
}

The Top Layer

The top layer is implemented in the WebCameraControl class with the following interface.

/// <summary>
/// Gets a list of available video capture devices.
/// </summary>                                 
/// <exception cref="Win32Exception">Failed to load the DirectShow utilities dll.</exception>
public IEnumerable<WebCameraId> GetVideoCaptureDevices();

/// <summary>
/// Gets a value indicating whether the control is capturing a video stream.
/// </summary>
public Boolean IsCapturing { get; }

/// <summary>
/// Starts a capture.
/// </summary>
/// <param name="camera">The camera to capture from.</param>
/// <exception cref="ArgumentNullException">A null reference is passed as an argument.</exception>
/// <exception cref="Win32Exception">Failed to load the DirectShow utilities dll.</exception>
/// <exception cref="DirectShowException">Failed to run a video capture graph.</exception>
public void StartCapture(WebCameraId camera);

/// <summary>
/// Retrieves the unstretched image being captured.
/// </summary>
/// <returns>The current image.</returns>
/// <exception cref="InvalidOperationException">The control is not capturing a video stream.</exception>
/// <exception cref="DirectShowException">Failed to get the current image.</exception>
public Bitmap GetCurrentImage();

/// <summary>
/// Gets the unstretched video size.
/// </summary>
public Size VideoSize { get; }

/// <summary>
/// Stops a capture.
/// </summary>
/// <exception cref="InvalidOperationException">The control is not capturing a video stream.</exception>
/// <exception cref="DirectShowException">Failed to stop a video capture graph.</exception>
public void StopCapture();

Usage

First, we need to add the control to the Visual Studio Designer Toolbox, using a right-click and then the "Choose Items..." menu item. Then we place the control on a form at desired location and with desired size. The default name of the control instance variable will be webCameraControl1.

Then, on run-time stage, we need to get a list of web cameras available on the system.

List<WebCameraId> cameras = new List<WebCameraId>(webCameraControl1.GetVideoCaptureDevices());

The following code starts a capture from the first camera in the list.

webCameraControl1.StartCapture(cameras[0]); 

To get an image being captured just call the GetCurrentImage() method. The resolution and quality of the image depend on your camera device characteristics.

Bitmap image = webCameraControl1.GetCurrentImage();

To stop the capture the StopCapture() method is used.

webCameraControl1.StopCapture();

You can always ask the capture state using the following code.

if (webCameraControl1.IsCapturing)
{
    webCameraControl1.StopCapture();
}

To start the capture from another web camera just call the StartCapture method again.

webCameraControl1.StartCapture(cameras[1]);

To report errors, exceptions are used, so do not forget to wrap your code in a try/catch block. That is all about using it. To see the complete example please check the demo application sources.

WPF Version

In WPF user controls do not have a WinAPI window handle (HWND) associated with them and this is a problem, because the DirectShow framework requires a window handle in order to output the video stream. The VideoWindow class has been introduced to workaround this problem.

<UserControl x:Class="WebCamera.WebCameraControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             xmlns:local="clr-namespace:WebCamera">
    <local:VideoWindow x:Name="_videoWindow" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</UserControl>

GitHub

The project has a GitHub repository available on the following page.

https://github.com/jacobbo/WebEye/tree/master/WebCameraControl

Any questions, remarks, and comments are welcome.

History 

  • April 10, 2015 - Added the x64 platform support.
  • October 24, 2012 - Added a GitHub repository.
  • September 12, 2012 - The demo apps are updated to report exceptions.
  • September 9, 2012 - The WPF version of the control plus demo application.
  • February 19, 2012 - Updated demo code and documentation.
  • February 15, 2012 - The initial version.  

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Alexander Iacobciuc
Software Developer RTSoft
Russian Federation Russian Federation
Niko Bellic (Serbian: Niko Belić) is the main protagonist and playable character in the video game Grand Theft Auto IV. He is a 30 year old former soldier who moved to Liberty City to escape his troubled past and pursue the American Dream.

Comments and Discussions

 
QuestionOnly one resolution Pin
Member 1134347621-May-15 16:40
memberMember 1134347621-May-15 16:40 
AnswerRe: Only one resolution Pin
Alexander Iacobciuc23-May-15 5:45
memberAlexander Iacobciuc23-May-15 5:45 
GeneralRe: Only one resolution Pin
Member 1134347625-May-15 9:06
memberMember 1134347625-May-15 9:06 
QuestionSomething about adding a mask on the video? Pin
Member 1153781214-Apr-15 6:27
memberMember 1153781214-Apr-15 6:27 
AnswerRe: Something about adding a mask on the video? Pin
Alexander Iacobciuc14-Apr-15 13:29
memberAlexander Iacobciuc14-Apr-15 13:29 
GeneralMy vote of 5 Pin
Franc Morales9-Apr-15 16:19
memberFranc Morales9-Apr-15 16:19 
QuestionI am impressed Pin
Member 1041007620-Mar-15 13:26
memberMember 1041007620-Mar-15 13:26 
AnswerRe: I am impressed Pin
Alexander Iacobciuc20-Mar-15 13:48
memberAlexander Iacobciuc20-Mar-15 13:48 
QuestionError if a setup is made with the WebCameraControl dll Pin
sherinmd13-Mar-15 0:45
membersherinmd13-Mar-15 0:45 
AnswerRe: Error if a setup is made with the WebCameraControl dll Pin
Alexander Iacobciuc13-Mar-15 1:54
memberAlexander Iacobciuc13-Mar-15 1:54 
GeneralRe: Error if a setup is made with the WebCameraControl dll Pin
sherinmd13-Mar-15 18:43
membersherinmd13-Mar-15 18:43 
GeneralRe: Error if a setup is made with the WebCameraControl dll Pin
Alexander Iacobciuc14-Mar-15 0:24
memberAlexander Iacobciuc14-Mar-15 0:24 
GeneralRe: Error if a setup is made with the WebCameraControl dll Pin
Joel Palmer17-Apr-15 6:40
memberJoel Palmer17-Apr-15 6:40 
QuestionSelfie mode? Pin
Xal114-Nov-14 3:37
memberXal114-Nov-14 3:37 
AnswerRe: Selfie mode? Pin
Alexander Iacobciuc14-Nov-14 4:49
memberAlexander Iacobciuc14-Nov-14 4:49 
GeneralRe: Selfie mode? Pin
Xal114-Nov-14 12:46
memberXal114-Nov-14 12:46 
AnswerRe: Selfie mode? Pin
Alexander Iacobciuc14-Nov-14 23:17
memberAlexander Iacobciuc14-Nov-14 23:17 
GeneralRe: Selfie mode? Pin
Xal115-Nov-14 3:52
memberXal115-Nov-14 3:52 
AnswerRe: Selfie mode? Pin
farhad20121-Feb-15 18:03
memberfarhad20121-Feb-15 18:03 
QuestionThank you! Pin
Member 1020796719-Aug-14 7:49
memberMember 1020796719-Aug-14 7:49 
GeneralThank you so much for this... Pin
Member 1023340818-Aug-14 10:07
memberMember 1023340818-Aug-14 10:07 
QuestionException when programmatically adding a WebCameraControl in wpf. Pin
Member 1033657331-Mar-14 0:39
memberMember 1033657331-Mar-14 0:39 
AnswerRe: Exception when programmatically adding a WebCameraControl in wpf. Pin
Alexander Iacobciuc31-Mar-14 12:39
memberAlexander Iacobciuc31-Mar-14 12:39 
GeneralRe: Exception when programmatically adding a WebCameraControl in wpf. Pin
Member 1033657331-Mar-14 21:55
memberMember 1033657331-Mar-14 21:55 
GeneralRe: Exception when programmatically adding a WebCameraControl in wpf. [modified] Pin
Alexander Iacobciuc31-Mar-14 23:59
memberAlexander Iacobciuc31-Mar-14 23:59 
GeneralRe: Exception when programmatically adding a WebCameraControl in wpf. Pin
Member 103365731-Apr-14 1:04
memberMember 103365731-Apr-14 1:04 
QuestionThank you! Take a look at a similar control with a bit more functionality - Camera_Net Library Pin
free5lot29-Dec-13 22:33
memberfree5lot29-Dec-13 22:33 
BugMemory issue Pin
pasquyonline11-Jul-13 6:00
memberpasquyonline11-Jul-13 6:00 
GeneralRe: Memory issue [modified] Pin
Alexander Iacobciuc11-Jul-13 8:32
memberAlexander Iacobciuc11-Jul-13 8:32 
GeneralRe: Memory issue Pin
pasquyonline11-Jul-13 22:09
memberpasquyonline11-Jul-13 22:09 
GeneralRe: Memory issue Pin
Alexander Iacobciuc12-Jul-13 8:27
memberAlexander Iacobciuc12-Jul-13 8:27 
QuestionAWESOME! Pin
Gichumz8-Jul-13 5:56
memberGichumz8-Jul-13 5:56 
AnswerRe: AWESOME! Pin
Alexander Iacobciuc9-Jul-13 1:44
memberAlexander Iacobciuc9-Jul-13 1:44 
GeneralRe: AWESOME! Pin
Gichumz15-Jul-13 3:49
memberGichumz15-Jul-13 3:49 
GeneralRe: AWESOME! Pin
Alexander Iacobciuc15-Jul-13 4:34
memberAlexander Iacobciuc15-Jul-13 4:34 
GeneralRe: AWESOME! Pin
mikesvile18-Aug-13 6:37
membermikesvile18-Aug-13 6:37 
GeneralMy vote of 5 Pin
Meghna_Patel14-Jun-13 7:19
memberMeghna_Patel14-Jun-13 7:19 
QuestionASP.Net C# Tablet Web Camera Control Pin
Member 32242962-May-13 16:23
memberMember 32242962-May-13 16:23 
QuestionI need it support with Win64Bit Pin
tithsochinda27-Mar-13 18:43
membertithsochinda27-Mar-13 18:43 
QuestionSvideo input Pin
ruddygonzalez18-Mar-13 11:18
memberruddygonzalez18-Mar-13 11:18 
AnswerRe: Svideo input [modified] Pin
Alexander Iacobciuc18-Mar-13 23:41
memberAlexander Iacobciuc18-Mar-13 23:41 
GeneralMy vote of 5 Pin
Paga Cession14-Mar-13 9:00
memberPaga Cession14-Mar-13 9:00 
QuestionCodec Pin
Coffeephile2-Jan-13 15:33
memberCoffeephile2-Jan-13 15:33 
AnswerRe: Codec Pin
Alexander Iacobciuc13-Feb-13 23:27
memberAlexander Iacobciuc13-Feb-13 23:27 
GeneralMy vote of 5 Pin
juergen196929-Oct-12 23:18
memberjuergen196929-Oct-12 23:18 
GeneralRe: My vote of 5 Pin
Alexander Iacobciuc30-Oct-12 9:19
memberAlexander Iacobciuc30-Oct-12 9:19 
GeneralMy vote of 5 Pin
gaga blues24-Oct-12 3:43
membergaga blues24-Oct-12 3:43 
BugException Pin
AbhishekBihani15-Oct-12 21:19
memberAbhishekBihani15-Oct-12 21:19 
GeneralRe: Exception Pin
Alexander Iacobciuc15-Oct-12 21:31
memberAlexander Iacobciuc15-Oct-12 21:31 
GeneralRe: Exception Pin
AbhishekBihani16-Oct-12 1:11
memberAbhishekBihani16-Oct-12 1:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150520.1 | Last Updated 9 Apr 2015
Article Copyright 2012 by Alexander Iacobciuc
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid