Click here to Skip to main content
15,895,606 members
Articles / Multimedia / Video

VMR9 Allocator Presenter in C# with Direct3D Video Rendering

Rate me:
Please Sign up or sign in to vote.
5.00/5 (10 votes)
10 Jul 2012CPOL5 min read 57.2K   3.3K   16  
Articles describes how to make pure C# rendering video on VMR9 with custom allocator presenter over Direct3D in .NET
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
using System.Threading;
using DirectShow;
using System.Security.Permissions;
using System.Collections.Generic;

namespace Sonic
{
    [SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
    [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
    public class DSFilterGraphBase: COMHelper , IDisposable
    {
        #region Constants

        protected const int WM_GRAPHNOTIFY = 0x00008001;

        #endregion

        #region Helper Classes

        public class HelperForm : System.Windows.Forms.Form
        {
            protected override void WndProc(ref Message m)
            {
                if (m.Msg == WM_GRAPHNOTIFY)
                {
                    try
                    {
                        if (m.LParam != IntPtr.Zero)
                        {
                            DSFilterGraphBase _graph = (DSFilterGraphBase)Marshal.GetObjectForIUnknown(m.LParam);
                            if (_graph != null)
                            {
                                if (_graph.ProcessGraphMessage())
                                {
                                    _graph.Stop();
                                }
                            }
                        }
                    }
                    catch
                    {
                    }
                }
                base.WndProc(ref m);
            }
        }

        #endregion

        #region Variables

        protected Control m_VideoControl = null;
        protected Control m_EventControl = new HelperForm();
        protected IGraphBuilder m_GraphBuilder = null;
        protected IMediaControl m_MediaControl = null;
        protected IMediaSeeking m_MediaSeeking = null;
        protected IMediaEventEx m_MediaEventEx = null;
        protected IVideoWindow m_VideoWindow = null;
        protected IBasicAudio m_BasicAudio = null;
        protected IBasicVideo m_BasicVideo = null;
        protected IVideoFrameStep m_FrameStep = null;

        protected bool m_bMute = false;
        protected int m_iVolume = 0;
        protected double m_dRate = 1.0;
        protected bool m_bShouldPreview = true;

        protected List<DSFilter> m_Filters = new List<DSFilter>();

        #endregion

        #region Constructor

        public DSFilterGraphBase()
        {
            
        }

        ~DSFilterGraphBase()
        {
            Dispose();
        }

        #endregion

        #region Properties

        public bool IsRunning
        {
            get
            {
                if (m_MediaControl != null)
                {
                    FilterState _state;
                    int hr = 0;
                    do
                    {
                        hr = m_MediaControl.GetState(200, out _state);
                    }
                    while (hr == 0x00040237 || hr == 0x00040268);
                    if (hr == 0)
                    {
                        return _state == FilterState.Running;
                    }
                }
                return false;
            }
        }

        public bool IsPaused
        {
            get
            {
                if (m_MediaControl != null)
                {
                    FilterState _state;
                    int hr = 0;
                    do
                    {
                        hr = m_MediaControl.GetState(200, out _state);
                    }
                    while (hr == 0x00040237 || hr == 0x00040268);
                    if (hr == 0)
                    {
                        return _state == FilterState.Paused;
                    }
                }
                return false;
            }
        }

        public bool IsStopped
        {
            get
            {
                if (m_MediaControl != null)
                {
                    FilterState _state;
                    int hr = 0;
                    do
                    {
                        hr = m_MediaControl.GetState(200, out _state);
                    }
                    while (hr == 0x00040237 || hr == 0x00040268);
                    if (hr == 0)
                    {
                        return _state == FilterState.Stopped;
                    }
                }
                return true;
            }
        }

        public long Position
        {
            get
            {
                if (m_MediaSeeking != null)
                {
                    long _time;
                    int hr = m_MediaSeeking.GetCurrentPosition(out _time);
                    Debug.Assert(hr == 0);
                    if (hr == 0) return _time;
                }
                return -1;
            }
            set
            {
                if (m_MediaSeeking != null)
                {
                    DsLong _stop = (long)0;
                    DsLong _current = value;
                    int hr = m_MediaSeeking.SetPositions(_current, AMSeekingSeekingFlags.AbsolutePositioning, _stop, AMSeekingSeekingFlags.NoPositioning);
                    Debug.Assert(hr >= 0);
                    if (OnPositionChange != null) OnPositionChange(this, EventArgs.Empty);
                }
            }
        }

        public long Duration
        {
            get
            {
                if (m_MediaSeeking != null)
                {
                    long _time;
                    int hr = m_MediaSeeking.GetDuration(out _time);
                    Debug.Assert(hr == 0);
                    if (hr == 0) return _time;
                }
                return -1;
            }
        }

        public int Volume
        {
            get { return m_iVolume; }
            set
            {
                m_iVolume = value;
                if (!m_bMute)
                {
                    SetVolume(value);
                }
            }
        }

        public bool Mute
        {
            get { return m_bMute; }
            set
            {
                if (m_bMute != value)
                {
                    m_bMute = value;
                    SetVolume(m_bMute ? -10000 : m_iVolume);
                }
            }
        }

        public double Rate
        {
            get
            {
                return m_dRate;
            }
            set
            {
                if (value > 0 && value <= 2.0)
                {
                    if (m_MediaSeeking != null)
                    {
                        int hr = m_MediaSeeking.SetRate(value);
                        if (hr == 0)
                        {
                            m_dRate = value;
                        }
                    }
                    else
                    {
                        m_dRate = value;
                    }
                }
            }
        }

        public Control VideoControl
        {
            get { return m_VideoControl; }
            set 
            {
                if (m_VideoControl != value)
                {
                    m_VideoControl = value;
                    m_VideoControl.Resize += new EventHandler(VideoControl_Resize);
                    m_VideoControl.VisibleChanged += new EventHandler(VideoControl_VisibleChanged);
                }
            }
        }

        public bool Visible
        {
            get
            {
                bool bVisible = ((m_VideoControl != null) ? m_VideoControl.Visible : false);
                return m_bShouldPreview || bVisible;
            }
            set
            {
                if (m_bShouldPreview != value)
                {
                    m_bShouldPreview = value;
                    if (m_VideoWindow != null)
                    {
                        m_VideoWindow.put_AutoShow(m_bShouldPreview ? -1 : 0);
                        m_VideoWindow.put_Visible(m_bShouldPreview ? -1 : 0);
                    }
                }
            }
        }

        public List<DSFilter> Filters
        {
            get 
            {
                if (m_MediaControl == null)
                {
                    while (m_Filters.Count > 0)
                    {
                        DSFilter _filter = m_Filters[0];
                        m_Filters.RemoveAt(0);
                        _filter.Dispose();
                    }
                    if (m_GraphBuilder != null)
                    {
                        IEnumFilters pEnum;
                        if (SUCCEEDED(m_GraphBuilder.EnumFilters(out pEnum)))
                        {
                            IBaseFilter[] aFilters = new IBaseFilter[1];
                            while (S_OK == pEnum.Next(1, aFilters, IntPtr.Zero))
                            {
                                m_Filters.Add(new DSFilter(aFilters[0]));
                            }
                            Marshal.ReleaseComObject(pEnum);
                        }
                    }
                }
                return m_Filters; 
            }
        }

        public int Count
        {
            get { return Filters.Count; }
        }

        public DSFilter AudioRenderer
        {
            get 
            {
                List<DSFilter> _filters = Filters;
                foreach (DSFilter _filter in _filters)
                {
                    if (_filter.IsSupported(typeof(IBasicAudio).GUID))
                    {
                        return _filter;
                    }
                }
                return null; 
            }
        }

        public DSFilter VideoRenderer
        {
            get 
            {
                List<DSFilter> _filters = Filters;
                foreach (DSFilter _filter in _filters)
                {
                    if (_filter.IsSupported(typeof(IVideoWindow)))
                    {
                        return _filter;
                    }
                }
                return null;
            }
        }

        public DSFilter this[int index]
        {
            get 
            {
                try
                {
                    return Filters[index];
                }
                catch
                {
                    return null;
                }
            }
        }

        public DSFilter this[string _name]
        {
            get 
            {
                List<DSFilter> _filters = Filters;
                foreach (DSFilter _filter in _filters)
                {
                    if (_filter.Name == _name)
                    {
                        return _filter;
                    }
                }
                return null; 
            }
        }

        public bool IsAudioSupported
        {
            get 
            { 
                return ((AudioRenderer != null) && AudioRenderer == true);
            }
        }

        #endregion

        #region Events

        public event EventHandler OnPlaybackPrepared;
        public event EventHandler OnPlaybackStart;
        public event EventHandler OnPlaybackStop;
        public event EventHandler OnPlaybackPause;
        public event EventHandler OnPositionChange;
        public event EventHandler OnPlaybackReady;

        #endregion

        #region Public Methods

        public virtual HRESULT Start()
        {
            int hr = 0;
            if (m_MediaControl == null)
            {
                hr = Load();
                if (hr != 0) return (HRESULT)hr;
            }
            hr = 1;
            while (hr == 1)
            {
                hr = m_MediaControl.Run();
                if (hr == 1) Thread.Sleep(50);
            }
            if (hr == 0)
            {
                if (OnPlaybackStart != null) OnPlaybackStart(this, EventArgs.Empty);
            }
            return (HRESULT)hr;
        }

        public virtual HRESULT Pause()
        {
            int hr = 0;
            if (m_MediaControl == null)
            {
                hr = Load();
                if (hr != 0) return (HRESULT)hr;
            }
            if (!IsPaused)
            {
                hr = m_MediaControl.Pause();
            }
            else
            {
                return Start();
            }
            if (hr >= 0)
            {
                if (OnPlaybackPause != null) OnPlaybackPause(this, EventArgs.Empty);
            }
            return (HRESULT)hr;
        }

        public virtual HRESULT Stop()
        {
            if (m_MediaControl == null) return (HRESULT)E_POINTER;
            m_MediaControl.Stop();
            if (OnPlaybackStop != null) OnPlaybackStop(this, EventArgs.Empty);
            return Unload();
        }

        public virtual HRESULT StepForward()
        {
            if (m_FrameStep != null)
            {
                if (!IsPaused)
                {
                    Pause();
                }
                int hr = m_FrameStep.Step(1, null);
                if (hr < 0)
                {
                    long _time;
                    hr = m_MediaSeeking.GetCurrentPosition(out _time);
                    DsLong _stop = (long)0;
                    TimeSpan _ts = new TimeSpan(0, 0, 1);
                    _time += _ts.Ticks / 20;
                    DsLong _current = _time;
                    hr = m_MediaSeeking.SetPositions(_current, AMSeekingSeekingFlags.AbsolutePositioning, _stop, AMSeekingSeekingFlags.NoPositioning);
                }
                return (HRESULT)hr;
            }
            return (HRESULT)E_POINTER;
        }

        public virtual HRESULT StepBackward()
        {
            if (m_FrameStep != null)
            {
                if (!IsPaused)
                {
                    Pause();
                }
                long _time;
                int hr = m_MediaSeeking.GetCurrentPosition(out _time);
                DsLong _stop = (long)0;
                TimeSpan _ts = new TimeSpan(0, 0, 1);
                _time -= _ts.Ticks / 20;
                if (_time < 0) _time = 0;
                DsLong _current = _time;
                hr = m_MediaSeeking.SetPositions(_current, AMSeekingSeekingFlags.AbsolutePositioning, _stop, AMSeekingSeekingFlags.NoPositioning);
                return (HRESULT)hr;
            }
            return (HRESULT)E_POINTER;
        }

        #endregion

        #region Private Methods

        private void SetVolume(int _volume)
        {
            if (m_BasicAudio == null)
            {
                if (m_GraphBuilder == null)
                {
                    return;
                }
                else
                {
                    m_BasicAudio = (IBasicAudio)m_GraphBuilder;
                }
            }
            m_BasicAudio.put_Volume(_volume);
        }

        private HRESULT InitInterfaces()
        {
            CloseInterfaces();
            HRESULT hr = E_FAIL;
            try
            {
                m_GraphBuilder = (IGraphBuilder)new FilterGraph();
                hr = OnInitInterfaces();
                hr.Throw();
                return PreparePlayback();
            }
            catch (Exception _exception)
            {
                if (_exception is COMException)
                {
                    hr = (HRESULT)((COMException)_exception).ErrorCode;
                }
                else
                {
                    hr = E_UNEXPECTED;
                }
            }
            finally
            {
                if (hr.Succeeded)
                {
                    while (m_Filters.Count > 0)
                    {
                        DSFilter _filter = m_Filters[0];
                        m_Filters.RemoveAt(0);
                        _filter.Dispose();
                    }
                    IEnumFilters pEnum;
                    if (SUCCEEDED(m_GraphBuilder.EnumFilters(out pEnum)))
                    {
                        IBaseFilter[] aFilters = new IBaseFilter[1];
                        while(S_OK == pEnum.Next(1,aFilters,IntPtr.Zero))
                        {
                            m_Filters.Add(new DSFilter(aFilters[0]));
                        }
                        Marshal.ReleaseComObject(pEnum);
                    }
                }
            }
            CloseInterfaces();
            return hr;
        }

        private HRESULT CloseInterfaces()
        {
            try
            {
                OnCloseInterfaces();
                if (m_MediaEventEx != null)
                {
                    m_MediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);
                    m_MediaEventEx = null;
                }
                if (m_VideoWindow != null)
                {
                    m_VideoWindow.put_Visible(0);
                    m_VideoWindow.put_Owner(IntPtr.Zero);
                    m_VideoWindow = null;
                }
                m_MediaSeeking = null;
                m_BasicVideo = null;
                m_BasicAudio = null;
                m_MediaControl = null;
                while (m_Filters.Count > 0)
                {
                    DSFilter _filter = m_Filters[0];
                    m_Filters.RemoveAt(0);
                    _filter.Dispose();
                }
                if (m_GraphBuilder != null)
                {
                    Marshal.ReleaseComObject(m_GraphBuilder);
                    m_GraphBuilder = null;
                }
                GC.Collect();
                return (HRESULT)NOERROR;
            }
            catch
            {
                return (HRESULT)E_FAIL;
            }
        }

        #endregion

        #region Protected Methods

        protected virtual HRESULT Load()
        {
            HRESULT hr = InitInterfaces();
            if (hr.Succeeded)
            {
                if (OnPlaybackPrepared != null) OnPlaybackPrepared(this,EventArgs.Empty);
            }
            return hr;
        }

        protected virtual HRESULT Unload()
        {
            return CloseInterfaces();
        }

        protected virtual HRESULT OnInitInterfaces()
        {
            return (HRESULT)NOERROR;
        }

        protected virtual HRESULT OnCloseInterfaces()
        {
            return (HRESULT)NOERROR;
        }

        protected virtual void SettingUpVideoWindow()
        {
            if (m_VideoWindow != null)
            {
                if (m_VideoControl != null)
                {
                    m_VideoWindow.put_Owner(m_VideoControl.Handle);
                    m_VideoWindow.put_MessageDrain(m_VideoControl.Handle);
                    m_VideoWindow.put_WindowStyle(0x40000000 | 0x04000000);
                    ResizeVideoWindow();
                }
                m_VideoWindow.put_AutoShow(Visible ? -1 : 0);
                m_VideoWindow.put_Visible(Visible ? -1 : 0);
            }
        }

        protected virtual HRESULT PreparePlayback()
        {
            m_MediaControl = (IMediaControl)m_GraphBuilder;
            m_BasicVideo = (IBasicVideo)m_GraphBuilder;
            m_MediaSeeking = (IMediaSeeking)m_GraphBuilder;
            m_VideoWindow = (IVideoWindow)m_GraphBuilder;
            m_MediaEventEx = (IMediaEventEx)m_GraphBuilder;
            m_FrameStep = (IVideoFrameStep)m_GraphBuilder;
            SettingUpVideoWindow();
            int hr = m_MediaEventEx.SetNotifyWindow(m_EventControl.Handle, WM_GRAPHNOTIFY, Marshal.GetIUnknownForObject(this));
            Debug.Assert(hr == 0);
            SetVolume(m_bMute ? -10000 : m_iVolume);
            if (m_dRate != 1.0)
            {
                m_MediaSeeking.SetRate(m_dRate);
                m_MediaSeeking.GetRate(out m_dRate);
            }
            if (OnPlaybackReady != null) OnPlaybackReady(this, EventArgs.Empty);
            return (HRESULT)hr;
        }

        protected virtual bool ProcessGraphMessage()
        {
            if (m_MediaEventEx != null)
            {
                int hr = 0;
                int _param1, _param2;
                DsEvCode _code;
                while (hr == 0)
                {
                    hr = m_MediaEventEx.GetEvent(out _code, out _param1, out _param2, 20);
                    if (hr == 0)
                    {
                        hr = m_MediaEventEx.FreeEventParams(_code, _param1, _param2);

                        if (_code == DsEvCode.Complete)
                        {
                            return true;
                        }
                        if (_code == DsEvCode.DeviceLost)
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        protected virtual void ResizeVideoWindow()
        {
            if (m_VideoWindow != null && m_VideoControl != null)
            {
                m_VideoWindow.SetWindowPosition(0, 0, m_VideoControl.Width, m_VideoControl.Height);
            }
        }

        #endregion

        #region Overridden Methods

        private void VideoControl_VisibleChanged(object sender, EventArgs e)
        {
            if (m_VideoWindow != null && m_VideoControl != null && m_bShouldPreview)
            {
                m_VideoWindow.put_AutoShow(m_VideoControl.Visible ? -1 : 0);
                m_VideoWindow.put_Visible(m_VideoControl.Visible ? -1 : 0);
            }
        }

        private void VideoControl_Resize(object sender, EventArgs e)
        {
            ResizeVideoWindow();
        }

        #endregion
    
        #region IDisposable Members

        public virtual void  Dispose()
        {
 	        CloseInterfaces();
        }

        #endregion
    }

    public interface ISourceFileSupport
    {
        string FileName { get; set; }
        HRESULT Open();
        HRESULT Open(bool bStart);
        HRESULT Open(string sFileName);
        HRESULT Open(string sFileName, bool bStart);
    }

    public interface IFileDestSupport
    {
        string OutputFileName { get; set; }
        HRESULT Save();
        HRESULT Save(bool bStart);
        HRESULT Save(string sFileName);
        HRESULT Save(string sFileName, bool bStart);
    }

    public class DSFilePlayback : DSFilterGraphBase, ISourceFileSupport
    {
        #region Variables

        protected string m_sFileName = "";

        #endregion

        #region Properties

        public string FileName
        {
            get { return m_sFileName; }
            set { Open(value); }
        }

        #endregion

        #region Public Methods

        public HRESULT Open()
        {
            return Open(true);
        }

        public HRESULT Open(bool bStart)
        {
            return Open(m_sFileName, bStart);
        }

        public HRESULT Open(string sFileName)
        {
            return Open(sFileName, true);
        }

        public HRESULT Open(string sFileName, bool bStart)
        {
            if (sFileName != null && sFileName != "")
            {
                m_sFileName = sFileName;
                if (bStart)
                {
                    return Start();
                }
                else
                {
                    return Load();
                }
            }
            return E_POINTER;
        }

        #endregion

        #region Overridden Methods

        protected override HRESULT OnInitInterfaces()
        {
            int hr = m_GraphBuilder.RenderFile(m_sFileName, null);
            if (hr < 0) Marshal.ThrowExceptionForHR(hr);
            return (HRESULT)hr;
        }

        #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 Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Russian Federation Russian Federation
I'm a professional multimedia developer (more than 10 years) in any kind of applications and technologies related to it, such as DirectShow, Direct3D, WinMM, OpenGL, MediaFoundation, WASAPI, Windows Media and other including drivers development of Kernel Streaming, Audio/Video capture drivers and audio effects. Have experience in following languages: C, C++, C#, delphi, C++ builder, VB and VB.NET. Strong knowledge in math and networking.

Comments and Discussions