DirectShow. NET получение интерфейса IAMDroppedFrames из filtergraph - PullRequest
1 голос
/ 07 августа 2020

Я использую библиотеку DirectShow. NET в C#, чтобы написать программу, которая захватывает кадры с видеокарты ATI AllInWonder для преобразования старых лент VHS в цифровые. Видеокарта старая и использует порт AGP. У меня только два компьютера с этим портом, и процессор слишком медленный, чтобы захватить видеосигнал и закодировать его в видеофайл. Я взял пример DxLo go из примеров DirectShow. NET и изменил его, чтобы просто выгружать необработанные данные кадра в файл, который будет проанализирован позже.

Мой граф графа состоит из подключенного исходного фильтра в смарт-тройку, которую captureGraphBuilder добавил автоматически. Далее идет sampleGrabber, подключенный к нулевому фильтру. Обратный вызов sampleGrabber просто сбрасывает данные кадра RGB24 в выходной файл.

Я могу записать видеосигнал и преобразовать его в файл AVI. Однако я получаю только частоту около 15 кадров в секунду, хотя у меня установлен фильтр источника на 30 кадров в секунду. Я знаю, что моя карта поддерживает 30 кадров в секунду, так как в исходном программном обеспечении есть такая возможность. Мой процессор загружен примерно на 60%, поэтому я не думаю, что это вызывает проблему.

Я хотел бы использовать интерфейс IAMDroppedFrames в своем фильтре захвата, чтобы узнать, пропускаю ли я кадры или проблема в в другом месте. Я могу получить интерфейс отброшенных кадров и данные из него в моей функции построения графиков, однако, поскольку ссылки на фильтры освобождаются после построения графика, я теряю доступ к интерфейсу отброшенных кадров.

I пытались использовать интерфейсы EnumFilters и EnumPins с разочаровывающими результатами. Я могу успешно получить объекты IBaseFilter для всех моих фильтров, и я могу получить имя моего исходного фильтра «Веб-камера Logitech» (это то, что я использую для тестирования своего кода, чтобы я мог написать код на своей основной машине вместо визуального express 2010 на Windows XP). Когда я запускаю метод EnumPins, а затем вызываю pins.Next в результирующем интерфейсе, я получаю сообщение об ошибке «System.InvalidCastException: 'Невозможно преобразовать объект типа' System .__ ComObject 'в тип' DirectShowLib.IPin '.'« Для некоторых причина, по которой метод pins.Next возвращает ComObject вместо интерфейса IPin. Если я использую тот же самый код в фильтре smart-tee, я могу получить объекты IPin и получить доступ к интерфейсам на входных и выходных контактах.

Итак, в основном я ищу способ получить IAMDroppedFrames считать, пока выполняется мой захват, и единственный известный мне способ сделать это - использовать методы EnumFilters и EnumPins. Однако один фильтр, из которого мне нужно получить контакты, я не могу, но я могу получить доступ к контактам любого из других фильтров.

Как я могу получить текущий счет сброшенных кадры, отображаемые в моей форме во время захвата?

Код для получения ссылок IPin:

IEnumFilters filters = null;
m_FilterGraph.EnumFilters(out filters);
IntPtr numFilters = new IntPtr();
IBaseFilter[] filtersFound = new IBaseFilter[1];
filters.Skip(3);
filters.Next(1, filtersFound, numFilters);
FilterInfo info;
filtersFound[0].QueryFilterInfo(out info);
System.Diagnostics.Debug.WriteLine(info.achName);
IEnumPins pins = null;
filtersFound[0].EnumPins(out pins);
IntPtr numPins = new IntPtr();
IPin[] pinsFound = new IPin[1];
pins.Next(1, pinsFound, numPins);

Полный код:

/****************************************************************************
While the underlying libraries are covered by LGPL, this sample is released 
as public domain.  It is distributed in the hope that it will be useful, but 
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
or FITNESS FOR A PARTICULAR PURPOSE.  
*****************************************************************************/

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;

using DirectShowLib;
using System.ComponentModel;

namespace DxLogo
{
    /// <summary> Summary description for MainForm. </summary>
    internal class Capture : ISampleGrabberCB, IDisposable
    {
        //static long milliseconds = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
        //static bool written = false;
        #region Member variables

        /// <summary> graph builder interface. </summary>
        private IFilterGraph2 m_FilterGraph = null;
        IMediaControl m_mediaCtrl = null;

        /// <summary> Set by async routine when it captures an image </summary>
        private bool m_bRunning = false;

        /// <summary> Dimensions of the image, calculated once in constructor. </summary>
        private int m_videoWidth;
        private int m_videoHeight;
        private int m_stride;

        FileStream file = File.Open("C:\\Users\\Admin\\Desktop\\CapturedFrames\\output", FileMode.OpenOrCreate, FileAccess.Write);

        BitmapData m_bmdLogo = null;
        Bitmap m_Bitmap = null;

#if DEBUG
        // Allow you to "Connect to remote graph" from GraphEdit
        DsROTEntry m_rot = null;
        
#endif

        #endregion

        #region API

        [DllImport("Kernel32.dll", EntryPoint="RtlMoveMemory")]
        private static extern void CopyMemory(IntPtr Destination, IntPtr Source, [MarshalAs(UnmanagedType.U4)] uint Length);

        #endregion

        /// zero based device index, and some device parms, plus the file name to save to
        public Capture(int iDeviceNum, int iFrameRate, int iWidth, int iHeight, string FileName)
        {
            DsDevice[] capDevices;

            // Get the collection of video devices
            capDevices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice);

            if (iDeviceNum + 1 > capDevices.Length)
            {
                throw new Exception("No video capture devices found at that index!");
            }

            try
            {
                // Set up the capture graph
                SetupGraph( capDevices[iDeviceNum], iFrameRate, iWidth, iHeight, FileName);
            }
            catch
            {
                Dispose();
                throw;
            }
        }

        /// <summary> release everything. </summary>
        public void Dispose()
        {
            CloseInterfaces();
            if (m_Bitmap != null)
            {
                m_Bitmap.UnlockBits(m_bmdLogo);
                m_Bitmap = null;
                m_bmdLogo = null;
            }
        }
        // Destructor
        ~Capture()
        {
            CloseInterfaces();
        }


        /// <summary> capture the next image </summary>
        public void Start()
        {
            if (!m_bRunning)
            {
                int hr = m_mediaCtrl.Run();
                DsError.ThrowExceptionForHR( hr );

                m_bRunning = true;
            }
        }
        // Pause the capture graph.
        // Running the graph takes up a lot of resources.  Pause it when it
        // isn't needed.
        public void Pause()
        {
            if (m_bRunning)
            {
                int hr = m_mediaCtrl.Pause();
                DsError.ThrowExceptionForHR( hr );

                m_bRunning = false;
            }
        }

        /// <summary> build the capture graph for grabber. </summary>
        private void SetupGraph(DsDevice dev, int iFrameRate, int iWidth, int iHeight, string FileName)
        {
            int hr;

            ISampleGrabber sampGrabber = null;
            IBaseFilter baseGrabFlt = null;
            IBaseFilter capFilter = null;
            //IBaseFilter muxFilter = null;
            IBaseFilter nullFilter = null;
            IFileSinkFilter fileWriterFilter = null;
            ICaptureGraphBuilder2 capGraph = null;

            // Get the graphbuilder object
            m_FilterGraph = new FilterGraph() as IFilterGraph2;
            m_mediaCtrl = m_FilterGraph as IMediaControl;

#if DEBUG
            m_rot = new DsROTEntry(m_FilterGraph);
#endif
            try
            {
                // Get the ICaptureGraphBuilder2
                capGraph = (ICaptureGraphBuilder2) new CaptureGraphBuilder2();

                // Get the SampleGrabber interface
                sampGrabber = (ISampleGrabber) new SampleGrabber();

                // Start building the graph
                hr = capGraph.SetFiltergraph( m_FilterGraph );
                DsError.ThrowExceptionForHR( hr );

                // Add the video device
                hr = m_FilterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out capFilter);
                DsError.ThrowExceptionForHR( hr );

                baseGrabFlt = (IBaseFilter) sampGrabber;
                ConfigureSampleGrabber(sampGrabber);

                // Add the frame grabber to the graph
                hr = m_FilterGraph.AddFilter( baseGrabFlt, "Ds.NET Grabber" );
                DsError.ThrowExceptionForHR( hr );

                Type type = Type.GetTypeFromCLSID(new Guid("C1F400A4-3F08-11d3-9F0B-006008039E37"));
                nullFilter = (IBaseFilter)Activator.CreateInstance(type);

                hr = m_FilterGraph.AddFilter(nullFilter, "Null Filter");
                DsError.ThrowExceptionForHR(hr);

                
                // If any of the default config items are set
                if (iFrameRate + iHeight + iWidth > 0)
                {
                    SetConfigParms(capGraph, capFilter, iFrameRate, iWidth, iHeight);
                }
                /*
                // Create a filter for the output avi file
                hr = capGraph.SetOutputFileName(MediaSubType.Avi, FileName, out muxFilter, out fileWriterFilter);
                DsError.ThrowExceptionForHR( hr );
                */
                // Connect everything together
                hr = capGraph.RenderStream( PinCategory.Capture, MediaType.Video, capFilter, baseGrabFlt,  nullFilter);
                DsError.ThrowExceptionForHR( hr );


                object droppedFramesObject;
                capGraph.FindInterface(FindDirection.DownstreamOnly, null, capFilter, typeof(IAMDroppedFrames).GUID, out droppedFramesObject);
                IAMDroppedFrames droppedFrames = droppedFramesObject as IAMDroppedFrames;
                int numDropped, numNotDropped;
                droppedFrames.GetNumDropped(out numDropped);
                droppedFrames.GetNumNotDropped(out numNotDropped);
                System.Diagnostics.Debug.WriteLine("Dropped Frames");
                System.Diagnostics.Debug.WriteLine(numDropped);
                System.Diagnostics.Debug.WriteLine(numNotDropped);

                

                // Now that sizes are fixed, store the sizes
                SaveSizeInfo(sampGrabber);
            }
            finally
            {
                if (fileWriterFilter != null)
                {
                    Marshal.ReleaseComObject(fileWriterFilter);
                    fileWriterFilter = null;
                }
                if (nullFilter != null)
                {
                    Marshal.ReleaseComObject(nullFilter);
                    nullFilter = null;
                }
                if (capFilter != null)
                {
                    Marshal.ReleaseComObject(capFilter);
                    capFilter = null;
                }
                if (sampGrabber != null)
                {
                    Marshal.ReleaseComObject(sampGrabber);
                    sampGrabber = null;
                }
            }
        }

        /// <summary> Read and store the properties </summary>
        private void SaveSizeInfo(ISampleGrabber sampGrabber)
        {
            int hr;

            // Get the media type from the SampleGrabber
            AMMediaType media = new AMMediaType();
            hr = sampGrabber.GetConnectedMediaType( media );
            DsError.ThrowExceptionForHR( hr );

            if( (media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero) )
            {
                throw new NotSupportedException( "Unknown Grabber Media Format" );
            }

            // Grab the size info
            VideoInfoHeader videoInfoHeader = (VideoInfoHeader) Marshal.PtrToStructure( media.formatPtr, typeof(VideoInfoHeader) );
            m_videoWidth = videoInfoHeader.BmiHeader.Width;
            m_videoHeight = videoInfoHeader.BmiHeader.Height;
            m_stride = m_videoWidth * (videoInfoHeader.BmiHeader.BitCount / 8);

            DsUtils.FreeAMMediaType(media);
            media = null;
        }
        /// <summary> Set the options on the sample grabber </summary>
        private void ConfigureSampleGrabber(ISampleGrabber sampGrabber)
        {
            int hr;
            AMMediaType media = new AMMediaType();

            // Set the media type to Video/RBG24
            media.majorType = MediaType.Video;
            media.subType = MediaSubType.RGB24;
            media.formatType = FormatType.VideoInfo;
            hr = sampGrabber.SetMediaType( media );
            DsError.ThrowExceptionForHR( hr );

            DsUtils.FreeAMMediaType(media);
            media = null;

            // Configure the samplegrabber callback
            hr = sampGrabber.SetCallback( this, 1 );
            DsError.ThrowExceptionForHR( hr );

        }

        // Set the Framerate, and video size
        private void SetConfigParms(ICaptureGraphBuilder2 capGraph, IBaseFilter capFilter, int iFrameRate, int iWidth, int iHeight)
        {
            int hr;
            object o;
            AMMediaType media;
            IAMStreamConfig videoStreamConfig;
            IAMVideoControl videoControl = capFilter as IAMVideoControl;

            // Find the stream config interface
            hr = capGraph.FindInterface(
                PinCategory.Capture, MediaType.Video, capFilter, typeof(IAMStreamConfig).GUID, out o );

            videoStreamConfig = o as IAMStreamConfig;
            try
            {
                if (videoStreamConfig == null)
                {
                    throw new Exception("Failed to get IAMStreamConfig");
                }

                hr = videoStreamConfig.GetFormat(out media);
                DsError.ThrowExceptionForHR( hr );

                // copy out the videoinfoheader
                VideoInfoHeader v = new VideoInfoHeader();
                Marshal.PtrToStructure( media.formatPtr, v );

                // if overriding the framerate, set the frame rate
                if (iFrameRate > 0)
                {
                    v.AvgTimePerFrame = 10000000 / iFrameRate;
                }

                // if overriding the width, set the width
                if (iWidth > 0)
                {
                    v.BmiHeader.Width = iWidth;
                }

                // if overriding the Height, set the Height
                if (iHeight > 0)
                {
                    v.BmiHeader.Height = iHeight;
                }

                // Copy the media structure back
                Marshal.StructureToPtr( v, media.formatPtr, false );

                // Set the new format
                hr = videoStreamConfig.SetFormat( media );
                DsError.ThrowExceptionForHR( hr );

                DsUtils.FreeAMMediaType(media);
                media = null;

                // Fix upsidedown video
                if (videoControl != null)
                {
                    VideoControlFlags pCapsFlags;

                    IPin pPin = DsFindPin.ByCategory(capFilter, PinCategory.Capture, 0);
                    hr = videoControl.GetCaps(pPin, out pCapsFlags);
                    DsError.ThrowExceptionForHR( hr );

                    if ((pCapsFlags & VideoControlFlags.FlipVertical) > 0)
                    {
                        hr = videoControl.GetMode(pPin, out pCapsFlags);
                        DsError.ThrowExceptionForHR( hr );

                        hr = videoControl.SetMode(pPin, 0);
                    }
                }
            }
            finally
            {
                Marshal.ReleaseComObject(videoStreamConfig);
            }
        }

        /// <summary> Shut down capture </summary>
        private void CloseInterfaces()
        {
            int hr;

            try
            {
                if( m_mediaCtrl != null )
                {
                    // Stop the graph
                    hr = m_mediaCtrl.Stop();
                    m_mediaCtrl = null;
                    m_bRunning = false;
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }

#if DEBUG
            if (m_rot != null)
            {
                m_rot.Dispose();
            }
#endif

            if (m_FilterGraph != null)
            {
                Marshal.ReleaseComObject(m_FilterGraph);
                m_FilterGraph = null;
            }
            GC.Collect();
        }


        /// <summary> sample callback, NOT USED. </summary>
        int ISampleGrabberCB.SampleCB( double SampleTime, IMediaSample pSample )
        {
            Marshal.ReleaseComObject(pSample);
            return 0;
        }

        /// <summary> buffer callback, COULD BE FROM FOREIGN THREAD. </summary>
        int ISampleGrabberCB.BufferCB( double SampleTime, IntPtr pBuffer, int BufferLen )
        {
            //System.Diagnostics.Debug.WriteLine(m_videoWidth);
            //System.Diagnostics.Debug.WriteLine(m_videoHeight);
            Byte[] byteArray = new byte[BufferLen];
            Marshal.Copy(pBuffer, byteArray, 0, BufferLen);
            file.Write(byteArray, 0, BufferLen);




            IEnumFilters filters = null;
            m_FilterGraph.EnumFilters(out filters);
            IntPtr numFilters = new IntPtr();
            IBaseFilter[] filtersFound = new IBaseFilter[1];
            filters.Skip(3);
            filters.Next(1, filtersFound, numFilters);
            FilterInfo info;
            filtersFound[0].QueryFilterInfo(out info);
            System.Diagnostics.Debug.WriteLine(info.achName);
            IEnumPins pins = null;
            filtersFound[0].EnumPins(out pins);
            IntPtr numPins = new IntPtr();
            IPin[] pinsFound = new IPin[1];
            pins.Next(1, pinsFound, numPins);

            return 0;
        }
    }
}

...