aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/MachineStudio/Modules/Tango.MachineStudio.Statistics/Views/ChartsView.xaml
blob: b76154941f5072791017396804b84f4185c9153c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<UserControl x:Class="Tango.MachineStudio.Statistics.Views.ChartsView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:Tango.MachineStudio.Statistics.ViewModels"
             xmlns:global="clr-namespace:Tango.MachineStudio.Statistics"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             xmlns:localrule="clr-namespace:Tango.MachineStudio.Statistics.ValidationRules"
             xmlns:local="clr-namespace:Tango.MachineStudio.Statistics.Views"
             xmlns:tooltips="clr-namespace:Tango.MachineStudio.Statistics.Tooltips"
             xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid IsEnabled="{Binding IsFree}">
        <Grid.RowDefinitions>
            <RowDefinition Height="80"/>
            <RowDefinition Height="337*"/>
        </Grid.RowDefinitions>

        
            <StackPanel Margin="60 20 0 0" VerticalAlignment="Center" HorizontalAlignment="Left" Orientation="Horizontal" DockPanel.Dock="Left">
                <StackPanel>
                    <TextBlock FontSize="10">Start Date:</TextBlock>
                <DatePicker x:Name="startdatePicker" SelectedDateChanged="StartDatePicker_SelectedDateChanged"  materialDesign:HintAssist.Hint="Pick start date" Width="200" VerticalAlignment="Bottom" FontSize="16">
                    <DatePicker.SelectedDate>
                        <Binding Path="StartDate"  UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
                            <Binding.ValidationRules>
                                <localrule:DateExpiredRule x:Name="StartDateExpiredRule"/>
                            </Binding.ValidationRules>
                        </Binding>
                    </DatePicker.SelectedDate>
                </DatePicker>
                </StackPanel>

                <StackPanel Margin="40 0 0 0">
                    <TextBlock FontSize="10">End Date:</TextBlock>
                <DatePicker x:Name="endDatePicker"  materialDesign:HintAssist.Hint="Pick end date" Width="200" VerticalAlignment="Bottom" FontSize="16" SelectedDateChanged="EndDatePicker_SelectedDateChanged">
                    <DatePicker.SelectedDate>
                        <Binding Path="EndDate"  UpdateSourceTrigger="PropertyChanged" NotifyOnValidationError="True">
                            <Binding.ValidationRules>
                                <localrule:DateExpiredRule x:Name="EndDateExpiredRule"/>
                            </Binding.ValidationRules>
                        </Binding>
                    </DatePicker.SelectedDate>
                </DatePicker>
                </StackPanel>
            <Button Width="100"  Margin="60 0 0 0" Command="{Binding LoadJobRunsCommand}" VerticalAlignment="Bottom" HorizontalAlignment="Right" Content="LOAD">
                <Button.Style>
                    <Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
                        <Setter Property="IsEnabled" Value="False"/>
                        <Style.Triggers>
                            <MultiDataTrigger>
                                <MultiDataTrigger.Conditions>
                                    <Condition Binding="{Binding Path=(Validation.HasError), Source={x:Reference startdatePicker}}" Value="False"/>
                                    <Condition Binding="{Binding Path=(Validation.HasError), Source={x:Reference endDatePicker}}" Value="False"/>
                                </MultiDataTrigger.Conditions>
                                <Setter Property="IsEnabled" Value="True"/>
                            </MultiDataTrigger>
                        </Style.Triggers>
                    </Style>
                </Button.Style>
            </Button>
        </StackPanel>
        

        <UniformGrid Columns="3" Margin="50 20 50 50" Rows="2" Grid.Row="1">
            <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10">
                <DockPanel>
                    <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding TimelineJobStatusSeries.Title}"></TextBlock>
                    <lvc:CartesianChart Series="{Binding TimelineJobStatusSeries.SeriesCollection}" LegendLocation="Bottom">
                        <lvc:CartesianChart.DataTooltip>
                            <lvc:DefaultTooltip BulletSize="20" Background="{StaticResource TransparentBackgroundBrush450}" Foreground="{StaticResource Dialog.Foreground}"/>
                        </lvc:CartesianChart.DataTooltip>
                        <lvc:CartesianChart.AxisX>
                            <lvc:Axis Title="{Binding TimelineJobStatusSeries.LabelsTitle}" Labels="{Binding TimelineJobStatusSeries.Labels}" Foreground="{StaticResource DarkGrayBrush200}">
                                <lvc:Axis.Separator>
                                    <lvc:Separator Stroke="#A5A5A5" />
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisX>
                        <lvc:CartesianChart.AxisY>
                            <lvc:Axis Title="{Binding TimelineJobStatusSeries.ChartTitle}" MinValue="0" Foreground="{StaticResource DarkGrayBrush200}" >
                                <lvc:Axis.Separator>
                                    <lvc:Separator Stroke="#B0B0B0" />
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisY>
                    </lvc:CartesianChart>
                </DockPanel>
            </Border>

            <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10">
                <DockPanel>
                    <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding PieJobFailedReasons.Title}"></TextBlock>
                    <UniformGrid Columns="2">
                        <lvc:PieChart DataHover="PieChart_DataHover" Series="{Binding PieJobFailedReasons.SeriesCollection}" LegendLocation="None"  Background="Transparent">
                            <lvc:PieChart.Resources>
                                <Style TargetType="lvc:PieSeries">
                                    <Setter Property="Stroke" Value="#99F9F9F9"></Setter>
                                    <Setter Property="StrokeThickness" Value="2"/>
                                </Style>
                            </lvc:PieChart.Resources>
                            <lvc:PieChart.DataTooltip>
                                <tooltips:PieChartTooltipControl Visibility="Hidden" />
                            </lvc:PieChart.DataTooltip>
                        </lvc:PieChart>

                        <TextBlock x:Name="txtPieTitle" TextWrapping="Wrap"></TextBlock>
                    </UniformGrid>
                </DockPanel>
            </Border>

            <Border BorderBrush="{StaticResource Statistics.BorderBrush}" Padding="5" BorderThickness="1" CornerRadius="5" Margin="10">
                <DockPanel>
                    <TextBlock DockPanel.Dock="Top" FontSize="16" FontWeight="Bold" HorizontalAlignment="Center" Padding="10" Text="{Binding PrintPerWeekSeries.Title}"></TextBlock>
                    <lvc:CartesianChart Series="{Binding PrintPerWeekSeries.SeriesCollection}" LegendLocation="Bottom" >
                        <lvc:CartesianChart.Resources>
                            <Style TargetType="lvc:ColumnSeries">
                                <Setter Property="Foreground" Value="{StaticResource DarkGrayBrush200}"></Setter>
                            </Style>
                        </lvc:CartesianChart.Resources>
                        <lvc:CartesianChart.AxisX>
                            <lvc:Axis Title="{Binding PrintPerWeekSeries.LabelsTitle}" Labels="{Binding PrintPerWeekSeries.Labels}" Foreground="{StaticResource DarkGrayBrush200}">
                                <lvc:Axis.Separator>
                                    <lvc:Separator Stroke="#A5A5A5" />
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisX>
                        <lvc:CartesianChart.AxisY>
                            <lvc:Axis Title="{Binding PrintPerWeekSeries.ChartTitle}" MinValue="0" Foreground="{StaticResource DarkGrayBrush200}" >
                                <lvc:Axis.Separator>
                                    <lvc:Separator Stroke="#6F6F6F" />
                                </lvc:Axis.Separator>
                            </lvc:Axis>
                        </lvc:CartesianChart.AxisY>
                        <lvc:CartesianChart.DataTooltip>
                            <lvc:DefaultTooltip BulletSize="20" Background="{StaticResource TransparentBackgroundBrush450}" Foreground="{StaticResource Dialog.Foreground}"/>
                        </lvc:CartesianChart.DataTooltip>
                    </lvc:CartesianChart>
                </DockPanel>
            </Border>
        </UniformGrid>
    </Grid>
</UserControl>
Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DirectShowLib;
using System.Windows.Media.Imaging;
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Threading;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using Tango.Video.DirectShow.Delegates;
using Tango.Video.DirectShow.EventArguments;

namespace Tango.Video.DirectShow
{
    public class DirectShowPlayer : DependencyObject
    {
        int counter = 0;
        double currentTime;

        public InteropBitmap Source
        {
            get { return (InteropBitmap)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Source.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty SourceProperty =
            DependencyProperty.Register("Source", typeof(InteropBitmap), typeof(DirectShowPlayer), new PropertyMetadata(null));

        #region Members
        private IGraphBuilder _pGraph = null;
        private SampleGrabberImpl _sampleGrabberImpl;
        private readonly ConcurrentQueue<WriteableBitmap> _bitmapQueue = new ConcurrentQueue<WriteableBitmap>();
        Thread _bitmapThread = null;
        private int _totalFrames = 0;
        private double _totalDuration = 0;
        private int _captureBitCount;
        private int _captureWidth;
        private int _captureHeight;
        private double _captureFramerate;
        private System.Timers.Timer _playbackTimer = null;
        private static int TIMER_PERIOD = 50;
        private MsgOnlyWindow _msgFrm = null;
        private IntPtr _mappedFile = IntPtr.Zero;
        private IntPtr _destFrame = IntPtr.Zero;
        private InteropBitmap _myBitmap = null;
#if DEBUG
        //private long _copyBufferElapsed = 0;
        //private long _frames = 0;

        //private long _frameReceivedElapsed = 0;
        //private long _frameReceivedCount = 0;
#endif
        #endregion

        #region Constructor
        public DirectShowPlayer()
        {
            Loop = true;
            EnableAudio = true;
            FlipImage = true;
            State = PlaybackState.NotLoaded;
            _playbackTimer = new System.Timers.Timer(TIMER_PERIOD);
            _playbackTimer.Elapsed += _playbackTimer_Elapsed;
            UseQueue = false;
        }
        #endregion

        #region events
        public event FrameReceivedEventDelegate FrameReceived;
        public event PlayerProgressEventDelegate PlayerProgress;
        #endregion

        #region Methods
        public void Load(string fileName)
        {
            IBaseFilter pSourceFilter = null;
            IBaseFilter pVideoRenderFilter = null;
            IBaseFilter pAudioRenderFilter = null;
            IBaseFilter pSampleGrabberFilter = null;
            IBaseFilter pSplitterFilter = null;
            try
            {
                if (!File.Exists(fileName))
                {
                    throw new Exception(string.Format("File '{0}' does not exists", fileName));
                }

                Close();

                _pGraph = (IGraphBuilder)new FilterGraph();

                int hr = _pGraph.AddSourceFilter(fileName, fileName, out pSourceFilter);
                DsError.ThrowExceptionForHR(hr);

                pVideoRenderFilter = (IBaseFilter)new NullRenderer();
                _pGraph.AddFilter(pVideoRenderFilter, "Null Renderer");

                if (EnableAudio)
                {
                    pAudioRenderFilter = (IBaseFilter)new DSoundRender();
                    _pGraph.AddFilter(pAudioRenderFilter, "Audio Renderer");
                }

                pSampleGrabberFilter = (IBaseFilter)new SampleGrabber();
                _pGraph.AddFilter(pSampleGrabberFilter, "Sample Grabber");

                _sampleGrabberImpl = new SampleGrabberImpl(OnBuffer);
                DSUtils.SetSampleGrabber(pSampleGrabberFilter, _sampleGrabberImpl);


                bool result = DSUtils.ConnectFiltersTryAllPins(_pGraph,
                                                pSourceFilter, pSampleGrabberFilter);
                if (!result)
                {
                    throw new Exception("Failed to connect source filter with sample grabber");
                }

                result = DSUtils.ConnectFiltersTryAllPins(_pGraph,
                                               pSampleGrabberFilter, pVideoRenderFilter);
                if (!result)
                {
                    throw new Exception("Failed to connect sample grabber filter with video renderer");
                }

                if (EnableAudio)
                {
                    result = DSUtils.ConnectFiltersTryAllPins(_pGraph,
                                                  pSourceFilter, pAudioRenderFilter);

                    if (!result)
                    {
                        pSplitterFilter = DSUtils.GetNextConnectedFilter(_pGraph, pSourceFilter);
                        if (pSplitterFilter != null)
                        {
                            result = DSUtils.ConnectFiltersTryAllPins(_pGraph,
                                                      pSplitterFilter, pAudioRenderFilter);
                            if (!result)
                            {
                                // Maybe there's no audio... be silent
                                //throw new Exception("Failed to connect splitter filter with audio renderer");
                            }
                        }
                        else
                        {
                            //  Maybe there's no audio... be silent
                            //throw new Exception("Failed to find splitter filter");
                        }
                    }
                }

                // Now create the frmMsgQue window, from where we will receieve DS messages
                _msgFrm = new MsgOnlyWindow(this);

                var meEx = _pGraph as IMediaEventEx;
                if (meEx != null)
                    meEx.SetNotifyWindow(_msgFrm.Handle, WM_GRAPH_NOTIFY, IntPtr.Zero);


                // Now get the connection media types.
                // Find the video sample grabber input pin and get the media type
                var mtVideo = new AMMediaType();
                IPin videoPin = DsFindPin.ByDirection(pSampleGrabberFilter, PinDirection.Input, 0);
                if (videoPin != null)
                {
                    videoPin.ConnectionMediaType(mtVideo);

                    var vih = (VideoInfoHeader)Marshal.PtrToStructure(mtVideo.formatPtr, typeof(VideoInfoHeader));

                    _captureBitCount = vih.BmiHeader.BitCount;
                    _captureWidth = vih.BmiHeader.Width;
                    _captureHeight = vih.BmiHeader.Height;
                    _captureFramerate = 10000000.0 / vih.AvgTimePerFrame;

                    Marshal.ReleaseComObject(videoPin);
                    DsUtils.FreeAMMediaType(mtVideo);
                }

                IMediaSeeking ms = _pGraph as IMediaSeeking;
                long rtDuration;
                ms.GetDuration(out rtDuration);
                _totalDuration = rtDuration / 10000000.0;
                _totalFrames = (int)(_totalDuration * _captureFramerate);

                if (PlayerProgress != null)
                {
                    PlayerProgress(this, new PlayerProgressEventArgs(0, _totalFrames, 0, _totalDuration));
                }

                State = PlaybackState.Loaded;
            }
            finally
            {
                if (pSourceFilter != null)
                    Marshal.ReleaseComObject(pSourceFilter);
                if (pVideoRenderFilter != null)
                    Marshal.ReleaseComObject(pVideoRenderFilter);
                if (pAudioRenderFilter != null)
                    Marshal.ReleaseComObject(pAudioRenderFilter);
                if (pSampleGrabberFilter != null)
                    Marshal.ReleaseComObject(pSampleGrabberFilter);
                if (pSplitterFilter != null)
                    Marshal.ReleaseComObject(pSplitterFilter);
            }
        }
        public void Play()
        {
            if (State < PlaybackState.Loaded ||
                State == PlaybackState.Running)
                return;

            IMediaControl mc = _pGraph as IMediaControl;

            int hr = mc.Run();
            State = PlaybackState.Running;
            _playbackTimer.Start();

            if (UseQueue)
            {
                _bitmapThread = new Thread(new ThreadStart(BitmapThread)) { Name = "Bitmap Thread" };
                _bitmapThread.Start();
            }
        }
        public void Pause()
        {
            if (State < PlaybackState.Loaded) return;

            IMediaControl mc = _pGraph as IMediaControl;

            int hr = mc.Pause();
            State = PlaybackState.Paused;
            _playbackTimer.Stop();
        }
        public void Stop()
        {
            if (State < PlaybackState.Loaded) return;

            IMediaControl mc = _pGraph as IMediaControl;

            int hr = mc.Stop();
            State = PlaybackState.Paused;
            _playbackTimer.Stop();

            if (!UseQueue)
            {
                _myBitmap = null;
                Win32Interop.UnmapViewOfFile(_destFrame);
                Win32Interop.CloseHandle(_mappedFile);

                _destFrame = IntPtr.Zero;
                _mappedFile = IntPtr.Zero;
            }
#if DEBUG
            //System.Diagnostics.Debug.WriteLine(string.Format("DirectShowPlayer. Average copy buffer time: {0}, Frames: {1}", _copyBufferElapsed / (double)_frames, _frames));
            //System.Diagnostics.Debug.WriteLine(string.Format("DirectShowPlayer. Average WPF image processing time: {0}, EventNr: {1}", _frameReceivedElapsed / (double)_frameReceivedCount, _frameReceivedCount));

            //_copyBufferElapsed = 0;
            //_frames = 0;
            //_frameReceivedElapsed = 0;
            //_frameReceivedCount = 0;
#endif

            Source = null;
        }
        public void Close()
        {
            Stop();

            if (_msgFrm != null)
            {
                _msgFrm.DestroyHandle();
                _msgFrm = null;
            }
            _msgFrm = null;

            State = PlaybackState.NotLoaded;
            if (_bitmapThread != null)
            {
                _bitmapThread.Join(5000);
            }
            _bitmapThread = null;

            if (_pGraph != null)
            {
                var meEx = _pGraph as IMediaEventEx;
                if (meEx != null)
                    meEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);

                Marshal.ReleaseComObject(_pGraph);
            }

            _pGraph = null;
            _sampleGrabberImpl = null;

            WriteableBitmap bitmapSource;
            while (_bitmapQueue.TryDequeue(out bitmapSource)) ;
        }

        private void Seek(double pos)
        {
            var ms = _pGraph as IMediaSeeking;
            var lCurPos = (long)(pos * 10000000);

            ms.SetPositions(lCurPos, AMSeekingSeekingFlags.AbsolutePositioning, 0,
                            AMSeekingSeekingFlags.NoPositioning);
        }
        #endregion

        #region Properties
        public bool Loop { get; set; }
        public bool EnableAudio { get; set; }
        public bool FlipImage { get; set; }
        public PlaybackState State { get; private set; }
        public double Duration
        {
            get
            {
                return _totalDuration;
            }
        }
        /// <summary>
        /// Sets the current position of the playing stream(s) in seconds
        /// </summary>
        public double CurrentTime
        {
            set
            {
                if (State < PlaybackState.Loaded || _pGraph == null) return;

                Seek(value);
            }
            get
            {
                return currentTime;
            }
        }
        /// <summary>
        /// Sets the current position of the playing stream(s) in frames.
        /// </summary>
        public int CurrentFrame
        {
            set
            {
                if (State < PlaybackState.Loaded || _pGraph == null) return;

                Seek(value / (double)_captureFramerate);
            }
        }
        /// <summary>
        ///  Gets or sets the speed control of the playback. 1.0 is normal playback,
        ///  0.5 is halftime slower and 1.5 is half time faster. negative values are
        ///  not supported.
        /// </summary>
        public double Rate
        {
            get
            {
                if (State < PlaybackState.Loaded || _pGraph == null) return 1.0;

                var ms = _pGraph as IMediaSeeking;

                double rate;
                ms.GetRate(out rate);
                return rate;
            }
            set
            {
                if (State < PlaybackState.Loaded || _pGraph == null) return;

                var ms = _pGraph as IMediaSeeking;

                try
                {
                    DsError.ThrowExceptionForHR(ms.SetRate(value));
                }
                catch { }
            }
        }
        /// <summary>
        /// If true, it will use BitmapSource queue internally and client can get frames
        /// in FrameReceived, if false, FrameReceived is not fires, queue is not used and
        /// the client can grab frames at Source property.
        /// </summary>
        public bool UseQueue { get; set; }

        private bool _paused;

        public bool IsPaused
        {
            get
            {
                if (State == PlaybackState.Paused)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            set
            {
                _paused = value;
                if (_paused)
                {
                    State = PlaybackState.Paused;
                    Pause();
                }
                else
                {
                    State = PlaybackState.Running;
                    Play();
                }
            }
        }

        #endregion

        #region Const Members
        internal const int WM_GRAPH_NOTIFY = 0x8000 + 1;
        #endregion

        #region BufferCallback
        protected int OnBuffer(double SampleTime, IntPtr pBuffer, int BufferLen)
        {
            if (!UseQueue)
            {
                int stride = ((_captureWidth * _captureBitCount / 8) + 3) & ~3;
                if (_destFrame == IntPtr.Zero)
                {
                    _mappedFile = Win32Interop.CreateFileMapping(new IntPtr(-1), IntPtr.Zero, PageAccess.ReadWrite, 0, BufferLen, null);
                    _destFrame = Win32Interop.MapViewOfFile(_mappedFile, FileMapAccess.AllAccess, 0, 0, (uint)BufferLen);

                    this.Dispatcher.Invoke(() =>
                    {
                        _myBitmap = (InteropBitmap)Imaging.CreateBitmapSourceFromMemorySection(_mappedFile, _captureWidth, _captureHeight,
                                                                         System.Windows.Media.PixelFormats.Bgr24, stride, 0);
                        Source = _myBitmap;
                    });

                }
                if (FlipImage)
                {
                    unsafe
                    {
                        var pDest = (byte*)_destFrame;
                        var pSrc = (byte*)pBuffer;
                        pSrc += BufferLen;

                        for (int i = 0; i < _captureHeight; i++)
                        {
                            byte* pDstTmp = pDest + i * stride;
                            byte* pSrcTemp = pSrc - (i + 1) * stride;

                            BitmapHelper.CopyMemory((IntPtr)pDstTmp, (IntPtr)pSrcTemp, (uint)stride);
                        }
                    }
                }
                else
                {
                    BitmapHelper.CopyMemory(_destFrame, pBuffer, (uint)(_captureWidth * _captureHeight * _captureBitCount / 8));
                }

                this.Dispatcher.BeginInvoke(new Action(() =>
                {
                    if (_myBitmap != null)
                    {
                        _myBitmap.Invalidate();
                    }
                }), System.Windows.Threading.DispatcherPriority.Send, null);
            }
            else
            {
                WriteableBitmap bs = BitmapHelper.FromNativePointer(pBuffer, BufferLen, _captureWidth, _captureHeight, _captureBitCount == 24 ? 3 : 4, FlipImage);
                bs.Freeze();
                _bitmapQueue.Enqueue(bs);
            }



            counter++;

            if (counter > 30)
            {
                //GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, blocking: false);
                counter = 0;
            }

            return 0;
        }
        #endregion

        #region BitmapThread
        protected void BitmapThread()
        {
            do
            {
                if (_bitmapQueue.Count > 0)
                {
                    WriteableBitmap bs;
                    if (_bitmapQueue.TryDequeue(out bs))
                    {
                        if (FrameReceived != null)
                        {
                            FrameReceived(this, new FrameReceivedEventArgs(bs));
                        }
                    }
                }
                else
                {
                    Thread.Sleep(5);
                }
            } while (State > PlaybackState.NotLoaded);
        }
        #endregion

        #region Playback Timer
        void _playbackTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (_pGraph != null)
            {
                if (PlayerProgress != null)
                {
                    var ms = _pGraph as IMediaSeeking;
                    long rtCurrent;
                    ms.GetCurrentPosition(out rtCurrent);
                    double curTime = rtCurrent / 10000000.0;
                    currentTime = curTime;
                    var args = new PlayerProgressEventArgs((int)(curTime * _captureFramerate), _totalFrames, curTime, _totalDuration);
                    PlayerProgress(this, args);
                }

            }
        }
        #endregion

        #region Handle Event
        internal void HandleEvent()
        {
            if (State < PlaybackState.Loaded || _pGraph == null) return;

            var pMediaEvent = _pGraph as IMediaEventEx;
            if (pMediaEvent == null) return;

            bool completed = false;
            EventCode evc;
            IntPtr lParam1, lParam2;
            while (pMediaEvent.GetEvent(out evc, out lParam1, out lParam2, 0) == 0)
            {
                switch (evc)
                {
                    case EventCode.Complete:
                        completed = true;
                        break;
                }

                pMediaEvent.FreeEventParams(evc, lParam1, lParam2);
            }

            if (completed)
            {
                if (Loop)
                {
                    CurrentTime = 0;
                }
            }
        }
        #endregion
    }
}