This tutorial is an implementation of a Dowitchers filter that displays the input video stream in red, green or blue. When I wrote tutorials for programming DirectShow in C# a couple a years ago, I entitled "The final frontier" the tutorial that showed how to perform a similar task in DirectShow using the ISampleGrabber interface. I felt at the time that it was as far as we could go with DirectShow through interop and the inability to write filters. The intuition turned out to be pretty accurate.

Now, with Dowitchers, we can write filters in managed code. And this tutorial starts where the previous technology left us. So we are now "In the beginning".

We'll start with a few "using" directives.


using System;
using System.IO;
using System.Collections.Generic;

// Dowitchers plumbing and classes for filter
using Dowitchers;
using Dowitchers.Baseclasses;
Now we are ready to start writing our filter. With Dowitchers, a filter is derived from the class Dowitchers.Baseclasses.Filter, and in our case, we have three fields for the input and output pin and the mediatype.

public class DoEzRgbFilter : Filter
{
  private InputPin  _inputPin;
  private IOutputPin _outputPin;
  private IMediaType _videoType; 
Then we can write our constructor. First, we call the parent constructor with the name that we want to give to the filter. Then we create an InputPin object and hook the CheckMediaTypeEvent that decides which mediatype we accept for the filter. Finally, we declare an object of type "DoEzRgbOutputPin" for our output pin since we have to override the OutputMediaTypes property of the abstract class OutputPin. So we'll have to write the code the class "DoEzRgbOutputPin" later.

public DoEzRgbFilter()
  : base("DoEzRgb")
{
  _inputPin = new InputPin("Input", this);
  _inputPin.CheckMediaTypeEvent += new CheckMediaTypeEventHandler(_checkMediaHandler);
  
  _outputPin = new DoEzRgbOutputPin("Output", this);  
}  
Now, we are going to write the "_checkMediaHandler" method. In order to keep the filter simple, we'll just accept the MediaType.RGB32 type for input. So we can write "_checkMediaHandler" as follows:

bool _checkMediaHandler(IInputPin p, IMediaType mt)
{
  IVideoType vt = mt as IVideoType;
  if (vt == null || vt.Id != MediaType.RGB32)
    return false;
  else 
  {
    // return true only for rbg32 and store the video type away
    _videoType = mt;
    return true;
  }
}
We might as well write the code for the class "DoEzRgbOutputPin" now:

public class DoEzRgbOutputPin : OutputPin
{
  public DoEzRgbOutputPin(string name, IFilter owner)
    : base(name, owner)
  {
  }

  public override IEnumerable OutputMediaTypes
  {
    get
    {
      DoEzRgbFilter f = Owner as DoEzRgbFilter;                    
      // return the video type of the input pin if it's connected
      if (f != null && f._inputPin.Connected != null)
        yield return f._videoType;
      else
        yield return null;
    }
  }
}
First, we derive it from the class OutputPin and the constructor just calls the parent constructor. Then we override the OutputMediaTypes property of the parent class. The code uses the .Net 2.0 ability to turn an object into an enumerator by using the yield keyword. If the input pin is connected we return the "_videoType" object that was stored while doing checking the media type on the input pin.

The filter actual processing is done in the Receive method:


unsafe public override void Receive(ISampleStream sender, ISample sample)
{
  base.Receive(sender, sample);

  IMediaSample s = sample as IMediaSample;
  if (s != null)
  {
    // rgb32 contains b g r a bytes
    // just overwrite the b and g bytes
    int length = s.Data.Length;
    fixed (byte* bptr = s.Data)
    {
      for (int i = 0; i < length; i += 4)
        // if you want to keep only the green component
        // replace the 2nd index by i+2
        // if you want to keep only the blue component
        // replace the 1st index by i+2
        bptr[i] = bptr[i+1] = (byte)0;
    }
  }
  // pass the sample along
  _outputPin.Receive(this, sample);

}
We use a pointer to the bytes of the frame, so we need to declare the method unsafe. Then we call the parent method (which does nothing in this case). After that, we cast the "sample" argument to an IMediaSample, if the cast is not valid, we simply pass the sample along to the output pin (this is the case when we seek to a new location and the first sample is a NewSegmentSample). If the cast is valid, we grab the sample length and we overwrite the first two bytes of every word (i.e. the blue and green components). We do this inside a fixed statement to ensure that the GC thread will not try to move the sample around while we are looping. And we use a pointer to byte to avoid any managed overhead. The ability to choose the color component to show should be left to a custom interface. The custom interface and the code for adding the filter to a library will be left for another tutorial. To complete the filter, we need to implement the Pins property:

public override IEnumerable Pins
{
  get
  {
    yield return _inputPin;  
    yield return _outputPin;  
  }
}

} // closing brace for the class
We just return the input and output pins as an enumerator.