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);
}
}
}
}