using DirectShowLib; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; namespace Tango.Video.DirectShow { internal class DSUtils { public static void SetSampleGrabber(IBaseFilter filter, SampleGrabberImpl sampleGrabberImpl) { var pVideoSampleGrabber = (ISampleGrabber)filter; var mtVideo = new AMMediaType { majorType = MediaType.Video, subType = MediaSubType.RGB24 }; pVideoSampleGrabber.SetMediaType(mtVideo); pVideoSampleGrabber.SetBufferSamples(false); pVideoSampleGrabber.SetOneShot(false); pVideoSampleGrabber.SetCallback(sampleGrabberImpl, 1); DirectShowLib.DsUtils.FreeAMMediaType(mtVideo); } public static void SetSampleGrabber2(IBaseFilter filter, SampleGrabberImpl sampleGrabberImpl) { var pVideoSampleGrabber = (ISampleGrabber)filter; var mtVideo = new AMMediaType { majorType = MediaType.Video, subType = MediaSubType.RGB24 }; pVideoSampleGrabber.SetMediaType(mtVideo); pVideoSampleGrabber.SetBufferSamples(false); pVideoSampleGrabber.SetOneShot(false); pVideoSampleGrabber.SetCallback(sampleGrabberImpl, 0); DirectShowLib.DsUtils.FreeAMMediaType(mtVideo); } public static IBaseFilter GetVideoInputDeviceFilter(Device device) { return GetFilterByNameAndCat(device); } /// /// Create IBaseFilter from the specified category by the specified name. /// /// /// /// public static IBaseFilter GetFilterByNameAndCat(Device device) { object source = null; var iid = (typeof(IBaseFilter)).GUID; var devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); foreach (DsDevice d in devices) { if (d.DevicePath == device.DevicePath) { d.Mon.BindToObject(null, null, ref iid, out source); break; } } return (IBaseFilter)source; } public static List GetVideoInputDevices() { var devices = DsDevice.GetDevicesOfCat(FilterCategory.VideoInputDevice); return devices.Select(device => new Device() { DevicePath = device.DevicePath, Name = device.Name }).ToList(); } /// /// Connect 2 filters. This is a special implementation of connect filters. It will loop through /// all of the unconnected output pins of the source filter and all of the unconnected input pins of the destination filter /// and it will try any connection, between any of the 2 pairs of pins. On the first succesfull connection /// it will return. /// /// IGraphBuilder where source and destination filter are added. /// Source filter. /// Destination filter. /// true if the connection is succesfull, otherwise false. [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public static bool ConnectFiltersTryAllPins(IGraphBuilder graph, IBaseFilter pSrc, IBaseFilter pDst) { int srcPins = CountPins(pSrc, PinDirection.Output); int dstPins = CountPins(pDst, PinDirection.Input); bool connection = false; for (int i = 0; i < srcPins; i++) { IPin pSrcPin = GetPinAtIndex(pSrc, PinDirection.Output, i, false); if (pSrcPin == null) continue; for (int j = 0; j < dstPins; j++) { IPin pDstPin = GetPinAtIndex(pDst, PinDirection.Input, j, false); if (pDstPin == null) continue; int hr = graph.Connect(pSrcPin, pDstPin); connection = (hr == 0) || (hr == 262722); // S_OK or VFW_S_PARTIAL_RENDER= 0x00040242 (262722) Marshal.ReleaseComObject(pDstPin); if (connection) break; } Marshal.ReleaseComObject(pSrcPin); if (connection) break; } return connection; } /// /// Count all of the pins on a filter with a specified direction. /// /// Filter to count pins for. /// Pin direction required to count pins for. /// [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public static int CountPins(IBaseFilter pFilter, PinDirection pinDir) { IEnumPins enumPins; var pins = new IPin[1]; int hr = pFilter.EnumPins(out enumPins); if (hr != 0) return 0; var nrPins = 0; try { while (enumPins.Next(pins.Length, pins, IntPtr.Zero) == 0) { try { PinInfo pinInfo; hr = pins[0].QueryPinInfo(out pinInfo); if (pinInfo.dir == pinDir) { nrPins++; } if (pinInfo.filter != null) Marshal.ReleaseComObject(pinInfo.filter); } finally { Marshal.ReleaseComObject(pins[0]); } } } finally { if (enumPins != null) Marshal.ReleaseComObject(enumPins); } return nrPins; } /// /// Get pin at a specified index on a filter. /// /// IBaseFilter to find pin on. /// Pin direction. /// Zero based index of the pin. Indexes are counted depending on the pin direction. /// Should the pin be connected or not. /// [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public static IPin GetPinAtIndex(IBaseFilter pFilter, PinDirection pinDir, int index, bool connected) { IEnumPins enumPins; var pins = new IPin[1]; int hr = pFilter.EnumPins(out enumPins); if (hr != 0) return null; var nrPins = -1; bool found = false; try { while (enumPins.Next(pins.Length, pins, IntPtr.Zero) == 0) { try { PinInfo pinInfo; hr = pins[0].QueryPinInfo(out pinInfo); if (pinInfo.dir == pinDir) { nrPins++; } if (pinInfo.filter != null) Marshal.ReleaseComObject(pinInfo.filter); // Check if a pin is connected IPin pConPin = null; pins[0].ConnectedTo(out pConPin); if (pConPin != null && !connected) { Marshal.ReleaseComObject(pConPin); continue; } else if (pConPin == null && connected) continue; if (nrPins == index) { found = true; break; } } finally { if (!found) Marshal.ReleaseComObject(pins[0]); } } } finally { if (enumPins != null) Marshal.ReleaseComObject(enumPins); } return pins[0]; } /// /// Searches for the next connected filter. If the start filter is connected to some downstream /// filter it will return that filter, otherwise, null. /// /// /// /// [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public static IBaseFilter GetNextConnectedFilter(IGraphBuilder pGraph, IBaseFilter pStartFilter) { int pinCount = CountPins(pStartFilter, PinDirection.Output); IBaseFilter pReturnFilter = null; for (int i = 0; i < pinCount; i++) { IPin pOutPin = GetPinAtIndex(pStartFilter, PinDirection.Output, i, true); if (pOutPin != null) { IPin pConnectedToPin = null; pOutPin.ConnectedTo(out pConnectedToPin); if (pConnectedToPin != null) { PinInfo pinInfo; pConnectedToPin.QueryPinInfo(out pinInfo); pReturnFilter = pinInfo.filter; Marshal.ReleaseComObject(pConnectedToPin); } Marshal.ReleaseComObject(pOutPin); } if (pReturnFilter != null) break; } return pReturnFilter; } /// /// Save a DirectShow Graph to a GRF file /// /// the IGraphBuilder interface of the graph /// the file to be saved /// Thrown if graphBuilder is null /// Thrown if errors occur during the file creation /// /// This method overwrites any existing file [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] public static void SaveGraphFile(IGraphBuilder graphBuilder, string fileName) { int hr = 0; IStorage storage = null; #if USING_NET11 UCOMIStream stream = null; #else IStream stream = null; #endif if (graphBuilder == null) throw new ArgumentNullException("graphBuilder"); try { hr = NativeMethods.StgCreateDocfile( fileName, STGM.Create | STGM.Transacted | STGM.ReadWrite | STGM.ShareExclusive, 0, out storage ); Marshal.ThrowExceptionForHR(hr); hr = storage.CreateStream( @"ActiveMovieGraph", STGM.Write | STGM.Create | STGM.ShareExclusive, 0, 0, out stream ); Marshal.ThrowExceptionForHR(hr); hr = (graphBuilder as IPersistStream).Save(stream, true); Marshal.ThrowExceptionForHR(hr); hr = storage.Commit(STGC.Default); Marshal.ThrowExceptionForHR(hr); } finally { if (stream != null) Marshal.ReleaseComObject(stream); if (storage != null) Marshal.ReleaseComObject(storage); } } } }