How to capture audio and Video using DirectShow NET?

Lord_JABA picture Lord_JABA · Oct 30, 2011 · Viewed 7.8k times · Source

I'm trying to learn C# net. Cause I already know OO PHP some Java and C++ (which i never really used because of manual memory handling) I was thinking I'm going to start with something useful instead of another book full of artificial problems. I was thinking about simple house monitoring.
I do some googling and grabbed DirectShowNET. Compiled it course I'm on win7 64b. Compiled CaptureWMV sample, look at the code and for 12 hour tried to add audio capture to it, but it always crush when I try to use not video only codec. I googled for working example but couldn't find any.(audio only-yes, video only-yes, but both-nowhere) Here is my slightly modified code. I'm for sure doing something stupid, but will be very glad if someone point me in the right direction.

Take close look at commented code near

(...)AddSourceFilterForMoniker(devAudio.Mon(...)

This is wrong i know but how to do it right?

using System;
using System.Runtime.InteropServices;
using DirectShowLib;

namespace AsfFilter
{
    internal class Capture: IDisposable
    {
        #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;
#if DEBUG
        DsROTEntry m_rot = null;
#endif

        #endregion

        /// <summary> release everything. </summary>
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            CloseInterfaces();
        }

        ~Capture()
        {
            Dispose();
        }

        /// <summary>
        /// Create capture object
        /// </summary>
        /// <param name="iDeviceNum">Zero based index of capture device</param>
        /// <param name="szFileName">Output ASF file name</param>
        public Capture(int iDeviceNum, int iAudioNum,string szOutputFileName)
        {
            DsDevice [] capDevices;
            DsDevice [] audioCapDevices;

            // Get the collection of video devices
            capDevices = DsDevice.GetDevicesOfCat( FilterCategory.VideoInputDevice );
            audioCapDevices = DsDevice.GetDevicesOfCat(FilterCategory.AudioInputDevice);
            if (iDeviceNum + 1 > capDevices.Length)
            {
                throw new Exception("No video capture devices found at that index!");
            }
            if (iAudioNum + 1 > audioCapDevices.Length)
            {
                throw new Exception("No audio capture devices found at that index!");
            }
            try
            {
                // Set up the capture graph
                SetupGraph( capDevices[iDeviceNum], audioCapDevices[iDeviceNum], szOutputFileName);
                m_bRunning = false;
            }
            catch
            {
                Dispose();
                throw;
            }
        }

        // Start the capture graph
        public void Start()
        {
            if (!m_bRunning)
            {
                int hr = m_mediaCtrl.Run();
                Marshal.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)
            {
                IMediaControl mediaCtrl = m_FilterGraph as IMediaControl;
                int hr = mediaCtrl.Pause();
                Marshal.ThrowExceptionForHR( hr );
                m_bRunning = false;
            }
        }

        /// <summary> build the capture graph. </summary>
        private void SetupGraph(DsDevice dev, DsDevice devAudio, string szOutputFileName)
        {
            int hr;
            IBaseFilter capFilter = null;
            IBaseFilter asfWriter = null;
            IBaseFilter audFilter = null;
            ICaptureGraphBuilder2 capGraph = null;

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

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

                // Add the capture device to the graph
                //hr = m_FilterGraph.AddSourceFilterForMoniker(devAudio.Mon, null, devAudio.Name, out capFilter);
                //Marshal.ThrowExceptionForHR(hr);
                hr = m_FilterGraph.AddSourceFilterForMoniker(dev.Mon, null, dev.Name, out capFilter);
                Marshal.ThrowExceptionForHR( hr );
                asfWriter = ConfigAsf(capGraph, szOutputFileName);
                hr = capGraph.RenderStream(null, null, capFilter, null, asfWriter);
                Marshal.ThrowExceptionForHR( hr );
                m_mediaCtrl = m_FilterGraph as IMediaControl;
            }
            finally
            {
                if (capFilter != null)
                {
                    Marshal.ReleaseComObject(capFilter);
                    capFilter = null;
                }
                if (asfWriter != null)
                {
                    Marshal.ReleaseComObject(asfWriter);
                    asfWriter = null;
                }
                if (capGraph != null)
                {
                    Marshal.ReleaseComObject(capGraph);
                    capGraph = null;
                }
            }
        }

        private IBaseFilter ConfigAsf(ICaptureGraphBuilder2 capGraph, string szOutputFileName)
        {
            IFileSinkFilter pTmpSink = null;
            IBaseFilter asfWriter = null;
            int hr = capGraph.SetOutputFileName( MediaSubType.Asf, szOutputFileName, out asfWriter, out pTmpSink);
            Marshal.ThrowExceptionForHR( hr );

            try
            {
                IConfigAsfWriter lConfig = asfWriter as IConfigAsfWriter;

                // Windows Media Video 8 for Dial-up Modem (No audio, 56 Kbps)
                // READ THE README for info about using guids
                //WMProfile_V80_56VideoOnly
                Guid cat = new Guid(0x6e2a6955, 0x81df, 0x4943, 0xba, 0x50, 0x68, 0xa9, 0x86, 0xa7, 0x8, 0xf6);
                //WMProfile_V80_BESTVBRVideo
                Guid bat = new Guid(0x48439ba, 0x309c, 0x440e, 0x9c, 0xb4, 0x3d, 0xcc, 0xa3, 0x75, 0x64, 0x23);
                //WMProfile_V80_288VideoOnly
                Guid vot = new Guid(0x8c45b4c7, 0x4aeb, 0x4f78, 0xa5, 0xec, 0x88, 0x42, 0xb, 0x9d, 0xad, 0xef);
                //WMProfile_V80_56Video
                Guid vau = new Guid(0x254E8A96, 0x2612, 0x405C, 0x80, 0x39, 0xf0, 0xBF, 0x72, 0x5C, 0xED, 0x7D);
                //WMProfile_V80_288MonoAudio
                Guid aon = new Guid(0x7ea3126d, 0xe1ba, 0x4716, 0x89, 0xaf, 0xf6, 0x5c, 0xee, 0xc, 0xc, 0x67);

                hr = lConfig.ConfigureFilterUsingProfileGuid(cat);
                //hr = lConfig.ConfigureFilterUsingProfileGuid(W288Video);
                Marshal.ThrowExceptionForHR( hr );
            }
            finally
            {
                Marshal.ReleaseComObject(pTmpSink);
            }
            return asfWriter;
        }

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

            try
            {
                if( m_mediaCtrl != null )
                {
                    // Stop the graph
                    hr = m_mediaCtrl.Stop();
                    m_bRunning = false;
                }
            }
            catch {}
#if DEBUG
            // Remove graph from the ROT
            if ( m_rot != null )
            {
                m_rot.Dispose();
                m_rot = null;
            }
#endif
            if (m_FilterGraph != null)
            {
                Marshal.ReleaseComObject(m_FilterGraph);
                m_FilterGraph = null;
            }
        }
    }
}

I don't want to use c++ code for this because of memory management and DirectShowNet docs are very... minimalistic. And I can't really understand the idea behind "pin's". Maybe I must completely resign from using ICaptureGraphBuilder2 and try to do it hard way.

THIS IS MY FIRST ENCOUNTER WITH C# - PLEASE REMEMBER THIS!

Answer

Ray picture Ray · May 29, 2013

try this. you can add the sourceFilt and aufilter to the graphbuilder, by using graphbuilder.addFilter(aufilter,"Name Here") graphbuilder.addFilter(sourceFilt,"Name Here"), and forget to mention sourceFilt and aufilter is an IBaseFilter

Dim classEnum As IEnumMoniker = Nothing
Dim moniker As IMoniker() = New IMoniker(0) {}
Dim devEnum As ICreateDevEnum = CType(New CreateDevEnum, ICreateDevEnum)
hr = devEnum.CreateClassEnumerator(FilterCategory.VideoInputDevice, classEnum, 0)
Marshal.ReleaseComObject(devEnum)
If classEnum.Next(moniker.Length, moniker, IntPtr.Zero) = 0 Then
    Dim iid As Guid = GetType(IBaseFilter).GUID
    moniker(0).BindToObject(Nothing, Nothing, iid, source)
End If
sourceFilt = CType(source, IBaseFilter)

Dim classEnum2 As IEnumMoniker = Nothing
Dim moniker2 As IMoniker() = New IMoniker(0) {}
Dim devEnum2 As ICreateDevEnum = CType(New CreateDevEnum, ICreateDevEnum)
hr = devEnum2.CreateClassEnumerator(FilterCategory.AudioInputDevice, classEnum2, 0)
Marshal.ReleaseComObject(devEnum2)
If classEnum2.Next(moniker2.Length, moniker2, IntPtr.Zero) = 0 Then
    Dim iid As Guid = GetType(IBaseFilter).GUID
    moniker2(0).BindToObject(Nothing, Nothing, iid, sobject)
End If
aufilter = CType(sobject, IBaseFilter)