|Yet another capture sample in C#|
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:
The only new object we haven't seen before is given in the following declaration:
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:
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.
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.
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:
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.
|< Prev||Next >|