如何在MediaFundation.Net中使用MMFCreateDXGISurfaceBuffer?

How to use MMFCreateDXGISurfaceBuffer in MediaFundation.Net?

我在 Microsoft 网站上搜索了关于 MMFCreateDXGISurfaceBuffer 的内容,但它都是用 C++ 编写的。我正在尝试在 C# 中使用 MMFCreateDXGISurfaceBuffer,但找不到任何关于如何正确使用该方法的参考。

下面是c++中的代码。

MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), pAcquiredDesktopImage, 0, FALSE, &pMediaBuffer)

我尝试将上面的代码转换为 C#,下面是我的代码,但似乎不正确,因为我得到 E_NOINTERFACE

MFExtern.MFCreateDXGISurfaceBuffer(typeof(SharpDX.Direct3D11.Texture2D).GUID, texture, 0, false, out buffer);

我在我的应用程序中使用 SharpDX DXGI 和 MediaFoundation.Net。我不确定

typeof(SharpDX.Direct3D11.Texture2D).GUID

等同于

__uuidof(ID3D11Texture2D)

下面是 MMFCreateDXGISurfaceBuffer

的包装器
 [DllImport("mfplat.dll", ExactSpelling = true), SuppressUnmanagedCodeSecurity]
    public static extern HResult MFCreateDXGISurfaceBuffer(
        [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
        [MarshalAs(UnmanagedType.Interface)] object punkSurface,
        int uSubresourceIndex,
        [MarshalAs(UnmanagedType.Bool)] bool fBottomUpWhenLinear,
        out IMFMediaBuffer ppBuffer
    );

谁能教我如何在 MediaFOundation.Net 中使用 MFCreateDXGISurfaceBuffer。谢谢

编辑:我使用了 MFTrace,下面是失败的日志

1716,2494 03:19:20.59013 CMFTransformDetours::SetInputType @02E96B4C Failed MT: MF_MT_FRAME_SIZE=5866925327104 (1366,768);MF_MT_MAJOR_TYPE=MEDIATYPE_Video;MF_MT_FRAME_RATE=257698037761 (60,1);MF_MT_PIXEL_ASPECT_RATIO=4294967297 (1,1);MF_MT_INTERLACE_MODE=2;MF_MT_SUBTYPE=MFVideoFormat_RGB32

我不确定我是否正确设置了输入类型,因为我正在为接收器提供 direct3d 表面。

我认为这是你的 post (https://sourceforge.net/p/mfnet/discussion/711229/thread/9814e58b/#c149);我在那里回答过,但在 Whosebug 上看到了你的问题,我认为我应该重现我的回答。


MFCreateDXGISurfaceBuffer 接受 IUnknown 作为第二个参数,因此您可以将任何看起来像 COM 对象的东西传递给它,无论是本机对象还是托管对象。但是,在内部 MFCreateDXGISurfaceBuffer 实际上是在寻找一个实现了 ID3D11Texture2D 接口的对象,这就是为什么你必须将那个接口的 IID 作为第一个参数传递。

您的代码可能会失败,因为您将 SharpDX Texture2D 托管对象本身传递给 MFCreateDXGISurfaceBuffer。您可能认为这会在 .NET 端失败(即编译器或编组错误),但 .NET 互操作编组器实际上使 P/Invoke 函数调用成为可能。这是因为任何 .NET class 都可以转换为 object,并且所有 .NET 对象都可以通过 CCW(COM 可调用包装器)编组为本机 IUnknown COM 对象。

您从 MFCreateDXGISurfaceBuffer 获得 E_NOINTERFACE 的原因是 .NET 互操作编组器用本机 CCW 包装 SharpDX Texture2D 对象,将 CCW 交给它函数,然后该函数尝试查看对象(它认为是本机 IUnknown)是否支持 ID3D11Texture2D 接口。 MFCreateDXGISurfaceBuffer 通过使用 IUnknown 提供的 COM QueryInterface 函数来实现。然而,SharpDX Texture2D class 没有实现 ID3D11Texture2D 接口,所以当查询 Texture2D 的编组版本时,查找失败并且 QueryInterface 将 return E_NOINTERFACE.

要解决此问题,您需要获取 SharpDX Texture2D 封装的本机 COM 指针,而不是传入 SharpDX 对象本身。所有 SharpDX 对象都派生自基础 ComObject class,它提供 NativePointer 属性 访问器,它为您提供 IntPtr 到实际包装的 COM 对象。在您的案例中,这是实际的 COM ID3D11Texture2D 指针,但现在我们需要从中获取 object 而不是 IntPtr。看起来像这样:

HResult hr;
IMFMediaBuffer buffer;

// "Marshal" is from the System.Runtime.InteropServices namespace

// This line converts the IntPtr texture.NativePointer in to a .NET RCW 
// (runtime callable wrapper) so that it can be used like a native .NET
// object.
object texNativeObject = Marshal.GetObjectForIUnknown(texture.NativePointer);

// When texNativeObject is passed as an argument to MFCreateDXGISurfaceBuffer,
// the .NET marshaller will simply unwrap the RCW and provide a native COM 
// pointer to the function.
hr = MFExtern.MFCreateDXGISurfaceBuffer(
    typeof(SharpDX.Direct3D11.Texture2D).GUID, 
    texNativeObject, 
    0, false, out buffer
);

// check "hr" here for success or failure

// "COMBase" is from the MediaFoundation.Misc namespace
// If this line causes problems at runtime, just remove it. SafeRelease
// forces the .NET runtime to release the RCW now, rather than later when
// a GC happens. It calls Marshal.ReleaseComObject(...) internally.
COMBase.SafeRelease(texNativeObject); 
texNativeObject = null;

或者,您可以添加另一个接受 IntPtr 作为第二个参数的 MFCreateDXGISurfaceBuffer 定义。您可以将此添加到您自己的代码中,而不是修改 MediaFoundation.NET.

class NativeMethods
{
    // EntryPoint must be provided since we are naming the alternate
    // version MFCreateDXGISurfaceBuffer2 to separate it from the original
    [DllImport("mfplat.dll", ExactSpelling = true, EntryPoint = "MFCreateDXGISurfaceBuffer")]
    public static extern HResult MFCreateDXGISurfaceBuffer2(
        [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid,
        IntPtr punkSurface,
        int uSubresourceIndex,
        [MarshalAs(UnmanagedType.Bool)] bool fBottomUpWhenLinear,
        out IMFMediaBuffer ppBuffer
    );
}

可以使用这样的替代函数签名,只是因为 Texture2D.NativePointer 是一个 IntPtr 引用非托管内存。换句话说,您将非托管内存地址传递给非托管函数,这没关系。

一旦您的代码中有了替代版本,您就可以像这样使用新的 MFCreateDXGISurfaceBuffer2 函数:

HResult hr;
IMFMediaBuffer buffer;
hr = NativeMethods.MFCreateDXGISurfaceBuffer2(
    typeof(SharpDX.Direct3D11.Texture2D).GUID, 
    texture.NativePointer, 
    0, false, out buffer
);

// check "hr" here

希望对您有所帮助!