aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/PPC/Tango.PPC.UI/Navigation
ModeNameSize
-rw-r--r--DefaultNavigationManager.cs14983logstatsplain
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DirectShowLib;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;
using System.Collections.Concurrent;
using System.Threading;
using System.Windows.Media;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Interop;
using System.Windows;
using Tango.Video.DirectShow.EventArguments;
using Tango.Video.DirectShow.Delegates;


namespace Tango.Video.DirectShow
{
    public class DirectShowCapture : DependencyObject
    {
        int counter = 0;

        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(DirectShowCapture), new PropertyMetadata(null));

        #region Members
        private IGraphBuilder _graphBuilder;
        private ICaptureGraphBuilder2 _captureGraphBuilder2;
        private SampleGrabberImpl _sampleGrabberImpl;
        private int _captureBitCount;
        private int _captureWidth;
        private int _captureHeight;
        private readonly ConcurrentQueue<WriteableBitmap> _bitmapQueue = new ConcurrentQueue<WriteableBitmap>();
        Thread _bitmapThread = 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 events
        public event FrameReceivedEventDelegate FrameReceived;
        #endregion

        #region Constructor
        public DirectShowCapture()
        {
            IsStarted = false;
            UseFrameSize = true;
            FrameSize = new FrameSize(640, 480);
            UseFrameRate = true;
            FrameRate = 5.0;
            FlipImage = true;
            UseQueue = false;
        }

        #endregion

        #region Methods
        /// <summary>
        /// Returns a list of video input devices.
        /// </summary>
        /// <returns></returns>
        public static List<Device> GetAvailableDevices()
        {
            return DSUtils.GetVideoInputDevices();
        }
        /// <summary>
        /// Opens the DirectShow Capture
        /// </summary>
        public void Start()
        {
            Stop();

            if (VideoDevice == null)
            {
                VideoDevice = GetAvailableDevices().FirstOrDefault();

                if (VideoDevice == null)
                {
                    throw new Exception("No video device found.");
                }
            }

            IBaseFilter videoDevice = null;
            IBaseFilter nullRenderer = null;
            IBaseFilter sampleGrabberFilter = null;
            try
            {
                _graphBuilder = (IGraphBuilder)new FilterGraph();
                _captureGraphBuilder2 = (ICaptureGraphBuilder2)new CaptureGraphBuilder2();

                DsError.ThrowExceptionForHR(_captureGraphBuilder2.SetFiltergraph(_graphBuilder));

                videoDevice = DSUtils.GetVideoInputDeviceFilter(VideoDevice);
                if (videoDevice == null)
                {
                    throw new Exception("Video input device not found.");
                }

                DsError.ThrowExceptionForHR(_graphBuilder.AddFilter(videoDevice, "Video input device"));

                nullRenderer = (IBaseFilter)new NullRenderer();
                DsError.ThrowExceptionForHR(_graphBuilder.AddFilter(nullRenderer, "Null Renderer"));

                sampleGrabberFilter = (IBaseFilter)new SampleGrabber();
                DsError.ThrowExceptionForHR(_graphBuilder.AddFilter(sampleGrabberFilter, "Sample Grabber"));

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

                SetupVideoSource(videoDevice);

                // Connect
                DsError.ThrowExceptionForHR(_captureGraphBuilder2.RenderStream(PinCategory.Capture, MediaType.Video,
                                                                               videoDevice,
                                                                               sampleGrabberFilter, nullRenderer));

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

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

                    _captureBitCount = vih.BmiHeader.BitCount;
                    _captureWidth = vih.BmiHeader.Width;
                    _captureHeight = vih.BmiHeader.Height;

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

                var mc = _graphBuilder as IMediaControl;
                mc.Run();

                IsStarted = true;

                //if (UseQueue)
                //{
                    _bitmapThread = new Thread(new ThreadStart(BitmapThread)) { Name = "Bitmap Thread" };
                    _bitmapThread.IsBackground = true;
                    _bitmapThread.Start();
                //}
            }
            finally
            {
                if (videoDevice != null)
                    Marshal.ReleaseComObject(videoDevice);
                videoDevice = null;

                if (nullRenderer != null)
                    Marshal.ReleaseComObject(nullRenderer);
                nullRenderer = null;

                if (sampleGrabberFilter != null)
                    Marshal.ReleaseComObject(sampleGrabberFilter);
                sampleGrabberFilter = null;
            }

        }
        /// <summary>
        /// Stop directshow capture.
        /// </summary>
        public void Stop()
        {
            if (!IsStarted) return;

            IsStarted = false;
            if (_bitmapThread != null)
            {
                _bitmapThread.Join(5000);
            }
            _bitmapThread = null;

            if (_graphBuilder != null)
            {
                var mc = _graphBuilder as IMediaControl;
                if (mc != null)
                    mc.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("DirectShowCapture. Average copy buffer time: {0}, Frames: {1}", _copyBufferElapsed/(double)_frames, _frames));
            //System.Diagnostics.Debug.WriteLine(string.Format("DirectShowCapture. Average WPF image processing time: {0}, EventNr: {1}", _frameReceivedElapsed / (double)_frameReceivedCount, _frameReceivedCount));

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

            if (_captureGraphBuilder2 != null)
                Marshal.ReleaseComObject(_captureGraphBuilder2);
            _captureGraphBuilder2 = null;

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

            _graphBuilder = null;
            _sampleGrabberImpl = null;

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

        #region Properties
        public bool UseFrameSize { get; set; }
        public FrameSize FrameSize { get; set; }
        public bool UseFrameRate { get; set; }
        public double FrameRate { get; set; }
        public Device VideoDevice { get; set; }
        public bool IsStarted { get; private set; }
        public bool FlipImage { get; set; }
        /// <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; }
        #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 DEBUG
                        var sw = new Stopwatch();
                        sw.Start();
#endif

                        if (FrameReceived != null)
                        {
                            FrameReceived(this, new FrameReceivedEventArgs(bs));
                        }

#if DEBUG
                        sw.Stop();
                        //_frameReceivedElapsed += sw.ElapsedMilliseconds;
                        //_frameReceivedCount++;
#endif
                    }
                }
                else
                {
                    Thread.Sleep(2);
                }
            } while (IsStarted);
        }
        #endregion

        #region Helper Methods
        private void SetupVideoSource(IBaseFilter pVideoSource)
        {
            object oStreamConfig = null;

            int hr = _captureGraphBuilder2.FindInterface(PinCategory.Capture, MediaType.Video, pVideoSource, typeof(IAMStreamConfig).GUID, out oStreamConfig);

            var pStreamConfig = (IAMStreamConfig)oStreamConfig;

            var mt = new AMMediaType();
            pStreamConfig.GetFormat(out mt);

            var vfi = (VideoInfoHeader)Marshal.PtrToStructure(mt.formatPtr, typeof(VideoInfoHeader));
            if (UseFrameSize)
            {
                vfi.BmiHeader.Width = FrameSize.Width;
                vfi.BmiHeader.Height = FrameSize.Height;
            }
            if (UseFrameRate)
            {
                vfi.AvgTimePerFrame = (long)(10000000.0 / FrameRate);
            }
            vfi.BmiHeader.BitCount = 24;
            vfi.BmiHeader.ImageSize = FrameSize.Width * FrameSize.Height * vfi.BmiHeader.BitCount / 8;
            vfi.BmiHeader.Compression = 0;// BI_RGB

            Marshal.StructureToPtr(vfi, mt.formatPtr, false);
            hr = pStreamConfig.SetFormat(mt);
            DsUtils.FreeAMMediaType(mt);
        }
        #endregion
    }
}