100% C# directshow filters
Quick sample on how to write filters in C# without COM registration.
Interested in doing reliable C# filters for real using a solid framework? Please check out the MediaSDK (Note: This tutorial does not use the MediaSDK.)
Here's the thing. After converting the original Directshow BaseClasses from C++ to C#, you quickly find that 90% of the code is verbose code acheiving very simple logic, which when converted to C# turns 50 lines into about 10.
The Directshow base classes are not that complicated, and unless you really plan on deploying an activex filter to be used by many disparate applications, there is no reason for you to have to register your filter with COM. Just add your filter to the graph using a pointer to your managed C# class which exposes the required interfaces.
The sample code in this download has some base classes to get you started -- you can effectively write any source, sink or tranform filter in about 5 minutes.
Here's a sample I've done -- a bit of a wink and nudge to the talented Jeremiah Morrill's media element hack:
[ComVisible(true)]
[Guid("00000028-5733-4c70-9092-13057E2BAF00")]
public class MediaElementProxy : BaseFilter, IFileSourceFilter
{
public delegate void CreateGraphDelegate(IGraphBuilder graph, string actualFile);
/// <summary>
/// Populate this before setting MediaElement.Source
/// uri/Play(). This will create your graph.
/// </summary>
public static CreateGraphDelegate OnRender;
static readonly string _ProtocolName = "SichboPVR";
string _FileName;
public MediaElementProxy()
{
// No pins needed, OnRender will create a real source filter
// this.Pins.Add(new FakePin(this));
}
protected override HRESULT OnJoinFilterGraph()
{
Debug.WriteLine("MediaElementProxy joined graph");
return base.OnJoinFilterGraph();
}
#region IFileSourceFilter Members
public HRESULT Load(string pszFileName, IntPtr pmt)
{
_FileName = pszFileName;
_FileName = _FileName.Substring((_ProtocolName + "://Foo/").Length);
_FileName = System.Web.HttpUtility.UrlDecode(_FileName);
if (OnRender != null)
OnRender((IGraphBuilder)_Graph, _FileName);
return HRESULT.S_OK;
}
public HRESULT GetCurFile(out string pszFileName, IntPtr pmt)
{
pszFileName = _FileName;
return HRESULT.S_OK;
}
#endregion
public static void Register()
{
RegisterForProtocol(typeof(MediaElementProxy), _ProtocolName, _ProtocolName + " MediaElementProxy");
}
public static void UnRegister()
{
UnRegisterForProtocol(typeof(MediaElementProxy), _ProtocolName, true);
}
}
Easy eh? obviously all of the work is done by the BaseFilter class.
Defining a pin is easy too, the MediaElement hack doesnt need any pins, but say if you want to write source filter, just go;
class YourFilter : BaseFilter
{
public YourFilter()
{
// No pins needed, OnRender will create a real source filter
this.Pins.Add(new YourOutputPin(this));
}
class YourOutputPin : BasePin
{
YourFilter _Owner;
public YourOutputPin(YourFilter filter)
: base(PinDirection.Output, filter)
{
_Owner = filter; // keep a reference to the main filter... sure, why not..
_Name = "My Output";
}
...
}
}