Click here to Skip to main content
15,886,857 members
Articles / Programming Languages / C#

DSGraphEdit: A Reasonable Facsimile of Microsoft's GraphEdit in .NET

Rate me:
Please Sign up or sign in to vote.
4.93/5 (79 votes)
28 Jan 2018MIT7 min read 297.3K   10K   142  
A library for adding DirectShow GraphEdit-like abilities to .NET applications
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

using DirectShowLib;
using DirectShowLib.Utils;
using DirectShowLib.Dvd;
using DaggerLib.Core;
using DaggerLib.UI.Windows;

namespace DaggerLib.DSGraphEdit
{
    public partial class DSGraphEditPanel : UserControl
    {
        #region Fields

        // style of pin placement for the nodes
        private DaggerNodePinPlacement _pinPlacement = DaggerNodePinPlacement.Outset;

        private const int WMGraphNotify = 0x0400 + 13;
        
        // FilterGraph for DirectShow Source
        private IFilterGraph _graph;

        // flag to indicate if filter graph was created here
        private bool _filterGraphCreated;

        // flag to indicate this graph is connected to an IFilterGraph on the ROT
        private bool _isRemoteGraph = false;

        // the string representing the ROT connected IFilterGraph
        private string _rotEntryString = string.Empty;

        // flag to indicate if we use Intelligent Connection
        private bool _connectIntelligent = true;

        // flag to indicate if the graph uses a reference clock if available
        private bool _useReferenceClock = true;

        //interfaces for controling DirectShow Sources
        private IGraphBuilder _graphBuilder;
        private IMediaControl _mediaControl;
        private IMediaSeeking _mediaSeeking;
        private IBasicAudio _basicAudio;
        private IBasicVideo2 _basicVideo;
        private IVideoWindow _videoWindow;
        private IMediaEventEx _mediaEventEx;
        private IVideoFrameStep _frameStep;

        //ROT to allow attaching in GraphEdit
        private DsROTEntry rot;

        private FilterState _mediaState = FilterState.Stopped;

        #endregion

        #region Constructors

        /// <summary>
        /// Default Constructor
        /// </summary>
        public DSGraphEditPanel()
        {
            InitializeComponent();
            int hr = 0;

            // create filter graph
            _graph = (IFilterGraph)new FilterGraph();

            _filterGraphCreated = true;

            // give the filter graph to the DaggerUIGraph
            dsDaggerUIGraph1._Graph = _graph;

            // mark it as having been created internally
            dsDaggerUIGraph1._filterGraphCreated = true;

#if DEBUG
            rot = new DsROTEntry(_graph);
#endif

            // Initialize items common to all constructors
            Init();

            // get the state
            _mediaControl.GetState(100, out _mediaState);

            // Have the graph signal event via window callbacks for performance
            _mediaEventEx = _graph as IMediaEventEx;
            if (_mediaEventEx != null)
            {
                hr = _mediaEventEx.SetNotifyWindow(this.Handle, WMGraphNotify, IntPtr.Zero);
                DsError.ThrowExceptionForHR(hr);
            }
        }

        /// <summary>
        /// Connect to existing filtergraph
        /// </summary>
        public DSGraphEditPanel(IFilterGraph filterGraph)
        {
            InitializeComponent();

            // create filter graph
            _graph = filterGraph;

            // give the filter graph to the DaggerUIGraph
            dsDaggerUIGraph1._Graph = _graph;

            // Initialize items common to all constructors
            Init();

            // get the state
            _mediaControl.GetState(100, out _mediaState);

            // match our state to the existing state
            switch (_mediaState)
            {
                case FilterState.Paused:
                    Pause();
                    break;
                case FilterState.Running:
                    Play();
                    break;
                case FilterState.Stopped:
                    Stop();
                    break;
                default:
                    break;
            }

            dsDaggerUIGraph1.SyncGraphs(null);
            dsDaggerUIGraph1.ArrangeNodes(AutoArrangeStyle.All);

            // see if this graph has a reference clock
            IReferenceClock rc = null;
            (filterGraph as IMediaFilter).GetSyncSource(out rc);
            if (rc == null)
            {
                _useReferenceClock = false;
            }
        }

        /// <summary>
        /// Constructor from a Graph file
        /// </summary>
        /// <param name="graphFileName">Path of Graph File to load</param>
        public DSGraphEditPanel(string graphFileName)
        {
            InitializeComponent();
            int hr = 0;

            // create filter graph
            _graph = (IFilterGraph)new FilterGraph();
            _filterGraphCreated = true;

            // give the filter graph to the DaggerUIGraph
            dsDaggerUIGraph1._Graph = _graph;

            // mark it as having been created internally
            dsDaggerUIGraph1._filterGraphCreated = true;

#if DEBUG
            rot = new DsROTEntry(_graph);
#endif            
            // Initialize items common to all constructors
            Init();

            // try to load the graph from IStorage
            try
            {
                hr = FilterGraphTools.LoadGraphFile(_graphBuilder, graphFileName);
                Marshal.ThrowExceptionForHR(hr);
            }
            catch (Exception ex)
            {
                // release the filtergraph and rethrow the exception
                Marshal.ReleaseComObject(_graph);
                throw ex;
            }

            // get the state
            _mediaControl.GetState(100, out _mediaState);

            // Have the graph signal event via window callbacks for performance
            _mediaEventEx = (IMediaEventEx)_graph;
            hr = _mediaEventEx.SetNotifyWindow(this.Handle, WMGraphNotify, IntPtr.Zero);
            DsError.ThrowExceptionForHR(hr);

            // sync the loaded graph and arrange the nodes
            dsDaggerUIGraph1.SyncGraphs(null);
            dsDaggerUIGraph1.ArrangeNodes(AutoArrangeStyle.All);
        }

        // From Play-Wnd-2005 sample in DirectShowLib
        //
        // Some video renderers support stepping media frame by frame with the
        // IVideoFrameStep interface.  See the interface documentation for more
        // details on frame stepping.
        //
        private bool GetFrameStepInterface()
        {
            int hr = 0;

            IVideoFrameStep frameStepTest = null;

            // Get the frame step interface, if supported
            frameStepTest = (IVideoFrameStep)this._graph;

            // Check if this decoder can step
            hr = frameStepTest.CanStep(0, null);
            if (hr == 0)
            {
                _frameStep = frameStepTest;
                return true;
            }
            else
            {
                _frameStep = null;
                return false;
            }
        }

        /// <summary>
        /// Static method to connect to a IFilterGraph on the ROT and create a DSGraphEditPanel for it
        /// </summary>
        /// <returns></returns>
        public static DSGraphEditPanel ConnectToRemoteGraph()
        {
            DSGraphEditPanel ret = null;
            ROTEntriesDialog rd = new ROTEntriesDialog();
            if (rd.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    // Connect to the FilterGraph selected in ROTEntriesDialog and create
                    // a new GraphEditPanel for it
                    IFilterGraph fg = rd.FilterGraph;
                    ret = new DSGraphEditPanel(fg);
                    ret._isRemoteGraph = true;
                    ret._rotEntryString = rd.SelectedROTEntry;
                }
                finally
                {
                    // always dispose of ROTEntriesDialog when you're done with it
                    rd.Dispose();
                    rd = null;
                }
            }
            return ret;
        }

        /// <summary>
        /// Initialize items common to all constructors
        /// </summary>
        private void Init()
        {
            // get interfaces
            _mediaControl = _graph as IMediaControl;
            _basicAudio = _graph as IBasicAudio;
            _basicVideo = _graph as IBasicVideo2;
            _graphBuilder = _graph as IGraphBuilder;            

            // hook the DaggerGraph events
            dsDaggerUIGraph1.Graph.BeforeNodeRemoved += new BeforeNodeRemoveHandler(Graph_BeforeNodeRemoved);
            dsDaggerUIGraph1.Graph.BeforePinsConnected += new PinBeforeConnectedHandler(Graph_BeforePinsConnected);
            dsDaggerUIGraph1.Graph.BeforePinsDisconnected += new PinBeforeDisconnectedHandler(Graph_BeforePinsDisconnected);
            dsDaggerUIGraph1.BeforeSelectionDeleted += new BeforeDeleteSelected(dsDaggerUIGraph1_BeforeSelectionDeleted);
            dsDaggerUIGraph1.Graph.OnTopologyChanged +=new EventHandler(Graph_OnTopologyChanged);
            this.Disposed += new EventHandler(DSGraphEditPanel_Disposed);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Get's or sets the display style of filter connections
        /// </summary>
        [Category("GraphStyles")]
        [Description("Get's or sets the display style of filter connections")]
        public NoodleStyle NoodleStyle
        {
            get
            {
                return dsDaggerUIGraph1.NoodleStyle;
            }
            set
            {
                dsDaggerUIGraph1.NoodleStyle = value;
            }
        }

        /// <summary>
        /// Get's or sets if the pin names are drawn onto the canvas
        /// </summary>
        [Category("GraphStyles")]
        [Description("Get's or sets if the pin names are drawn onto the canvas")]
        public bool ShowPinNames
        {
            get
            {
                return this.dsDaggerUIGraph1.ShowPinNames;
            }
            set
            {
                this.dsDaggerUIGraph1.ShowPinNames = value;
            }
        }

        /// <summary>
        /// Get's or sets the pin placement in the nodes
        /// </summary>
        [Category("GraphStyles")]
        [Description("Gets or sets the visual style of the filters in the graph")]
        public DaggerNodePinPlacement PinPlacement
        {
            get
            {
                return _pinPlacement;
            }
            set
            {
                _pinPlacement = value;
                dsDaggerUIGraph1.BeginCanvasUpdate();
                foreach (DaggerUINode uinode in dsDaggerUIGraph1.AllNodes)
                {
                    if (uinode.PinPlacement != value)
                    {
                        uinode.PinPlacement = value;
                    }
                }
                dsDaggerUIGraph1.EndCanvasUpdate();
            }
        }

        /// <summary>
        /// Sets visual properties of the DSGraphEditPanel
        /// </summary>
        [Browsable(false)]
        public DSGraphEditPanelProperties DSGraphEditPanelProperties
        {
            set
            {
                int r = dsDaggerUIGraph1.BeginCanvasUpdate();
                DropShadow = value.DropShadowVisible;
                PinPlacement = value.PinPlacement;
                ModalProperties = value.ModalProperties;
                BackColor = value.CanvasBackColor;
                ShowTimeSlider = value.ShowTimeSlider;
                ShowPinNames = value.ShowPinNames;
                NoodleStyle = value.NoodleStyle;
                r = dsDaggerUIGraph1.EndCanvasUpdate();
            }
        }

        /// <summary>
        /// Gets or sets if FilterGraph uses a reference clock if available
        /// </summary>
        [Browsable(false)]
        public bool UseReferenceClock
        {
            get
            {
                return _useReferenceClock;
            }
            set
            {
                int hr = 0;
                if (value != _useReferenceClock)
                {
                    // make sure the graph is stopped
                    Stop();

                    _useReferenceClock = value;
                    DSFilterNodeUI refnode = GetReferenceClock();
                    try
                    {
                        if (value)
                        {
                            if (refnode == null)
                            {
                                dsDaggerUIGraph1._Graph.SetDefaultSyncSource();
                            }
                            else
                            {
                                (dsDaggerUIGraph1._Graph as IMediaFilter).SetSyncSource(refnode._referenceClock);
                            }
                        }
                        else
                        {
                            hr = (dsDaggerUIGraph1._Graph as IMediaFilter).SetSyncSource(null);
                        }
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, "Error setting reference clock");
                    }
                }
            }
        }

        /// <summary>
        /// Gets or Sets if Intelligent Connect is used in IPin connections
        /// </summary>
        [Category("GraphStyles")]
        [Description("Gets or sets if pin connecting uses Intelligent Connect")]
        public bool ConnectIntelligent
        {
            get
            {
                return _connectIntelligent;
            }
            set
            {
                _connectIntelligent = value;
            }
        }

        /// <summary>
        /// Gets or sets if the drop shadow is visible
        /// </summary>
        [Description("Gets or sets if the graph shows a dropshadow")]
        [Category("GraphStyles")]
        public bool DropShadow
        {
            get
            {
                return dsDaggerUIGraph1.DropShadowVisible;
            }
            set
            {
                dsDaggerUIGraph1.DropShadowVisible = value;
            }
        }

        /// <summary>
        /// Gets or sets if Property pages are shown as modal dialogs
        /// </summary>
        [Description("Gets or sets if Property pages are shown as modal dialogs")]
        [Category("GraphStyles")]
        public bool ModalProperties
        {
            get
            {
                return dsDaggerUIGraph1.ModalProperties;
            }
            set
            {
                dsDaggerUIGraph1.ModalProperties = value;
            }
        }

        /// <summary>
        /// Gets or sets the back color of the Canvas
        /// </summary>
        [Description("Gets or sets the backcolor of the graph canvas")]
        [Category("GraphStyles")]
        public override Color BackColor
        {
            get
            {
                return dsDaggerUIGraph1.BackColor;
            }
            set
            {
                dsDaggerUIGraph1.BackColor = value;
            }
        }

        /// <summary>
        /// Gets or sets if the Time Slider control is visible
        /// </summary>
        [Description("Gets or sets if the Time Slider control is visible")]
        [Category("GraphStyles")]
        public bool ShowTimeSlider
        {
            get
            {
                return _timeSliderControl.Visible;
            }
            set
            {
                _timeSliderControl.Visible = value;
            }
        }

        /// <summary>
        /// Returns true if this is connected to a remote IFilterGraph on the ROT
        /// </summary>
        [Browsable(false)]
        public bool IsRemoteGraph
        {
            get
            {
                return _isRemoteGraph;
            }
        }

        /// <summary>
        /// Returns a string representing the remote IFilterGraph
        /// </summary>
        [Browsable(false)]
        public string ROTEntryString
        {
            get
            {
                return _rotEntryString;
            }
        }

        /// <summary>
        /// Returns the internal UI Graph
        /// </summary>
        [Browsable(false)]
        public DSDaggerUIGraph DSDaggerUIGraph
        {
            get
            {
                return dsDaggerUIGraph1;
            }
        }

        /// <summary>
        /// Gets a copy of the DaggerUIGraph Canvas Image
        /// </summary>
        [Browsable(false)]
        public Bitmap CanvasImage
        {
            get
            {
                return dsDaggerUIGraph1.CanvasImage;
            }
        }
        #endregion

        #region Public Methods

        /// <summary>
        /// Render a Media File
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public int RenderMediaFile(string filename)
        {
            int hr = _graphBuilder.RenderFile(filename, string.Empty);
            dsDaggerUIGraph1.SyncGraphs(null);
            dsDaggerUIGraph1.ArrangeNodes(AutoArrangeStyle.All);
            return hr;
        }

        /// <summary>
        /// Render a Media URL
        /// </summary>
        /// <param name="URL"></param>
        /// <returns></returns>
        public int RenderURL(string URL)
        {
            // guess what?  It's the same thing as RenderMediaFile :)
            return RenderMediaFile(URL);
        }

        /// <summary>
        /// Get the DSFilterNodeUI that is marked as the reference clock for the graph
        /// </summary>
        /// <returns></returns>
        internal DSFilterNodeUI GetReferenceClock()
        {
            foreach (DSFilterNodeUI node in dsDaggerUIGraph1.AllNodes)
            {
                if (node._clockButton.Tag != null && (bool)node._clockButton.Tag == true)
                {
                    return node;
                }
            }
            return null;
        }

        /// <summary>
        /// Save the current IFilterGraph to a grf file
        /// </summary>
        /// <param name="filename"></param>
        public void SaveFilterGraph(string filename)
        {
            FilterGraphTools.SaveGraphFile(_graphBuilder, filename);
        }

        /// <summary>
        /// Force the Disconnection of all IPins in the filterGraph
        /// </summary>
        public void DisconnectAllPins()
        {
            dsDaggerUIGraph1.BeginCanvasUpdate();

            foreach (DaggerNode node in dsDaggerUIGraph1.Graph.AllNodes)
            {
                foreach (DaggerOutputPin pin in node.OutputPins)
                {
                    // force disconnect even if it says it can't
                    pin.Disconnect(true);
                }
            }

            dsDaggerUIGraph1.EndCanvasUpdate();
        }

        /// <summary>
        /// Create and add a filter from a DSFilterTreeViewNode
        /// </summary>
        /// <param name="tn"></param>
        public IBaseFilter AddFilter(DSFilterTreeViewNode tn)
        {
            return dsDaggerUIGraph1.AddFilter(tn);
        }

        #endregion

        #region Media Control

        public void Play()
        {
            if (_mediaState != FilterState.Running)
            {
                _mediaControl.Run();
                _mediaState = FilterState.Running;
            }
            _playButton.Enabled = false;
            _pauseButton.Enabled = true;
            _stopButton.Enabled = true;
            timeSliderTimer.Enabled = true;
        }

        public void Pause()
        {
            if (_mediaState != FilterState.Paused)
            {
                _mediaControl.Pause();
                _mediaState = FilterState.Paused;
            }
            _playButton.Enabled = true;
            _pauseButton.Enabled = false;
            _stopButton.Enabled = true;
            timeSliderTimer.Enabled = false;
        }

        public void Stop()
        {
            try
            {
                if (_mediaState != FilterState.Stopped)
                {
                    _mediaControl.Stop();
                    _mediaState = FilterState.Stopped;
                }
            }
            catch(Exception ex)
            {
#if DEBUG 
                MessageBox.Show(ex.Message);
#endif
            }
            finally
            {
                _playButton.Enabled = true;
                _pauseButton.Enabled = true;
                _stopButton.Enabled = false;
                timeSliderTimer.Enabled = false;
            }
        }

        public void SyncGraphs()
        {
            dsDaggerUIGraph1.SyncGraphs(null);

            // reset the canvas size to the actual size
            dsDaggerUIGraph1.CanvasSize = dsDaggerUIGraph1.ActualCanvasSize;
        }

        public void ArrangeNodes()
        {
            dsDaggerUIGraph1.ArrangeNodes(AutoArrangeStyle.All);
        }

        public int StepOneFrame()
        {
            int hr = 0;

            // If the Frame Stepping interface exists, use it to step one frame
            if (_frameStep != null)
            {
                // The graph must be paused for frame stepping to work
                Pause();

                // Step the requested number of frames, if supported
                hr = _frameStep.Step(1, null);
            }

            // update the TimeSlider position
            timeSliderTimer_Tick(null, null);

            return hr;
        }

        #endregion

        #region DaggerUIGraph Events

        /////////////////////////////////////////////////////////////////////////////////////////////////
        // These events translate the user interface of DaggerLib into DirectShow filter connection
        // methods.
        /////////////////////////////////////////////////////////////////////////////////////////////////

        bool dsDaggerUIGraph1_BeforeSelectionDeleted(object sender)
        {
            if (_mediaState != FilterState.Stopped)
            {
                MessageBox.Show("Graph must be stopped before deleting", "Error");
                return false;
            }
            else return true;
        }

        /// <summary>
        /// Called when a uinode is added it the graph
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void dsDaggerUIGraph1_ControlAdded(object sender, ControlEventArgs e)
        {
            // get the last uinode added to the graph
            DSFilterNodeUI node = (sender as DSDaggerUIGraph).Controls[(sender as DSDaggerUIGraph).Controls.Count - 1] as DSFilterNodeUI;
            if (node != null)
            {
                // set it's pin placement
                if (node.PinPlacement != _pinPlacement)
                {
                    node.PinPlacement = _pinPlacement;
                }

                // set the Properties Button styles based on ModalProperties setting
                node.SetModalProperties();

                // update it's pins in case they changed during the filter insertion process (EVR especially)
                (node.Node as DSFilterNode).SyncPins();
            }
        }

        /// <summary>
        /// Called when a node or a connection is added/removed in the DaggerLib graph
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Graph_OnTopologyChanged(object sender, EventArgs e)
        {
            // the topology of the graph has changed
            // see if seeking is available and set the extent of the TimeSliderControl
            _mediaSeeking = _graph as IMediaSeeking;
            if (_mediaSeeking != null)
            {
                long duration = 0;
                _mediaSeeking.GetDuration(out duration);

                // only set the extents if something has changed
                if (_timeSliderControl.Extent != (int)(duration / 10000))
                {
                    _timeSliderControl.Extent = (int)(duration / 10000);
                    _timeSliderControl.Min = 0;
                    _timeSliderControl.Max = _timeSliderControl.Extent;
                }
            }

            // see if frame step is available
            _frameStepButton.Enabled = GetFrameStepInterface();

            // see if IVideoWindow is available
            _videoWindow = _graph as IVideoWindow;
        }

        /// <summary>
        /// Called before a node is removed from a DaggerLib Graph
        /// </summary>
        /// <param name="node"></param>
        /// <returns>false if node cannot be removed</returns>
        bool Graph_BeforeNodeRemoved(DaggerNode node)
        {
            // make sure the graph is stopped
            Stop();

            // make sure there if in fact a IBaseFilter in this node
            if ((node as DSFilterNode)._filter != null)
            {
                int hr = _graph.RemoveFilter((node as DSFilterNode)._filter);
                if (hr != 0)
                {
                    // the filtergraph won't let go of the filter
                    MessageBox.Show(DsError.GetErrorText(hr));
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// Check if we are allowed to disconnect two pins
        /// </summary>
        /// <param name="output"></param>
        /// <param name="input"></param>
        /// <returns>false if pins failed to disconnect</returns>
        bool Graph_BeforePinsDisconnected(DaggerOutputPin output, DaggerInputPin input)
        {
            int hr = 0;

            // make sure the graph is stopped
            Stop();

            // do these two pins still belong to the ds filters?
            if ((output.ParentNode as DSFilterNode).GetPins().Contains((output as DSOutputPin)._pin) &&
                (input.ParentNode as DSFilterNode).GetPins().Contains((input as DSInputPin)._pin))
            {
                // are these two pins actually connected?  This can be caused by Syncing a graph that has pins re-routed elsewhere.
                IPin conto = null;
                (output as DSOutputPin)._pin.ConnectedTo(out conto);
                if ((input as DSInputPin)._pin != conto)
                {
                    if (conto != null)
                    {
                        Marshal.ReleaseComObject(conto);
                    }
                    return true;
                }
                if (conto != null)
                {
                    Marshal.ReleaseComObject(conto);
                }

                // If we fail to disconnect the IPins return false.
                // This will tell DaggerLib not to remove the Noodle in it's own internal graph.
                hr = (output as DSOutputPin)._pin.Disconnect();
                if (hr != 0 && hr != 1) /* 1 = not connected*/
                {
                    MessageBox.Show(DsError.GetErrorText(hr));
                    return false;
                }

                // Call Disconnect on the input also to reset it's allowed media types (who knew?)
                (input as DSInputPin)._pin.Disconnect();

                // sync the pins on the nodes in case pins have been added or deleted
                if ((output.ParentNode as DSFilterNode)._filter != null)
                {
                    (output.ParentNode as DSFilterNode).SyncPins();
                }
                if ((input.ParentNode as DSFilterNode)._filter != null)
                {
                    (input.ParentNode as DSFilterNode).SyncPins();
                }

                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// Try to connect 2 pins in the FilterGraph.
        /// </summary>
        /// <param name="output"></param>
        /// <param name="input"></param>
        /// <returns>false if pins cannot be connected</returns>
        bool Graph_BeforePinsConnected(DaggerOutputPin output, DaggerInputPin input)
        {
            IPin connectedTo;
            (output as DSOutputPin)._pin.ConnectedTo(out connectedTo);
            if (connectedTo == (input as DSInputPin)._pin)
            {
                // these two are already connected in DSGraph so allow it
                Marshal.ReleaseComObject(connectedTo);
                return true;
            }

            if (connectedTo != null)
            {
                Marshal.ReleaseComObject(connectedTo);
            }

            // make sure the graph is stopped
            Stop();

            int hr = 0;

            if (!_connectIntelligent)
            {
                hr = _graph.ConnectDirect((output as DSOutputPin)._pin, (input as DSInputPin)._pin, null);
                if (hr == 0)
                {
                    // sync the pins on the nodes
                    (output.ParentNode as DSFilterNode).SyncPins();
                    (input.ParentNode as DSFilterNode).SyncPins();

                    return true;
                }
            }
            else
            {
                // try intelligent connection with the GraphBuilder
                hr = _graphBuilder.Connect((output as DSOutputPin)._pin, (input as DSInputPin)._pin);
                if (hr == 0 || hr == DsResults.S_PartialRender)
                {
                    // sync the FilterGraph and the DaggerGraph
                    dsDaggerUIGraph1.SyncGraphs(null);

                    // sync the pins on the nodes
                    (output.ParentNode as DSFilterNode).SyncPins();
                    (input.ParentNode as DSFilterNode).SyncPins();

                    // because SyncGraph already creates needed connections, return false to cancel this connection
                    return false;
                }
            }

            // cancel pin connection operations
            dsDaggerUIGraph1.StopPinConnect();

            // if we get here, we simply couldn't connect the pins
            MessageBox.Show(DsError.GetErrorText(hr));

            // sync the pins on the nodes just in case an attempt to connect created new pins
            (output.ParentNode as DSFilterNode).SyncPins();
            (input.ParentNode as DSFilterNode).SyncPins();

            return false;
        }

        #endregion

        #region ToolStrip Button Events

        private void _playButton_Click(object sender, EventArgs e)
        {
            Play();
        }

        private void _pauseButton_Click(object sender, EventArgs e)
        {
            Pause();
        }

        private void _stopButton_Click(object sender, EventArgs e)
        {
            Stop();
        }

        private void _refreshGraphButton_Click(object sender, EventArgs e)
        {
            SyncGraphs();
        }

        private void _arrangeNodesButton_Click(object sender, EventArgs e)
        {
            ArrangeNodes();
        }

        private void _disconnectAllPinsButton_Click(object sender, EventArgs e)
        {
            DisconnectAllPins();
        }

        private void _frameStepButton_Click(object sender, EventArgs e)
        {
            StepOneFrame();
        }

        #endregion

        #region Overrides

        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WMGraphNotify:
                    {
                        HandleGraphEvent();
                        break;
                    }
            }

            // Pass this message to the video window for notification of system changes
            try
            {
                if (_videoWindow != null)
                    _videoWindow.NotifyOwnerMessage(m.HWnd, m.Msg, m.WParam, m.LParam);
            }
            catch
            {
                _videoWindow = null;
            }

            base.WndProc(ref m);
        }

        #endregion

        #region Event Handlers

        void DSGraphEditPanel_Disposed(object sender, EventArgs e)
        {
            // only stop the graph if we created it
            if (_filterGraphCreated)
            {
                Stop();
            }

            timeSliderTimer.Enabled = false;

            // remove from rot table if we added it
            if (rot != null)
            {
                rot.Dispose();
                rot = null;
            }

            try
            {
                // nix the media sink
                if (_mediaEventEx != null)
                {
                    _mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);
                }

                _graphBuilder = null;
                _mediaControl = null;
                _mediaSeeking = null;
                _basicAudio = null;
                _basicVideo = null;
                _videoWindow = null;
                _mediaEventEx = null;

                // only release the filtergraph if WE created it.  If the user passed one into a constructor,
                // it's up to them to release it.
                if (_filterGraphCreated)
                {
                    int refc = Marshal.ReleaseComObject(_graph);
                }

                // if it is a connected graph, renounce all claim on the proxie's RCW
                if (_isRemoteGraph)
                {
                    Marshal.FinalReleaseComObject(_graph);
                }
            }
            catch (InvalidComObjectException ex)
            {
                // the RCW became disconnected,  This is most likely caused by connecting to a remote graph
                // in the same AppDomain.  Just ignore it.
            }
        }

        /// <summary>
        /// Callback that handles events sent from the IFilterGraph
        /// </summary>
        private void HandleGraphEvent()
        {
            int hr = 0;
            EventCode evCode;
            IntPtr evParam1, evParam2;

            // Make sure that we don't access the media event interface
            // after it has already been released.
            if (_mediaEventEx == null)
                return;

            // Process all queued events
            while (_mediaEventEx.GetEvent(out evCode, out evParam1, out evParam2, 0) == 0)
            {
                // Free memory associated with callback, since we're not using it
                hr = _mediaEventEx.FreeEventParams(evCode, evParam1, evParam2);

                // If this is the end of the clip, reset to beginning
                if (evCode == EventCode.Complete)
                {
                    Stop();
                    // Rewind to first frame of movie
                    hr = _mediaSeeking.SetPositions((long)_timeSliderControl.Min * 10000, AMSeekingSeekingFlags.AbsolutePositioning,
                      null, AMSeekingSeekingFlags.NoPositioning);
                    _timeSliderControl.Pos = _timeSliderControl.Min;
                }
            }
        }

        /// <summary>
        /// Timer Tick Event that updates time slider positions during playback
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timeSliderTimer_Tick(object sender, EventArgs e)
        {
            if (_mediaSeeking != null)
            {
                try
                {
                    long pos = 0;
                    _mediaSeeking.GetCurrentPosition(out pos);
                    _timeSliderControl.Pos = (int)(pos / 10000);
                }
                catch
                {
                    timeSliderTimer.Enabled = false;
                }
            }
        }

        /// <summary>
        /// Update MediaSeeking based on events from the TimesliderControl
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _timeSliderControl_ValuesChanged(object sender, EventArgs e)
        {
            switch (_timeSliderControl.TrackMode)
            {
                case TimeSliderTrackMode.None:
                    break;
                case TimeSliderTrackMode.StartTime:
                    if (_mediaSeeking != null)
                    {
                        long cur, stop;
                        _mediaSeeking.GetPositions(out cur, out stop);

                        // if the start position is moved past current position, update time
                        if (_timeSliderControl.Min > (int)(cur / 10000))
                        {
                            _mediaSeeking.SetPositions((long)_timeSliderControl.Min * 10000, AMSeekingSeekingFlags.AbsolutePositioning, 0, AMSeekingSeekingFlags.NoPositioning);
                        }
                    }
                    break;
                case TimeSliderTrackMode.StopTime:
                    if (_mediaSeeking != null)
                    {
                        long cur, stop;
                        _mediaSeeking.GetPositions(out cur, out stop);

                        // if the end position is moved before current position, update time
                        if (_timeSliderControl.Max < (int)(cur / 10000))
                        {
                            // rewind to start and set new end time
                            _mediaSeeking.SetPositions((long)_timeSliderControl.Min * 10000, AMSeekingSeekingFlags.AbsolutePositioning, (long)_timeSliderControl.Max * 10000, AMSeekingSeekingFlags.AbsolutePositioning);
                        }
                        else
                        {
                            // just set the end time
                            _mediaSeeking.SetPositions(0, AMSeekingSeekingFlags.NoPositioning, (long)_timeSliderControl.Max * 10000, AMSeekingSeekingFlags.AbsolutePositioning);
                        }
                    }
                    break;
                case TimeSliderTrackMode.CurrentTime:
                    if (_mediaSeeking != null)
                    {
                        _mediaSeeking.SetPositions((long)_timeSliderControl.Pos * 10000, AMSeekingSeekingFlags.AbsolutePositioning, 0, AMSeekingSeekingFlags.NoPositioning);
                    }
                    break;
                default:
                    break;
            }
        }

        #endregion

        #region Menus and Menu Handlers

        private void _optionsDropDownButton_DropDownOpening(object sender, EventArgs e)
        {
            // build the Pin Placement menu items
            pinPlacementToolStripMenuItem.DropDown.Items.Clear();
            for (int i = 0; i < 3; i++)
            {
                ToolStripMenuItem pinPlacementMenuItem = new ToolStripMenuItem(((DaggerNodePinPlacement)i).ToString());
                pinPlacementMenuItem.Tag = i;
                pinPlacementMenuItem.Click += new EventHandler(pinPlacementMenuItem_Click);
                if (i == (int)_pinPlacement) pinPlacementMenuItem.Checked = true;
                pinPlacementToolStripMenuItem.DropDown.Items.Add(pinPlacementMenuItem);
            }

            // set the check state of toggle items
            dropShadowToolStripMenuItem.Checked = this.dsDaggerUIGraph1.DropShadowVisible;
            showPinNamesToolStripMenuItem.Checked = this.dsDaggerUIGraph1.ShowPinNames;
            modalPropertiesToolStripMenuItem.Checked = this.dsDaggerUIGraph1.ModalProperties;
            useIntelligentConnectToolStripMenuItem.Checked = _connectIntelligent;
            _useClockToolStripMenuItem.Checked = this._useReferenceClock;
            _timeSliderVisibleMenuItem.Text = _timeSliderControl.Visible ? "Hide Time Slider" : "Show Time Slider";
        }

        private void _noodleStyleButton_DropDownOpening(object sender, EventArgs e)
        {
            // build the noodle-style menu items
            _noodleStyleButton.DropDown.Items.Clear();
            for (int i = 0; i < 6; i++)
            {
                ToolStripMenuItem nsMenuItem = new ToolStripMenuItem(((NoodleStyle)i).ToString());
                nsMenuItem.Tag = i;
                nsMenuItem.Click += new EventHandler(nsMenuItem_Click);
                if (i == (int)dsDaggerUIGraph1.NoodleStyle) nsMenuItem.Checked = true;
                _noodleStyleButton.DropDown.Items.Add(nsMenuItem);
            }
        }

        /// <summary>
        /// the user toggled the Time Slider Visible menu item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _timeSliderVisibleMenuItem_Click(object sender, EventArgs e)
        {
            _timeSliderControl.Visible = !_timeSliderControl.Visible;
        }

        // the user selected a new pin placement style
        void pinPlacementMenuItem_Click(object sender, EventArgs e)
        {
            PinPlacement = (DaggerNodePinPlacement)(sender as ToolStripMenuItem).Tag;
        }

        // the user selected a new noodle style
        void nsMenuItem_Click(object sender, EventArgs e)
        {
            dsDaggerUIGraph1.NoodleStyle = (NoodleStyle)(sender as ToolStripMenuItem).Tag;
        }

        /// <summary>
        /// Toggle Drop Shadow
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void dropShadowToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.dsDaggerUIGraph1.DropShadowVisible = dropShadowToolStripMenuItem.Checked;
        }

        /// <summary>
        /// Toggle Show Pin Names
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void showPinNamesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.dsDaggerUIGraph1.ShowPinNames = showPinNamesToolStripMenuItem.Checked;
        }

        /// <summary>
        /// Toggle Modal Properties
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void modalPropertiesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.dsDaggerUIGraph1.ModalProperties = modalPropertiesToolStripMenuItem.Checked;
        }

        /// <summary>
        /// Toggle Connect Intelligent
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void useIntelligentConnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            _connectIntelligent = useIntelligentConnectToolStripMenuItem.Checked;
        }

        /// <summary>
        /// Toggle Use Clock
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _useClockToolStripMenuItem_Click(object sender, EventArgs e)
        {
            UseReferenceClock = _useClockToolStripMenuItem.Checked;
        }

        private void _saveGraphFileButton_Click(object sender, EventArgs e)
        {
            SaveFileDialog sfd = new SaveFileDialog();
            sfd.FileName = "Untitled.grf";
            sfd.DefaultExt = "grf";
            sfd.Filter = "Graph Files (*.grf)|*.grf";
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    SaveFilterGraph(sfd.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Error Saving FilterGraph");
                }
            }
        }

        private void _renderMediaFileButton_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                // we don't want to create a new DSGraphEdit panel, just render
                // the media file onto the existing graph
                try
                {
                    // show we're busy rendering and building up the ui for the graph
                    Cursor = Cursors.WaitCursor;

                    int hr = RenderMediaFile(ofd.FileName);
                    if (hr != 0)
                    {
                        MessageBox.Show(DsError.GetErrorText(hr));
                    }
                }
                finally
                {
                    Cursor = Cursors.Default;
                }
            }
            ofd.Dispose();
            ofd = null;
        }

        private void _renderURLButton_Click(object sender, EventArgs e)
        {
            URLDialog ud = new URLDialog();
            if (ud.ShowDialog() == DialogResult.OK)
            {
                // we don't want to create a new DSGraphEdit panel, just render
                // the URL into the existing graph
                try
                {
                    // show we're busy rendering and building up the ui for the graph
                    Cursor = Cursors.WaitCursor;

                    int hr = RenderMediaFile(ud.URL);
                    if (hr != 0)
                    {
                        MessageBox.Show(DsError.GetErrorText(hr));
                    }
                }
                finally
                {
                    Cursor = Cursors.Default;
                }
            }
            ud.Dispose();
            ud = null;
        }

        #endregion
    }
}

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, along with any associated source code and files, is licensed under The MIT License


Written By
Software Developer (Senior)
United States United States
AKA Rich Insley.

I have over 25 years experience in programming, and I'm completely self taught. (Except for one year at California State University Fresno where I had to learn the God awful language Miranda (http://miranda.org.uk/). I've spent 10 years as a Paratrooper in the US Army during the Clinton Administration.

Comments and Discussions