DirectShow and C#
I'm currently in the "ramping up" phase of my job, and last week I spent a fair amount of time playing around with DirectShow from C#.
DirectShow is an interesting technology that provides a sort of "building block" approach to multimedia. You create a DirectShow graph, populate it with different filters, hook them together, and start the whole process in motion. DirectShow handles the mechanics of getting the different parts talking to each other so data can flow through the system.
If, for example, you wanted to capture some audio and save it to the disk in WMA format. You'd start with an audio input filter, which has an output pin. You'd wire that output pin to the input pin of a WMA encoder, and then wire the output pin of the encoder to the input pin of the FileStream filter, and that would give you the basic graph. Select which input you want, tell the graph to play, and your app is up and savin' data.
Want to add a little reverb? Hook in the reverb filter.
Video is similar - you hook things up together, and DShow handles passing things around. There's a cool workbench app named GraphEdit (graphedt.exe) that lets you graphically play around, and is a great way to find out what works before you write code.
All of this is explained quite well in "Programming Microsoft DirectShow for Digital Video and Television"
There are some managed DirectX wrappers in the DX9 SDK, but there aren't any available for DirectShow. I think I know why, but I'll save that for later...
I set out to try to use C# from DirectShow, to emulate some of the C++ samples. The first problem is defining the COM interfaces. I've spent some time trying to define interfaces by hand in the past, but that hasn't been very successful. I searched for a typelib, but didn't find one.
I did, however, find something that is a little better - the .IDL files. IDL files provide the definition of COM interfaces, and in this case are used to generate the C++ header files. They can also be used as input to MIDL, which produces a type library, which can be consumed by tlbimp to produce managed wrappers, which allows us all to go home early.
They can if they've been set up to do that. But this IDL wasn't authored to produce a typelib, so it doesn't generate one by default. To do that, you need a library statement, which tells MIDL what to put in the typelib. Here's what I put in an IDL file:
import "devenum.idl";
import "axcore.idl";
import "axextend.idl";
[
uuid(22995cc9-e37e-4b96-9326-b418935ac4be),
helpstring("DirectShow interfaces")
]
library DirectShow
{
interface IFilterGraph;
interface ICreateDevEnum;
interface IGraphBuilder;
interface ICaptureGraphBuilder2;
interface IFileSinkFilter;
interface IFileSinkFilter2;
interface IAMAudioInputMixer;
};
You have to have a GUID there for it to work.
When run through MIDL, this gave me the typelib, which gave me a wrapper with some of the types I needed. You'll also need to add quartz.dll from windows\system32, which does have type information, so that you can get some of the other types.
Once you've got that set up, you can start writing code. The C++ COM code in the samples is like most C++ COM code - ugly and hard to understand. But it's not that bad in C#.
To create a COM object, you need to write:
Type t = Type.GetTypeFromCLSID(guid);
IGraphBuilder graphBuilder = (IGraphBuilder) Activator.CreateInstance(t);
The guid is a Guid instance, and you get the Guid to use by looking in the include files or copying it from GraphEdit. That's a bit ugly, so I wrote this little helper function:
private T CreateComObject<T>(Guid guid) where T: class|
{
Type comType = Type.GetTypeFromCLSID(guid);
object o = Activator.CreateInstance(comType);
if (o == null)
return null;
else
return (T) o;
}
Which then allows me just to write:
IGraphBuilder graphBuilder =
CreateComObject<IGraphBuilder>(CLSID_FilterGraph);
That's a nice little use of a generic method.
Sometimes, you want an interface off an object rather than a new object. You can do that through a simple cast.
Up to this point, things are fairly easy, and it makes you wonder why there's no managed interface to this stuff. Some of the real power comes when you want to write a filter, which could do something convert a picture to grayscale. While one could write a filter in C#, one would have to be pretty daft to try it, given that there are lots of helper classes in C++ that you'd have to rewrite in C++ (ok, perhaps MC++ could help...). My guess is that that's why there are no managed wrappers.
Comments
Anonymous
September 20, 2004
Given all the media foundation stuff and the fact that MovieMaker ships in the product, will MovieMaker be rewritten to use media foundation in Longhorn?Anonymous
September 20, 2004
Eric,
just questioning that particular use of generics.
What is the difference between your generic CreateComObject method, and say:
private Object CreateComObject(Guid guid)
{
Type comType = Type.GetTypeFromCLSID(guid);
object o = Activator.CreateInstance(comType);
if (o == null)
return null;
return o;
}
IGraphBuilder graphBuilder = (IGraphBuilder)CreateComObject(CLSID_FilterGraph);
It just seems like using generics because they're there, rather than gaining some benefit out of them. Or am I missing the point?Anonymous
September 20, 2004
or for the sake of brevity
private Object CreateComObject(Guid guid)
{
return Activator.CreateInstance(Type.GetTypeFromCLSID(guid));
}Anonymous
September 20, 2004
Eric, are you aware of DirectShow.NET? (http://www.codeproject.com/cs/media/directshownet.asp) Or maybe you would just not be allowed to use it in your work.
If there's a real managed replacement for DirectShow coming, I can't wait to hear about it!Anonymous
September 20, 2004
The comment has been removedAnonymous
September 20, 2004
Matthew W. Jackson:
Part of your question is answered in this article: http://msdn.microsoft.com/library/?url=/library/en-us/dndotnet/html/win32map.asp.
I hope that it will get updated for the changes in .NET 2.0.Anonymous
September 20, 2004
Eric, you have to do something more and get real C# definitions of interfaces.
After you run IDL -> TLB -> Assembly conversion, you get some 'dirty' DLL. But with Lutz Roeder .NET Reflector you can get C# from this and just paste code to your project.
That dirty DLL that produces IDL/TLB/Assembly conversion often have too many unneed types, some parameter types must be redefined or MarshalAs attributes replaced. If you convert all that to C# source code, you can clean all interfaces and get stable soft.
To get interface definition in last version of Reflector open assembly, find the interface and press Space. I like this :-)Anonymous
September 20, 2004
Andreas Haber:
Thanks. That's actually a lot more detailed than I was looking for, but there's certainly no problem with that.Anonymous
September 23, 2004
Here's the file that I used to create the .TLB using MIDL. You should create your own UUID with UUIDGEN instead of using mine.
import "devenum.idl";
import "axcore.idl";
import "axextend.idl";
[
uuid(22995cc9-e37e-4b96-9326-b418935ac4be),
helpstring("DirectShow interfaces")
]
library DirectShow
{
interface IFilterGraph;
interface ICreateDevEnum;
interface IGraphBuilder;
interface ICaptureGraphBuilder2;
interface IFileSinkFilter;
interface IFileSinkFilter2;
interface IAMAudioInputMixer;
};Anonymous
September 27, 2004
If you're playing around with DirectShow as part of your ramp up for Movie Maker development have you tried out the use of Direct3D pixel shaders for video effects?
If you say wanted to convert video frames to grayscale or add a swirl effect you can either write a DirectShow filter which runs on the CPU or using the VMR-9 (Video Mixing Renderer) you can use the GPU with pixel shaders to get the same effect at the end of the pipeline.Anonymous
September 30, 2004
From what I've read about Managed DirectX 9 and from digging into the assemblies with ildasm.exe (to keep sharp with my IL - .NET Reflector is nice when I'm feeling lazy), MDX9 is not just a wrapper like DirectShow.NET (very nice, I heard, BTW). It's written from the ground-up and uses the native drivers in managed code. That definitely improves performance since marshaling is rare with MDX9.Anonymous
October 10, 2004
I've taken a slightly different approach... instead of trying to bring all the directshow API's into a managed langauge, I just create higher level wrappers in Managed C++, do all the actual directshow stuff inside in unmanaged code, and just call the interfaces on my wrapper.
In most cases, you really don't need access to every single directshow interface in the application.
I just expose the functionality i need rather than trying to emulate the entire interface.
You can look at my code here...
http://svn.xiph.org/trunk/oggdsf/src/lib/player/libDSPlayDotNET/DSPlay.cpp
and a very simple .NET player which uses it.
http://svn.xiph.org/trunk/oggdsf/src/tools/DNPlay/frmDNPlay.cs
Some of the stuff is specific to a particular media type i'm working on (annodex)Anonymous
September 22, 2007
Several years ago, I wrote an article for MSDN on programming against the DVR-MS file format. I'm veryAnonymous
September 22, 2007
Several years ago, I wrote an article for MSDN on programming against the DVR-MS file format. I'mAnonymous
April 01, 2008
PingBack from http://copyrightrenewalsblog.info/eric-gunnersons-c-compendium-directshow-and-c/Anonymous
May 31, 2009
PingBack from http://woodtvstand.info/story.php?id=9955