smourier / DirectN

Direct interop Code for .NET Framework, .NET Core and .NET 5+ : DXGI, WIC, DirectX 9 to 12, Direct2D, Direct Write, Direct Composition, Media Foundation, WASAPI, CodecAPI, GDI, Spatial Audio, DVD, Windows Media Player, UWP DXInterop, WinUI3, etc.
MIT License
311 stars 28 forks source link

Unable to use MFEnumDeviceSources(). #23

Closed Steph55 closed 2 years ago

Steph55 commented 2 years ago

I tried using the function MFEnumDeviceSources() - reference issue #22 - with many different methods, but it seems quite difficult in C#. How should we proceed to access the contents of the out parameter pppSourceActivate?

I even tried making my own function signature, without success:

[DllImport("mf", ExactSpelling = true)] public static extern HRESULT MFEnumDeviceSources( ref IMFAttributes pAttributes, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] out IMFActivate[] pppSourceActivate, out uint pcSourceActivate);

Maybe with unsafe code?

Thanks a lot!

smourier commented 2 years ago

Hi,

You never need unsafe code. And DirectN was designed with that idea in mind.

There was a bug in the MFEnumDeviceSources definition. The first parameter isn't ref. I have updated DirectN.

As for calling the method, I've also added an extension in IMFAttributesExtensions.cs that explains how to do it:

public static IEnumerable<IComObject<IMFActivate>> EnumDeviceSources(this IComObject<IMFAttributes> input) => EnumDeviceSources(input?.Object).Select(a => new ComObject<IMFActivate>(a));
public static IEnumerable<IMFActivate> EnumDeviceSources(this IMFAttributes input)
{
    if (input == null)
        throw new ArgumentNullException(nameof(input));

    Functions.MFEnumDeviceSources(input, out var array, out var count).ThrowOnError();
    for (var i = 0; i < count; i++)
    {
        var ptr = Marshal.ReadIntPtr(array, i * IntPtr.Size);
        var activate = (IMFActivate)Marshal.GetObjectForIUnknown(ptr);
        yield return activate;
        Marshal.Release(ptr);
    }

    Marshal.FreeCoTaskMem(array);
}

So you can call it like this for example (equivalent of code here: https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-mfenumdevicesources#examples):

MFFunctions.MFStartup();
var atts = MFFunctions.MFCreateAttributes();
atts.Set(MFConstants.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MFConstants.MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);

foreach (var src in atts.EnumDeviceSources())
{
    var source = src.ActivateObject<IMFMediaSource>();
    source.Object.GetCharacteristics(out var c);
}
...