Click here to Skip to main content
15,886,830 members
Articles / Multimedia / DirectX

Using third-party filters in a video application in C#

Rate me:
Please Sign up or sign in to vote.
4.14/5 (3 votes)
4 Jan 20065 min read 143.8K   2.1K   57  
A simple application to test DirectShow filters.
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using DirectShowLib;

namespace CsVidSeg
{
  /// <summary>
  /// Summary description for Form1.
  /// </summary>
  public class Form1 : System.Windows.Forms.Form
  {
    private System.Windows.Forms.GroupBox groupBox1;
    private System.Windows.Forms.TrackBar trackBar1;
    private System.Windows.Forms.Panel panel1;
    private System.Windows.Forms.Button buttonPlay;
    private System.Windows.Forms.Button buttonPause;
    private System.Windows.Forms.Button buttonStop;
    private System.Windows.Forms.Button buttonFramestep;
    private System.Windows.Forms.Button buttonLoop;

    // some variables 
    bool bLoop = false; 
    bool canStep = false;
    int minutes, seconds;
    string fileName;

    // filter graph and different interfaces
    FilterGraph fg = null;
    IGraphBuilder graphBuilder = null;
    IMediaControl mediaCtrl = null;
    IMediaEventEx mediaEvt = null;
    IMediaPosition mediaPos = null;
    IVideoFrameStep frameStep = null;
    IVideoWindow videoWin = null;
    IVideoWindow videoWin2 = null;

    // We'll use an Infinite Tee filter object to duplicate the
    // video stream by two
    InfTee tee = null;

    // We'll only treat wmv format 
    WMAsfReader reader =  null;

    // The Sobel object and filter interface
    object sobelObject = null;
    IBaseFilter sobel = null;
    // the black and white filter interface
    IBaseFilter bw = null;

    // the Guids for our custom filters
    Guid sobelGuid = new Guid( "1C826B9A-4008-4C41-B601-A783A40AFAB2" );
    Guid bwGuid = new Guid( "0C017086-684E-41c9-A4BB-640570C64B28" );

    // Some states variables, I could have used DSLib ones instead
    enum State { Playing, Paused, Stopped };
    State graphState;

    private System.Windows.Forms.Label label1;
    private System.Windows.Forms.StatusBarPanel statusBarPanel1;
    private System.Windows.Forms.StatusBar statusBar1;
    private System.Windows.Forms.Panel panel2;
    private System.Windows.Forms.Label label2;
    private System.Windows.Forms.Button buttonOpen;
    private System.ComponentModel.IContainer components;

    public Form1()
    {
      //
      // Required for Windows Form Designer support
      //
      InitializeComponent();

      //
      // TODO: Add any constructor code after InitializeComponent call
      //
      trackBar1.Minimum = 0;
      trackBar1.Maximum = 256;
      trackBar1.Enabled = true;
      trackBar1.Value = 128;
      statusBar1.Panels[0].Text = "Duration: 00m:00s";
      fileName = "";
    }

    /// <summary>
    /// Clean up any resources being used.
    /// </summary>
    protected override void Dispose( bool disposing )
    {
      if( disposing )
      {
        if (components != null) 
        {
          components.Dispose();
        }
      }
      base.Dispose( disposing );
    }

        #region Windows Form Designer generated code
    /// <summary>
    /// Required method for Designer support - do not modify
    /// the contents of this method with the code editor.
    /// </summary>
    private void InitializeComponent()
    {
      this.groupBox1 = new System.Windows.Forms.GroupBox();
      this.label2 = new System.Windows.Forms.Label();
      this.label1 = new System.Windows.Forms.Label();
      this.trackBar1 = new System.Windows.Forms.TrackBar();
      this.buttonLoop = new System.Windows.Forms.Button();
      this.buttonFramestep = new System.Windows.Forms.Button();
      this.buttonStop = new System.Windows.Forms.Button();
      this.buttonPause = new System.Windows.Forms.Button();
      this.buttonPlay = new System.Windows.Forms.Button();
      this.panel1 = new System.Windows.Forms.Panel();
      this.statusBarPanel1 = new System.Windows.Forms.StatusBarPanel();
      this.statusBar1 = new System.Windows.Forms.StatusBar();
      this.panel2 = new System.Windows.Forms.Panel();
      this.buttonOpen = new System.Windows.Forms.Button();
      this.groupBox1.SuspendLayout();
      ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).BeginInit();
      ((System.ComponentModel.ISupportInitialize)(this.statusBarPanel1)).BeginInit();
      this.SuspendLayout();
      // 
      // groupBox1
      // 
      this.groupBox1.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                            this.buttonOpen,
                                                                            this.label2,
                                                                            this.label1,
                                                                            this.trackBar1,
                                                                            this.buttonLoop,
                                                                            this.buttonFramestep,
                                                                            this.buttonStop,
                                                                            this.buttonPause,
                                                                            this.buttonPlay});
      this.groupBox1.Location = new System.Drawing.Point(8, 0);
      this.groupBox1.Name = "groupBox1";
      this.groupBox1.Size = new System.Drawing.Size(664, 88);
      this.groupBox1.TabIndex = 9;
      this.groupBox1.TabStop = false;
      // 
      // label2
      // 
      this.label2.Location = new System.Drawing.Point(256, 56);
      this.label2.Name = "label2";
      this.label2.Size = new System.Drawing.Size(88, 24);
      this.label2.TabIndex = 8;
      this.label2.Text = "Threshold";
      // 
      // label1
      // 
      this.label1.Location = new System.Drawing.Point(24, 56);
      this.label1.Name = "label1";
      this.label1.Size = new System.Drawing.Size(136, 23);
      this.label1.TabIndex = 7;
      this.label1.Text = "Treshold value: 128";
      // 
      // trackBar1
      // 
      this.trackBar1.AutoSize = false;
      this.trackBar1.Location = new System.Drawing.Point(344, 40);
      this.trackBar1.Name = "trackBar1";
      this.trackBar1.Size = new System.Drawing.Size(312, 45);
      this.trackBar1.TabIndex = 6;
      this.trackBar1.TickStyle = System.Windows.Forms.TickStyle.TopLeft;
      this.trackBar1.ValueChanged += new System.EventHandler(this.trackBar1_ValueChanged);
      // 
      // buttonLoop
      // 
      this.buttonLoop.Location = new System.Drawing.Point(560, 16);
      this.buttonLoop.Name = "buttonLoop";
      this.buttonLoop.Size = new System.Drawing.Size(88, 24);
      this.buttonLoop.TabIndex = 5;
      this.buttonLoop.Text = "Loop";
      this.buttonLoop.Click += new System.EventHandler(this.buttonLoop_Click);
      // 
      // buttonFramestep
      // 
      this.buttonFramestep.Enabled = false;
      this.buttonFramestep.Location = new System.Drawing.Point(448, 16);
      this.buttonFramestep.Name = "buttonFramestep";
      this.buttonFramestep.Size = new System.Drawing.Size(88, 24);
      this.buttonFramestep.TabIndex = 3;
      this.buttonFramestep.Text = "Framestep";
      this.buttonFramestep.Click += new System.EventHandler(this.buttonFramestep_Click);
      // 
      // buttonStop
      // 
      this.buttonStop.Location = new System.Drawing.Point(344, 16);
      this.buttonStop.Name = "buttonStop";
      this.buttonStop.Size = new System.Drawing.Size(88, 24);
      this.buttonStop.TabIndex = 2;
      this.buttonStop.Text = "Stop";
      this.buttonStop.Click += new System.EventHandler(this.buttonStop_Click);
      // 
      // buttonPause
      // 
      this.buttonPause.Location = new System.Drawing.Point(232, 16);
      this.buttonPause.Name = "buttonPause";
      this.buttonPause.Size = new System.Drawing.Size(88, 24);
      this.buttonPause.TabIndex = 1;
      this.buttonPause.Text = "Pause";
      this.buttonPause.Click += new System.EventHandler(this.buttonPause_Click);
      // 
      // buttonPlay
      // 
      this.buttonPlay.Location = new System.Drawing.Point(120, 16);
      this.buttonPlay.Name = "buttonPlay";
      this.buttonPlay.Size = new System.Drawing.Size(88, 24);
      this.buttonPlay.TabIndex = 0;
      this.buttonPlay.Text = "Play";
      this.buttonPlay.Click += new System.EventHandler(this.buttonPlay_Click);
      // 
      // panel1
      // 
      this.panel1.Location = new System.Drawing.Point(8, 104);
      this.panel1.Name = "panel1";
      this.panel1.Size = new System.Drawing.Size(320, 240);
      this.panel1.TabIndex = 4;
      // 
      // statusBarPanel1
      // 
      this.statusBarPanel1.Alignment = System.Windows.Forms.HorizontalAlignment.Center;
      this.statusBarPanel1.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Contents;
      this.statusBarPanel1.Width = 10;
      // 
      // statusBar1
      // 
      this.statusBar1.Location = new System.Drawing.Point(0, 360);
      this.statusBar1.Name = "statusBar1";
      this.statusBar1.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] {
                                                                                  this.statusBarPanel1});
      this.statusBar1.ShowPanels = true;
      this.statusBar1.Size = new System.Drawing.Size(680, 22);
      this.statusBar1.SizingGrip = false;
      this.statusBar1.TabIndex = 6;
      // 
      // panel2
      // 
      this.panel2.Location = new System.Drawing.Point(352, 104);
      this.panel2.Name = "panel2";
      this.panel2.Size = new System.Drawing.Size(320, 240);
      this.panel2.TabIndex = 7;
      // 
      // buttonOpen
      // 
      this.buttonOpen.Location = new System.Drawing.Point(16, 16);
      this.buttonOpen.Name = "buttonOpen";
      this.buttonOpen.Size = new System.Drawing.Size(88, 24);
      this.buttonOpen.TabIndex = 0;
      this.buttonOpen.Text = "Open";
      this.buttonOpen.Click += new System.EventHandler(this.buttonOpen_Click);
      // 
      // Form1
      // 
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(680, 382);
      this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                  this.panel2,
                                                                  this.statusBar1,
                                                                  this.panel1,
                                                                  this.groupBox1});
      this.MaximizeBox = false;
      this.Name = "Form1";
      this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
      this.Text = "Video Segmentation in C#";
      this.groupBox1.ResumeLayout(false);
      ((System.ComponentModel.ISupportInitialize)(this.trackBar1)).EndInit();
      ((System.ComponentModel.ISupportInitialize)(this.statusBarPanel1)).EndInit();
      this.ResumeLayout(false);

    }
        #endregion

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main() 
    {
      Application.Run(new Form1());
    }

    // 
    //This method create the filter graph
    //
    void InitInterfaces() 
    {
      try 
      {   
        fg = new FilterGraph();
        graphBuilder = (IGraphBuilder) fg; 
        mediaCtrl = (IMediaControl) fg;
        mediaEvt = (IMediaEventEx) fg;
        mediaPos = (IMediaPosition) fg;
      }
      catch(Exception) 
      {
        MessageBox.Show( "Couldn't start" );
      }
    }  

    //
    //This method stop the filter graph and ensures that we stop
    //sending messages to our window
    //
    void CloseInterfaces() 
    {   
      if( mediaCtrl != null ) 
      {
        mediaCtrl.StopWhenReady();
        mediaEvt.SetNotifyWindow( (IntPtr)0, WM_GRAPHNOTIFY, (IntPtr)0 ); 
      }  
      mediaCtrl = null;
      mediaEvt = null;
      mediaPos = null;
      if( canStep )
        frameStep = null;
      videoWin = null;
      videoWin2 = null;
      if( fg != null ) 
        Marshal.ReleaseComObject(this.fg);
      fg = null;
    }   
  

    //
    // This method checks if a pin is processing a video stream
    //
    bool CheckVideo( IPin pin ) 
    {
      AMMediaType mt = new AMMediaType();
      mt.majorType = MediaType.Video;
      if( pin.QueryAccept( mt ) == 0 )
        return true;
      else
        return false;
    }

    //
    // This method find the second renderer in the filter graph;
    // it assumes that the first renderer had its "owner" property set
    //
    IVideoWindow GetSecondRenderer() 
    {
      IEnumFilters enumFilters;
      ArrayList filtersArray = new ArrayList();

      IFilterGraph filterGraph = (IFilterGraph)fg;
      filterGraph.EnumFilters(out enumFilters);
      
      IBaseFilter[] filters = new IBaseFilter[1];
      int fetched;

      while(enumFilters.Next(1, filters, out fetched) == 0)
      {
        IVideoWindow ivw = filters[0] as IVideoWindow;
        if( ivw != null ) 
        {
          IntPtr outPtr = new IntPtr();
          ivw.get_Owner( out outPtr );
          if( outPtr == IntPtr.Zero )
            return ivw;
        }
      }
      return null;
    }

    //
    //Method to start to play a media file
    //
    void LoadFile( string fName ) 
    {            
      try
      {
        // build the graph

        // create sobel filter
        Type t = Type.GetTypeFromCLSID( sobelGuid );
        sobelObject = Activator.CreateInstance( t );
        sobel = (IBaseFilter)sobelObject;        
        
        // create the black and white filter
        t = Type.GetTypeFromCLSID( bwGuid );
        bw = (IBaseFilter)Activator.CreateInstance( t );

        // create the infinite tee filter
        tee = new InfTee();

        // add all these filters to the graph
        graphBuilder.AddFilter( sobel, "Sobel" );
        graphBuilder.AddFilter( bw, "BW" );
        graphBuilder.AddFilter( (IBaseFilter)tee, "Tee" );

        // prepare our asf reader object
        reader = new WMAsfReader();
        ((IFileSourceFilter)reader).Load( fName, null );
        graphBuilder.AddFilter( (IBaseFilter)reader, "Reader" );

        // connect video source to infinite tee filter
        IPin teeInput = DsFindPin.ByDirection( (IBaseFilter)tee, PinDirection.Input, 0 );
        IPin vidOutput = DsFindPin.ByDirection( (IBaseFilter)reader, PinDirection.Output, 0 );
        // check if the video stream is on the first pin
        if( CheckVideo( vidOutput ) )
          graphBuilder.Connect( vidOutput, teeInput );
        else // video stream is on second pin
        {
          vidOutput = DsFindPin.ByDirection( (IBaseFilter)reader, PinDirection.Output, 1 );
          graphBuilder.Connect( vidOutput, teeInput );
        }

        // render output pin of tee
        IPin teeOutput = DsFindPin.ByDirection( (IBaseFilter)tee, PinDirection.Output, 0 );
        graphBuilder.Render( teeOutput );
        
        //check for the ability to step
        frameStep = fg as IVideoFrameStep;
        if( frameStep.CanStep( 1, null ) == 0 ) 
        {
          canStep = true;
          buttonFramestep.Enabled = true;
        }

        //prepare and set the first video window
        videoWin    = fg as IVideoWindow;
        videoWin.put_Owner( (IntPtr)panel2.Handle );
        videoWin.put_WindowStyle( WindowStyle.Child | WindowStyle.ClipSiblings | WindowStyle.ClipChildren );                
        Rectangle rc = panel2.ClientRectangle;
        videoWin.SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
        mediaEvt.SetNotifyWindow( (IntPtr)this.Handle, WM_GRAPHNOTIFY, (IntPtr)0 );
                
        // render original video from second output pin
        IPin teeOutput2 = DsFindPin.ByDirection( (IBaseFilter)tee, PinDirection.Output, 1 );
        graphBuilder.Render( teeOutput2 );

        // setup second video window 
        videoWin2 = GetSecondRenderer();
        if( videoWin2 != null ) 
        {
          videoWin2.put_Owner( (IntPtr)panel1.Handle );
          videoWin2.put_WindowStyle( WindowStyle.Child | WindowStyle.ClipSiblings | WindowStyle.ClipChildren );                
          rc = panel1.ClientRectangle;
          videoWin2.SetWindowPosition( 0, 0, rc.Right, rc.Bottom );
        }

        //set the different values for controls
        Text = fName;    
        double duration;
        mediaPos.get_Duration( out duration);        
        trackBar1.Value = 128;
        minutes = (int)duration/60;
        seconds = (int)duration % 60;
        statusBar1.Panels[0].Text = "Duration: " + minutes.ToString( "D2" ) 
          + ":m" + seconds.ToString( "D2" ) + ":s" ;
        graphState = State.Playing;

        //DsROTEntry rot = new DsROTEntry( (IFilterGraph)fg );
        //start the playback
        mediaCtrl.Run();  
      } 
      catch( Exception ) { Text = "Error loading file"; }
    }   

    //different constant needed by the app
    const int WM_GRAPHNOTIFY    = 0x00008001;   
    const int WS_CHILD          = 0x40000000;
    const int WS_CLIPCHILDREN   = 0x02000000;
    const int WS_CLIPSIBLINGS   = 0x04000000;
    const int WM_MOVE           = 0x00000003;
    const int EC_COMPLETE       = 0x00000001;
  
    //
    //override to process custom graph notify messages
    //
    protected override void WndProc( ref Message m )
    {
      if( m.Msg == WM_GRAPHNOTIFY )
      {
        if( mediaEvt != null )
          OnGraphNotify();
        return;
      }
      base.WndProc( ref m );
    }

    //
    //call to process WM_GRAPHNOTIFY message
    //get out of the loop when GetEvent returns not 0
    //
    void OnGraphNotify()
    {
      int p1, p2;
      EventCode code;

      if( mediaEvt == null )
        return;
      while( mediaEvt.GetEvent( out code, out p1, out p2, 0 ) == 0 ) 
      {                             
        mediaEvt.FreeEventParams( code, p1, p2 );
        if( code == EventCode.Complete )
          OnClipCompleted();
      }
    }

    //
    //method that update the graph filter and/or gui controls
    //when we have received a message that the media file is done playing
    //
    void OnClipCompleted()
    {
      graphState = State.Stopped;
      if( mediaCtrl == null )
        return;
      mediaCtrl.Stop(); 
      if( bLoop ) 
      {
        mediaPos.put_CurrentPosition( 0.0 );
        mediaCtrl.Run();
      }
    }

    //
    // Display a dialog box to have the user chose a video file
    //
    private void buttonOpen_Click(object sender, System.EventArgs e)
    {
      // if the graph is not stopped, stop it now
      if( mediaCtrl != null && graphState != State.Stopped ) 
      {
        mediaCtrl.Stop();
        graphState = State.Stopped;        
      }
      OpenFileDialog ofd = new OpenFileDialog();
      ofd.Filter = "WMVideo (*.wmv)|*.wmv";
      if( ofd.ShowDialog() == DialogResult.OK ) 
      {
        CloseInterfaces();
        InitInterfaces();
        fileName = ofd.FileName;
        LoadFile( fileName );
      }
    }    

    //
    // if user clicks play then play video file or restart if stopped
    //
    private void buttonPlay_Click(object sender, System.EventArgs e)
    {
      if( mediaCtrl != null &&  graphState == State.Paused ) 
      {
        mediaCtrl.Run();
        graphState = State.Playing;
      }
      if( mediaCtrl != null &&  graphState == State.Stopped )
      {
        mediaCtrl.Stop(); 
        mediaPos.put_CurrentPosition( 0.0 );
        mediaCtrl.Run();
      }
    }

    //
    // pause button handler
    //
    private void buttonPause_Click(object sender, System.EventArgs e)
    {
      if( mediaCtrl != null && graphState != State.Paused ) 
      {
        mediaCtrl.Pause();
        graphState = State.Paused;
      }
    }

    //
    // stop button handler
    //
    private void buttonStop_Click(object sender, System.EventArgs e)
    {
      if( mediaCtrl != null && graphState != State.Stopped ) 
      {
        mediaCtrl.Stop();
        graphState = State.Stopped;        
      }
    } 

    //
    //when the user grabs the trackbar, we change the Sobel 
    //treshold value and step one frame (if possible) or run again
    //
    private void trackBar1_ValueChanged(object sender, System.EventArgs e)
    {
      if( mediaCtrl != null ) 
      {
        mediaCtrl.Pause();
        // access the ISobel interface on the sobel object
        ISobel isobel = sobelObject as ISobel;
        if( isobel != null ) 
        {
          isobel.SetThreshold( trackBar1.Value );
          label1.Text = "Treshold value: " + trackBar1.Value.ToString();
        }
        if( frameStep != null ) 
        {
          frameStep.Step( 1, null );
          graphState = State.Paused;
        }
        else
          mediaCtrl.Run();
      }
    }

    //
    // loop button handler
    private void buttonLoop_Click(object sender, System.EventArgs e)
    {
      bLoop = ! bLoop;
    }


    //
    // frame stepping handler
    //
    private void buttonFramestep_Click(object sender, System.EventArgs e)
    {
      if( frameStep != null ) 
      {
        mediaCtrl.Pause();
        graphState = State.Paused;
        frameStep.Step( 1, null );
      }       
    }
  }

  // 
  // The ISobel interface is a custom COM interface
  // exposed by the Sobel filter, in order to access it
  // from C#, we need to define it
  //
  [Guid("263AFD9E-465C-4dde-9BB1-168C25E2B87F"),
  InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  public interface ISobel
  {
    int SetThreshold( int newValue );
  }

}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
You can read my blog entries at:
http://wwww.informikon.com/blog/

Comments and Discussions