如何在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
希望对您有所帮助!
我在 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
希望对您有所帮助!