Click here to Skip to main content
15,899,474 members
Articles / Programming Languages / C#
Article

DirectX.Capture Class Library

Rate me:
Please Sign up or sign in to vote.
4.94/5 (227 votes)
29 Mar 2008Public Domain5 min read 4.1M   72K   598   1.5K
A .NET class library for capturing video and audio to AVI files.
Sample Image - DirectXCapture.jpg

Introduction

This article presents a class library for capturing audio and video to AVI files in .NET. Some of the features of this library:

  • List and select hardware devices
  • Access to common audio and video settings (e.g. frame rate, size)
  • Support audio and video compression codecs
  • Support video preview
  • Support TV tuners
  • Support crossbars and audio mixers
  • Retrieve device capabilities
  • Show property pages exposed by drivers
  • MSDN-style documentation included

Using the Code

The Capture class is the core of this library. Here is a simple example:

C#
// Remember to add a reference to DirectX.Capture.dll
using DirectX.Capture

// Capture using the first video
// and audio devices available
Capture capture = new Capture( Filters.VideoInputDevices[0],
                               Filters.AudioInputDevices[0] );

// Start capturing
capture.Start();

// Stop capturing
capture.Stop();

Remember to add a reference in your project to DirectX.Capture.dll. This DLL requires DShowNET.dll, so make sure they are both in the same directory. Once you add the reference, Visual Studio .NET should take care of the copying for you.

This example will capture video and audio using the first video and audio devices installed on the system. To capture video only, pass a null as the second parameter to the constructor.

The class is initialized to a valid temporary file in the Windows temp folder. To capture to a different file, set the Capture.Filename property before you begin capturing.

A Second Example

This next example shows how to change video and audio settings. Properties such as Capture.FrameRate and Capture.AudioSampleSize allow you to programmatically adjust the capture. Use Capture.VideoCaps and Capture.AudioCaps to determine valid values for these properties.

C#
Capture capture = new Capture( Filters.VideoInputDevices[0],
                               Filters.AudioInputDevices[1] );

capture.VideoCompressor = Filters.VideoCompressors[0];
capture.AudioCompressor = Filters.AudioCompressors[0];

capture.FrameRate = 29.997;                 // NTSC
capture.FrameSize = new Size( 640, 480 );   // 640x480
capture.AudioSamplingRate = 44100;          // 44.1 kHz
capture.AudioSampleSize = 16;               // 16-bit
capture.AudioChannels = 1;                  // Mono

capture.Filename = "C:\MyVideo.avi";

capture.Start();
...
capture.Stop();

The example above also shows the use of video and audio compressors. In most cases, you will want to use compressors. Uncompressed video can easily consume over 1GB of disk space per minute. Whenever possible, set the Capture.VideoCompressor and Capture.AudioCompressor properties as early as possible. Changing them requires the internal filter graph to be rebuilt which often causes most of the other properties to be reset to default values.

Behind the Scenes

This project uses 100% DirectShow to capture video. Once a capture is started, DirectShow spawns another thread and handles retrieving/moving all the video and audio data itself. That means you should be able to capture at the same speed and quality as an application written in C.

DirectShow is implemented as a set of COM components and we use .NET Interop to access them. The pioneering work on this was done by NETMaster with the DShowNET project. This Capture library uses DShowNET for the interop layer with only a few extensions. This is the DShowNET.dll mentioned earlier.

Sitting on top of all of this is the Capture class library. The center of any DirectShow app is the filter graph and the filter graph manager. For a good overview, see The Filter Graph and Its Components from the MSDN.

The Least Work Possible

The library tries at all times to do the least amount of work possible. The problem is: DirectShow is very flexible, but has few firm standards for driver developers and I have limited hardware to test with. As a result, the class tries to avoid doing any work that may not be necessary, hopefully avoiding potential incompatibilities in the process.

One example is video preview. You can start and stop preview with:

C#
// Start preview
capture.PreviewWindow = myPanelControl;

// Stop preview
capture.PreviewWindow = null;

Hopefully this is simple to use. Internally, DirectShow does a lot of work: add required upstream filters for WDM devices, search for preview pins, use the Overlay Manager for video ports (hardware overlays), insert SmartTee filters when a separate preview pin is not available and more. Instead of rendering the preview stream as soon as the class is created, the class waits until the PreviewWindow property is set.

For developers who don't need preview, none of this work will ever be done. That means your application is more likely to work on a wider range of hardware. For developers that do need preview, this makes it easier to locate the cause of the problem and fix it or handle it gracefully.

Performance Tips

Many of the properties on the Capture class are retrieved directly from the underlying DirectShow COM components. If you need to refer to the property repeatedly in a block of code, take a copy of the value and use your copy.

C#
// AudioSampleSize is retrieved from DirectShow each iteration
for ( int c = 0; c < 32; c++ )
{
    if ( c == capture.AudioSampleSize )
        MessageBox.Show( "Found!" );
}

// A faster solution
int x = capture.AudioSampleSize;
for ( int c = 0; c < 32; c++ )
{
    if ( c == x )
        MessageBox.Show( "Found!" );
}

Why doesn't the class simply cache the value internally? We don't know when the filter (device driver) will change this value, so we have to retrieve the value every time. This means you will always get the real value of the property.

Credits

The DirectShow interop layer was developed by NETMaster in the DShowNET project. The MDSN-style documentation was generated from the source code using nDoc.

Troubleshooting

I have tested this with an Asus v7700 (NVidia GeForce2, reference drivers) and my onboard sound card. I can't guarantee any other hardware will work. However, I expect most video capture cards and sound cards will work. You may have trouble with TV Tuner cards and DV devices (Firewire camcorders) though they should be solvable.

Try the AMCap sample from the DirectX SDK (DX9\Samples\C++\DirectShow\Bin\AMCap.exe) or Virtual VCR, a free DirectShow capture application.

This class library uses COM Interop to access the full capabilities of DirectShow, so if there is another application that can successfully use a hardware device then it should be possible to modify this class library to use the device. Please post your experiences, good or bad, in the forum below.

User Enhancements

The following enhancements have been posted to the discussion board:

Thanks to fdaupias and dauboro for their submissions. I have not had time to post a tested, updated version with these enhancements. If anyone wants to make an updated download zip, mail it to me and I will added it to this page. Keep the enhancements coming.

DirectX.Capture Wiki

A Wiki for this project is available here. This Wiki can be edited by anyone, no registration is required. I hope this Wiki will allow interested users to more easily collaborate on this project. New versions, enhancements, tips and tricks can be posted on the Wiki.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Web Developer
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
AnswerRe: no audio preview Pin
almere10916-Mar-06 7:26
almere10916-Mar-06 7:26 
GeneralRe: no audio preview Pin
werner2717-Mar-06 3:30
werner2717-Mar-06 3:30 
GeneralResolution of DX with a VideoGrabber Pin
Nasenbaaer15-Mar-06 10:45
Nasenbaaer15-Mar-06 10:45 
GeneralRe: Resolution of DX with a VideoGrabber Pin
almere10915-Mar-06 11:21
almere10915-Mar-06 11:21 
GeneralAsf file writer, profile question Pin
almere10913-Mar-06 4:00
almere10913-Mar-06 4:00 
Generalgreat library Pin
da vinci coder11-Mar-06 16:35
da vinci coder11-Mar-06 16:35 
GeneralRe: great library Pin
almere10911-Mar-06 21:41
almere10911-Mar-06 21:41 
GeneralRe: great library Pin
da vinci coder12-Mar-06 4:16
da vinci coder12-Mar-06 4:16 
not the all code.. i just needed when i click a button on a form..
a new form will open and there will be a Panel in it which
covers all form. And then capture the image in the boundry of the form and
add it to a Bitmap file..

so i use the class i mentioned before.. i use another class for capturing
the frame in the form which looks like this..
<br />
	[DllImport("gdi32.dll")] <br />
		private static extern bool BitBlt( <br />
			IntPtr hdcDest, // handle to destination DC <br />
			int nXDest, // x-coord of destination upper-left corner <br />
			int nYDest, // y-coord of destination upper-left corner <br />
			int nWidth, // width of destination rectangle <br />
			int nHeight, // height of destination rectangle <br />
			IntPtr hdcSrc, // handle to source DC <br />
			int nXSrc, // x-coordinate of source upper-left corner <br />
			int nYSrc, // y-coordinate of source upper-left corner <br />
			System.Int32 dwRop // raster operation code <br />
			); <br />
<br />
<br />
		private const Int32 SRCCOPY = 0xCC0020; <br />
<br />
<br />
		private Bitmap memImage; <br />
<br />
		<br />
		public void PrepareImage(System.Windows.Forms.Form form) <br />
		{ <br />
			Graphics graphic = form.CreateGraphics(); <br />
			Size s = form.Size; <br />
			memImage = new Bitmap(s.Width-20, s.Height-28, graphic); <br />
			Graphics memGraphic = Graphics.FromImage(memImage); <br />
			IntPtr dc1 = graphic.GetHdc(); <br />
			IntPtr dc2 = memGraphic.GetHdc(); <br />
			BitBlt(dc2, 0, 0, form.ClientRectangle.Width, <br />
				form.ClientRectangle.Height,dc1, 0, 0, SRCCOPY); <br />
			graphic.ReleaseHdc(dc1); <br />
			memGraphic.ReleaseHdc(dc2);<br />
			memImage.Save("ersan.bmp");<br />
<br />
<br />
<br />
		} <br />


then i simply call these classes from the form i want to use..
<br />
		private void WebCam_Load(object sender, System.EventArgs e)<br />
		{<br />
			Capturez.myCap imyCap = new myCap();<br />
			imyCap.prev(panel1);<br />
			<br />
		}<br />
<br />
		private void WebCam_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)<br />
		{<br />
			<br />
			if(e.KeyChar == (char) Keys.Enter)<br />
			{<br />
				Capturez.panelPhoto ipanelPhoto = new panelPhoto();<br />
				ipanelPhoto.PrepareImage(this);<br />
				System.IO.File.Copy("ersan.bmp",ConfigurationSettings.AppSettings[3].ToString()+ phonebook.Forms.New_User.NewUser.picName+".bmp",true);<br />
				System.IO.File.Delete("ersan.bmp");<br />
				phonebook.Forms.New_User.NewUser.imgWeb =Image.FromFile(ConfigurationSettings.AppSettings[3]+phonebook.Forms.New_User.NewUser.picName+".bmp");<br />
				Forms.New_User.NewUser.bShowImgWeb = true;<br />
				this.Close();<br />
<br />
			}<br />
		}<br />
<br />


it works.. i open form.. pressing enter, capturing the bitmap and it's saved
to the location.. as u can see after i capture the form closes..

the problem is when i reclick the button .. it gaves the error i mentioned..

its possible i might be doing something wrong, just added the comment
coz it told me to submit bug report Smile | :)

thanks for the library again !

good coding

-- modified at 10:21 Sunday 12th March, 2006
GeneralRe: great library Pin
almere10912-Mar-06 21:30
almere10912-Mar-06 21:30 
GeneralRe: great library Pin
almere10912-Mar-06 22:08
almere10912-Mar-06 22:08 
GeneralRe: great library Pin
mmxbass12-Mar-06 10:38
mmxbass12-Mar-06 10:38 
GeneralRe: great library Pin
da vinci coder13-Mar-06 3:50
da vinci coder13-Mar-06 3:50 
GeneralAudio compressing not working Pin
sstil_lg2-Mar-06 19:21
sstil_lg2-Mar-06 19:21 
GeneralRe: Audio compressing not working Pin
almere1092-Mar-06 21:24
almere1092-Mar-06 21:24 
GeneralRe:Audio compressing not working Pin
sstil_lg2-Mar-06 23:34
sstil_lg2-Mar-06 23:34 
GeneralRe:Audio compressing not working Pin
almere1093-Mar-06 0:02
almere1093-Mar-06 0:02 
GeneralRe:Audio compressing not working Pin
sstil_lg6-Mar-06 19:23
sstil_lg6-Mar-06 19:23 
GeneralRe:Audio compressing not working Pin
almere1097-Mar-06 1:16
almere1097-Mar-06 1:16 
GeneralRe:Audio compressing not working Pin
sstil_lg7-Mar-06 1:59
sstil_lg7-Mar-06 1:59 
GeneralRe:Audio compressing not working Pin
almere1097-Mar-06 5:20
almere1097-Mar-06 5:20 
GeneralRe:Audio compressing not working Pin
almere1097-Mar-06 8:22
almere1097-Mar-06 8:22 
GeneralRe:Audio compressing not working Pin
sstil_lg8-Mar-06 0:49
sstil_lg8-Mar-06 0:49 
GeneralRe:Audio compressing not working Pin
almere1098-Mar-06 1:27
almere1098-Mar-06 1:27 
GeneralRe:Audio compressing not working Pin
almere1098-Mar-06 11:39
almere1098-Mar-06 11:39 
GeneralRe:Audio compressing not working Pin
almere1098-Mar-06 21:36
almere1098-Mar-06 21:36 

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

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