aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Video/DirectShowPlayer.cs
blob: 35c09da4293fb02ea2c57680d583ac7be6ddbf77 (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
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Tango.PMR;
using Tango.PMR.Embroidery;

namespace Tango.EmbroideryUI
{
    public static class EmbroideryFileConverter
    {
        [DllImport("Tango.Embroidery.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "ConvertFile")]
        public static extern int ConvertFile(IntPtr data, int size, ref IntPtr output);

        public static void ConvertEmbroideryFile(String sourceFile, String targetFile)
        {
            ConvertFileInput input = new ConvertFileInput();
            input.SourceFile = sourceFile;
            input.TargetFile = targetFile;


            NativePMR<ConvertFileInput, ConvertFileOutput> nativePMR = new NativePMR<ConvertFileInput, ConvertFileOutput>(ConvertFile);
            ConvertFileOutput output = nativePMR.Invoke(input);

            if (!output.Successful)
            {
                throw new IOException("Error occurred in native embroidery library.");
            }
        }
    }
}
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
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
    }
}