<UserControl x:Class="Tango.PPC.Maintenance.Views.MaintenanceView"
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.PPC.Maintenance.ViewModels"
xmlns:global="clr-namespace:Tango.PPC.Maintenance"
xmlns:ifs="clr-namespace:Tango.PMR.IFS;assembly=Tango.PMR"
xmlns:arr="clr-namespace:System.Collections;assembly=mscorlib"
xmlns:touch="clr-namespace:Tango.Touch.Controls;assembly=Tango.Touch"
xmlns:localConverters="clr-namespace:Tango.PPC.Maintenance.Converters"
xmlns:localControls="clr-namespace:Tango.PPC.Maintenance.Controls"
xmlns:enumerations="clr-namespace:Tango.BL.Enumerations;assembly=Tango.BL"
xmlns:local="clr-namespace:Tango.PPC.Maintenance.Views"
mc:Ignorable="d"
d:DesignHeight="1800" d:DesignWidth="800" d:DataContext="{d:DesignInstance Type=vm:MaintenanceViewVM, IsDesignTimeCreatable=False}" DataContext="{x:Static global:ViewModelLocator.MaintenanceViewVM}">
<UserControl.Resources>
<localConverters:StringToFirstLetterConverter x:Key="StringToFirstLetterConverter" />
<localConverters:MidTankLevelToElementHusing 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
}
}