// ------------------------------------------------------------------ // DirectX.Capture // // History: // 2003-Jan-24 BL - created // // Copyright (c) 2003 Brian Low // ------------------------------------------------------------------ using System; using System.Diagnostics; using System.Collections; using System.Runtime.InteropServices; using DShowNET; namespace DirectX.Capture { /// /// A collection of sources (or physical connectors) on an /// audio or video device. This is used by the /// class to provide a list of available sources on the currently /// selected audio and video devices. This class cannot be created /// directly. This class assumes there is only 1 video and 1 audio /// crossbar and all input pins route to a single output pin on each /// crossbar. /// public class SourceCollection : CollectionBase, IDisposable { // ------------------ Constructors/Destructors ----------------------- /// Initialize collection with no sources. internal SourceCollection() { InnerList.Capacity = 1; } /// Initialize collection with sources from graph. internal SourceCollection(ICaptureGraphBuilder2 graphBuilder, IBaseFilter deviceFilter, bool isVideoDevice) { addFromGraph( graphBuilder, deviceFilter, isVideoDevice ); } /// Destructor. Release unmanaged resources. ~SourceCollection() { Dispose(); } // -------------------- Public Properties ----------------------- /// Get the source at the specified index. public Source this[int index] { get { return( (Source) InnerList[index] ); } } /// /// Gets or sets the source/physical connector currently in use. /// This is marked internal so that the Capture class can control /// how and when the source is changed. /// internal Source CurrentSource { get { // Loop through each source and find the first // enabled source. foreach ( Source s in InnerList ) { if ( s.Enabled ) return( s ); } return ( null ); } set { if ( value == null ) { // Disable all sources foreach ( Source s in InnerList ) s.Enabled = false; } else if ( value is CrossbarSource ) { // Enable this source // (this will automatically disable all other sources) value.Enabled = true; } else { // Disable all sources // Enable selected source foreach ( Source s in InnerList ) s.Enabled = false; value.Enabled = true; } } } // -------------------- Public methods ----------------------- /// Empty the collection. public new void Clear() { for ( int c = 0; c < InnerList.Count; c++ ) this[c].Dispose(); InnerList.Clear(); } /// Release unmanaged resources. public void Dispose() { Clear(); InnerList.Capacity = 1; } // -------------------- Protected Methods ----------------------- /// Populate the collection from a filter graph. protected void addFromGraph ( ICaptureGraphBuilder2 graphBuilder, IBaseFilter deviceFilter, bool isVideoDevice ) { Trace.Assert( graphBuilder != null ); ArrayList crossbars = findCrossbars( graphBuilder, deviceFilter ); foreach ( IAMCrossbar crossbar in crossbars ) { ArrayList sources = findCrossbarSources( graphBuilder, crossbar, isVideoDevice ); InnerList.AddRange( sources ); } if ( !isVideoDevice ) { if ( InnerList.Count == 0 ) { ArrayList sources = findAudioSources( graphBuilder, deviceFilter ); InnerList.AddRange( sources ); } } } /// /// Retrieve a list of crossbar filters in the graph. /// Most hardware devices should have a maximum of 2 crossbars, /// one for video and another for audio. /// protected ArrayList findCrossbars(ICaptureGraphBuilder2 graphBuilder, IBaseFilter deviceFilter) { ArrayList crossbars = new ArrayList(); Guid category = FindDirection.UpstreamOnly; Guid type = new Guid(); Guid riid = typeof(IAMCrossbar).GUID; int hr; object comObj = null; object comObjNext = null; // Find the first interface, look upstream from the selected device hr = graphBuilder.FindInterface( ref category, ref type, deviceFilter, ref riid, out comObj ); while ( (hr == 0) && (comObj != null) ) { // If found, add to the list if ( comObj is IAMCrossbar ) { crossbars.Add( comObj as IAMCrossbar ); // Find the second interface, look upstream from the next found crossbar hr = graphBuilder.FindInterface( ref category, ref type, comObj as IBaseFilter, ref riid, out comObjNext ); comObj = comObjNext; } else comObj = null; } return( crossbars ); } /// /// Populate the internal InnerList with sources/physical connectors /// found on the crossbars. Each instance of this class is limited /// to video only or audio only sources ( specified by the isVideoDevice /// parameter on the constructor) so we check each source before adding /// it to the list. /// protected ArrayList findCrossbarSources(ICaptureGraphBuilder2 graphBuilder, IAMCrossbar crossbar, bool isVideoDevice) { ArrayList sources = new ArrayList(); int hr; int numOutPins; int numInPins; hr = crossbar.get_PinCounts( out numOutPins, out numInPins ); if ( hr < 0 ) Marshal.ThrowExceptionForHR( hr ); // We loop through every combination of output and input pin // to see which combinations match. // Loop through output pins for ( int cOut = 0; cOut < numOutPins; cOut++ ) { // Loop through input pins for ( int cIn = 0; cIn < numInPins; cIn++ ) { // Can this combination be routed? hr = crossbar.CanRoute( cOut, cIn ); if ( hr == 0 ) { // Yes, this can be routed int relatedPin; PhysicalConnectorType connectorType; hr = crossbar.get_CrossbarPinInfo( true, cIn, out relatedPin, out connectorType ); if ( hr < 0 ) Marshal.ThrowExceptionForHR( hr ); // Is this the correct type?, If so add to the InnerList CrossbarSource source = new CrossbarSource( crossbar, cOut, cIn, connectorType ); if ( connectorType < PhysicalConnectorType.Audio_Tuner ) if ( isVideoDevice ) sources.Add( source ); else if ( !isVideoDevice ) sources.Add( source ); } } } // Some silly drivers (*cough* Nvidia *cough*) add crossbars // with no real choices. Every input can only be routed to // one output. Loop through every Source and see if there // at least one other Source with the same output pin. int refIndex = 0; while ( refIndex < sources.Count ) { bool found = false; CrossbarSource refSource = (CrossbarSource) sources[refIndex]; for ( int c = 0; c < sources.Count; c++ ) { CrossbarSource s = (CrossbarSource) sources[c]; if ( ( refSource.OutputPin == s.OutputPin ) && ( refIndex != c ) ) { found = true; break; } } if ( found ) refIndex++; else sources.RemoveAt( refIndex ); } return( sources ); } protected ArrayList findAudioSources(ICaptureGraphBuilder2 graphBuilder, IBaseFilter deviceFilter) { ArrayList sources = new ArrayList(); IAMAudioInputMixer audioInputMixer = deviceFilter as IAMAudioInputMixer; if ( audioInputMixer != null ) { // Get a pin enumerator off the filter IEnumPins pinEnum; int hr = deviceFilter.EnumPins( out pinEnum ); pinEnum.Reset(); if( (hr == 0) && (pinEnum != null) ) { // Loop through each pin IPin[] pins = new IPin[1]; int f; do { // Get the next pin hr = pinEnum.Next( 1, pins, out f ); if( (hr == 0) && (pins[0] != null) ) { // Is this an input pin? PinDirection dir = PinDirection.Output; hr = pins[0].QueryDirection( out dir ); if( (hr == 0) && (dir == (PinDirection.Input)) ) { // Add the input pin to the sources list AudioSource source = new AudioSource( pins[0] ); sources.Add( source ); } pins[0] = null; } } while( hr == 0 ); Marshal.ReleaseComObject( pinEnum ); pinEnum = null; } } // If there is only one source, don't return it // because there is nothing for the user to choose. // (Hopefully that single source is already enabled). if ( sources.Count == 1 ) sources.Clear(); return( sources ); } } }