Yet another capture sample in C# PDF Print E-mail

I haven't covered yet the topic of audio or video capture in DirectShow using C#. There are two reasons for this omission, first it's well illustrated in a few other places (for example, the original DirectShowLib from .Net Master at codeproject shows capture tasks). Second, the only hardware, I own to test this code, is a cheap digital camera that can serve as a web cam. So I have rewritten the PlayCap sample from the SDK in C#. (The open-source DirectShowLib has also a version of this sample in C#). Once again, the code can be found in a zip file.

I have created a custom library for this sample. The code (and the .cmd file) for this .idl file can be found in the .zip file of this tutorial. The library generated is named "CaptureTypeLib"; so we start our code with this statement:

  2. using CaptureTypeLib;

The only new object we haven't seen before is given in the following declaration:

  1. ICaptureGraphBuilder2 cg = null;

This declaration is followed by a bunch of GUID declarations taken from different DirectShow header files (we'll skip the details). We are now going to look at the function CaptureVideo:

  1. IBaseFilter sf = null; // source filter (the capture device)
  2. try
  3. {
  4. // release com object (useless here but can't hurt)
  5. Cleanup();
  6. // create the filter graph object (as a IGraphBuilder)
  7. Type t = Type.GetTypeFromCLSID( FgGuid );
  8. gb = (IGraphBuilder)Activator.CreateInstance( t );
  9. // create the cg object and add the filter graph to it
  10. t = Type.GetTypeFromCLSID( CgGuid );
  11. cg = (ICaptureGraphBuilder2)Activator.CreateInstance( t );
  12. // tell the graph builder about the filter graph
  13. cg.SetFiltergraph( gb );
  14. }

We declare a local variable, which the function returns in an "out" parameter, of type IBaseFilter that will represent our capture device. Then we create a filter graph and a capture graph object and tells the capture graph about the filter graph to use with a call to ICaptureGraphBuilder2.SetFiltergraph. We are now ready to search for a capture device in the system.

  1. // look for a capture device
  2. if( FindDevice( out sf ) )
  3. {
  4. gb.AddFilter( sf , "Video Capture" );
  5. }
  6. else
  7. {
  8. MessageBox.Show( "Can't find capture device" );
  9. Cleanup();
  10. return false;
  11. }

   If we can find a capture device we add it to the filter graph. Otherwise, we back    out after noticing the user. If we have found a capture device, we call    RenderStream of the ICaptureGraphBuilder2 interface.  

  1. // build the rest of the graph
  2. cg.RenderStream( ref PIN_CATEGORY_PREVIEW, ref MEDIATYPE_Video, sf , null, null );

This RenderStream function is the main method for DirectShow capture applications. The first argument is the "pin category", here "preview"; the other "pin category" commonly used is "capture" (to save the stream to a file, for example) The DirectShow runtime treats the streams differently, according to their "pin category". On a "preview" pin, it might drop frames without consequences; while on a "capture" pin missing frames might corrupt the final file output. The second argument is a GUID for the major type, here, video. The third argument is the source filter, returned from our FindDevice function. The fourth and fifth arguments are null and represent an intermediate and a sink filter for our capture graph. Since we just have a preview window for the incoming video stream, we can have these two arguments set to null. The rest of our CaptureVideo is similar to what we have done before. So let's have a look at the FindDevice function:

  1. ICreateDevEnum de = null;
  2. srcFilter = null;
  3. try
  4. {
  5. // create the device enumerator (COM) object
  6. Type t = Type.GetTypeFromCLSID( DevEnumGuid );
  7. de = (ICreateDevEnum)Activator.CreateInstance( t );
  8. IEnumMoniker em;
  9. // get an enumerator for the video caputre devices
  10. de.CreateClassEnumerator( ref VidCapGuid, out em, 0 );
  11. IMoniker mon;
  12. uint result;
  13. // get a reference to the 1st device
  14. em.RemoteNext( 1, out mon, out result );
  15. object o;
  16. // get our device ready
  17. mon.RemoteBindToObject( null, null, ref IID_IBaseFilter, out o );
  18. srcFilter = (IBaseFilter)o;
  19. }

ICreateDevEnum is one of the interfaces we have listed in the .idl file for our capture type library. This interfaces has only one method CreateClassEnumerator. So after creating a (COM) object for the device enumerator, we access its ICreateDevEnum interface and call its only method. The first argument to CreateClassEnumerator is the guid for the class category, here the video capture devices. The second argument is an IEnumMoniker reference. This interface was brought in by the MIDL compiler for our type library. The last argument is a flag, here zero, to request all the devices in the given category. Then using, the IEnumMoniker returned by this last call, we call RemoteNext to have a IMoniker to bind to our source filter that we are looking for.

The methods RemoteNext and RemoteBindToObject came as a surprise when I looked at the assembly created from our custom type library. After looking at the .idl files for these interfaces, I checked that their "local" versions were marked as [local] and the remote version as [call_as]. In any case, calling these methods worked as expected (except that RemoteNext second argument is an IMoniker instead of an array, but requesting a single moniker (using 1 for the first argument) returned an IMoniker that can be called to bind to the capture device). Afterword, I looked at the open-source DirectShowLib version of this sample, to check how they have done it and I noticed that they used the types provided in the Marshal class to perform this task. Their solution is probably better but I like to see the "Remote attribute" in front of these functions; it's like a "curve ahead" sign on the road, you'd better pay attention otherwise, you might be in trouble.

This is probably the simplest sample that makes use of a capture device in DirectShow. Its 250 lines covers the basic operations for a capture application, and it illustrates again that a lot of DirectShow applications can be written in C# relatively easily.

Bookmark it...
< Prev   Next >
Joomla Template by Joomlashack
Joomla Templates by JoomlaShack Joomla Templates by Compass Design