diff --git a/sources/Directory.Packages.props b/sources/Directory.Packages.props index 6354c5c82b..000ecdaab2 100644 --- a/sources/Directory.Packages.props +++ b/sources/Directory.Packages.props @@ -14,6 +14,7 @@ + @@ -22,7 +23,6 @@ - diff --git a/sources/engine/Stride.Video/NativeMethods.json b/sources/engine/Stride.Video/NativeMethods.json new file mode 100644 index 0000000000..340b621dd9 --- /dev/null +++ b/sources/engine/Stride.Video/NativeMethods.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://aka.ms/CsWin32.schema.json", + "allowMarshaling": false, + "comInterop": { "preserveSigMethods": [ "IMFMediaEngine.OnVideoStreamTick" ] } +} diff --git a/sources/engine/Stride.Video/NativeMethods.txt b/sources/engine/Stride.Video/NativeMethods.txt new file mode 100644 index 0000000000..7dbe375ec0 --- /dev/null +++ b/sources/engine/Stride.Video/NativeMethods.txt @@ -0,0 +1,21 @@ +MFStartup +MFShutdown +CLSID_MFMediaEngineClassFactory + +CoCreateInstance +IMFMediaEngine +IMFMediaEngineEx +IMFMediaEngineClassFactory +IMFMediaEngineNotify +MFCreateAttributes +MFCreateDXGIDeviceManager +MF_MEDIA_ENGINE_CALLBACK +MF_MEDIA_ENGINE_EVENT +MF_MEDIA_ENGINE_ERR +MF_MEDIA_ENGINE_DXGI_MANAGER +MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT +MF_VERSION + +IStream +STATFLAG +MFCreateMFByteStreamOnStream diff --git a/sources/engine/Stride.Video/Stride.Video.csproj b/sources/engine/Stride.Video/Stride.Video.csproj index af0b1840c7..15b281ce88 100644 --- a/sources/engine/Stride.Video/Stride.Video.csproj +++ b/sources/engine/Stride.Video/Stride.Video.csproj @@ -1,4 +1,4 @@ - + true true @@ -24,7 +24,10 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/sources/engine/Stride.Video/VideoInstance.Direct3D.cs b/sources/engine/Stride.Video/VideoInstance.Direct3D.cs index 1949f71bef..fbbc059926 100644 --- a/sources/engine/Stride.Video/VideoInstance.Direct3D.cs +++ b/sources/engine/Stride.Video/VideoInstance.Direct3D.cs @@ -2,12 +2,14 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. #if STRIDE_GRAPHICS_API_DIRECT3D11 +#pragma warning disable CA1416 // Validate platform compatibility (no need, we check for Windows already at a higher level) using System; +using System.Collections; using System.IO; using System.Linq; using System.Runtime.CompilerServices; -using SharpDX.MediaFoundation; +using System.Runtime.InteropServices; using Silk.NET.Core.Native; using Silk.NET.DXGI; using Silk.NET.Direct3D11; @@ -15,48 +17,45 @@ using Stride.Core.Serialization; using Stride.Graphics; using Stride.Media; +using Windows.Win32; +using Windows.Win32.Foundation; +using Windows.Win32.Media.MediaFoundation; +using Windows.Win32.System.Com; +using static Windows.Win32.PInvoke; +using IUnknown = Windows.Win32.System.Com.IUnknown; namespace Stride.Video { unsafe partial class VideoInstance { - private MediaEngine mediaEngine; + private IMFMediaEngine* mediaEngine; private Texture videoOutputTexture; private IDXGISurface* videoOutputSurface; - private SharpDX.ComObject videoOutputSurfaceSharpDX; // TODO: Remove when Silk includes Media Foundation private Stream videoFileStream; - private ByteStream videoDataStream; private int videoWidth; private int videoHeight; private bool reachedEOF; - private static MediaEngineClassFactory mediaEngineFactory = new(); - partial void ReleaseMediaImpl() { - mediaEngine?.Shutdown(); - mediaEngine?.Dispose(); + if (mediaEngine != null) + { + mediaEngine->Shutdown(); + mediaEngine->Release(); + } mediaEngine = null; - // Randomly crashes in sharpDX and the native code when disabling this - // The stream is problably accessed after disposal due to communication latency - // Unfortunately we don't receive any events after the call to Shutdown where we could dispose those - - //videoDataStream?.Dispose(); - //videoDataStream = null; - //videoFileStream?.Dispose(); - //videoFileStream = null; + videoFileStream?.Dispose(); + videoFileStream = null; if (videoOutputSurface != null) videoOutputSurface->Release(); videoOutputSurface = null; - videoOutputSurfaceSharpDX = null; // TODO: Remove when Silk includes Media Foundation - // NOTE: Do not Dispose() it. It just wraps around the IDXGISurface videoOutputTexture?.Dispose(); videoOutputTexture = null; @@ -67,29 +66,29 @@ partial void PlayImpl() if (playRange.Start > CurrentTime) Seek(playRange.Start); - mediaEngine.Play(); + mediaEngine->Play(); } partial void PauseImpl() { - mediaEngine.Pause(); + mediaEngine->Pause(); } partial void StopImpl() { - mediaEngine.Pause(); + mediaEngine->Pause(); Seek(playRange.Start); } partial void SeekImpl(TimeSpan time) { - mediaEngine.CurrentTime = time.TotalSeconds; + mediaEngine->SetCurrentTime(time.TotalSeconds); reachedEOF = false; } partial void ChangePlaySpeedImpl() { - mediaEngine.PlaybackRate = SpeedFactor; + mediaEngine->SetPlaybackRate(SpeedFactor); } partial void UpdatePlayRangeImpl() @@ -100,7 +99,7 @@ partial void UpdatePlayRangeImpl() partial void UpdateAudioVolumeImpl(float volume) { - mediaEngine.Volume = volume; + mediaEngine->SetVolume(volume); } partial void UpdateImpl(ref TimeSpan elapsed) @@ -109,7 +108,7 @@ partial void UpdateImpl(ref TimeSpan elapsed) return; // Transfer frame if a new one is available - if (mediaEngine.OnVideoStreamTick(out var presentationTimeTicks)) + if (mediaEngine->OnVideoStreamTick(out var presentationTimeTicks).Succeeded) { CurrentTime = TimeSpan.FromTicks(presentationTimeTicks); @@ -150,7 +149,7 @@ partial void UpdateImpl(ref TimeSpan elapsed) // Now update the video texture with data of the new video frame var graphicsContext = services.GetSafeServiceAs(); - mediaEngine.TransferVideoFrame(videoOutputSurfaceSharpDX, srcRef: null, new SharpDX.Mathematics.Interop.RawRectangle(0, 0, videoWidth, videoHeight), borderClrRef: null); + mediaEngine->TransferVideoFrame((IUnknown*)videoOutputSurface, pSrc: null, new RECT(0, 0, videoWidth, videoHeight), pBorderClr: null); videoTexture.CopyDecoderOutputToTopLevelMipmap(graphicsContext, videoOutputTexture); videoTexture.GenerateMipMaps(graphicsContext); @@ -174,29 +173,40 @@ partial void InitializeMediaImpl(string url, long startPosition, long length, re try { - //Assign our dxgi manager, and set format to bgra - var attr = new MediaEngineAttributes - { - VideoOutputFormat = (int) Format.FormatB8G8R8A8Unorm, - DxgiManager = videoSystem.DxgiDeviceManager - }; + CoCreateInstance(in CLSID_MFMediaEngineClassFactory, null, CLSCTX.CLSCTX_INPROC_SERVER, out IMFMediaEngineClassFactory* classFactory).ThrowOnFailure(); + using var _1 = ComHelpers.AsPtr(classFactory); - mediaEngine = new MediaEngine(mediaEngineFactory, attr); + //Assign our dxgi manager, and set format to bgra + IMFAttributes* attr; + MFCreateAttributes(&attr, 1).ThrowOnFailure(); + using var _2 = ComHelpers.AsPtr(attr); + attr->SetUINT32(in MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, (uint)Format.FormatB8G8R8A8Unorm); + attr->SetUnknown(in MF_MEDIA_ENGINE_DXGI_MANAGER, (IUnknown*)videoSystem.DxgiDeviceManager); // Register our PlayBackEvent - mediaEngine.PlaybackEvent += OnPlaybackCallback; + using var notify = ComHelpers.CreateCCW(new Notify(this)); + attr->SetUnknown(in MF_MEDIA_ENGINE_CALLBACK, notify); + + IMFMediaEngine* engine; + classFactory->CreateInstance(default, attr, &engine); + mediaEngine = engine; // set the video source - using var mediaEngineEx = mediaEngine.QueryInterface(); + mediaEngine->QueryInterface(out var mediaEngineEx).ThrowOnFailure(); + using var _3 = ComHelpers.AsPtr(mediaEngineEx); videoFileStream = new VirtualFileStream(File.OpenRead(url), startPosition, startPosition + length); - videoDataStream = new ByteStream(videoFileStream); + using var videoDataStream = ComHelpers.CreateCCW(new ComStreamWrapper(videoFileStream)); + IMFByteStream* byteStream; + MFCreateMFByteStreamOnStream(videoDataStream, &byteStream); + using var _4 = ComHelpers.AsPtr(byteStream); // Creates an URL to the file var uri = new Uri(url, UriKind.RelativeOrAbsolute); + using var uri_bstr = ComHelpers.AsBSTR(uri.AbsoluteUri); // Set the source stream - mediaEngineEx.SetSourceFromByteStream(videoDataStream, uri.AbsoluteUri); + mediaEngineEx->SetSourceFromByteStream(byteStream, uri_bstr); } catch { @@ -208,8 +218,11 @@ partial void InitializeMediaImpl(string url, long startPosition, long length, re private unsafe void CompleteMediaInitialization() { //Get our video size - mediaEngine.GetNativeVideoSize(out videoWidth, out videoHeight); - Duration = TimeSpan.FromSeconds(mediaEngine.Duration); + mediaEngine->GetNativeVideoSize(out var width, out var height); + videoWidth = (int)width; + videoHeight = (int)height; + + Duration = TimeSpan.FromSeconds(mediaEngine->GetDuration()); //Get DXGI surface to be used by our media engine videoOutputTexture = Texture.New2D(GraphicsDevice, videoWidth, videoHeight, 1, PixelFormat.B8G8R8A8_UNorm, TextureFlags.ShaderResource | TextureFlags.RenderTarget); @@ -220,12 +233,11 @@ private unsafe void CompleteMediaInitialization() result.Throw(); videoOutputSurface = outputSurface; - videoOutputSurfaceSharpDX = new SharpDX.ComObject((IntPtr) videoOutputSurface); AllocateVideoTexture(videoWidth, videoHeight); if (videoComponent.PlayAudio != true || videoComponent.AudioEmitters.Any(e => e != null)) - mediaEngine.Muted = true; + mediaEngine->SetMuted(true); } /// @@ -234,64 +246,243 @@ private unsafe void CompleteMediaInitialization() /// The play event. /// The param1. /// The param2. - private void OnPlaybackCallback(MediaEngineEvent playEvent, long param1, int param2) + private void OnPlaybackCallback(MF_MEDIA_ENGINE_EVENT playEvent, nuint param1, uint param2) { switch (playEvent) { - case MediaEngineEvent.ResourceLost: - case MediaEngineEvent.StreamRenderingerror: - case MediaEngineEvent.Suspend: - case MediaEngineEvent.Abort: - case MediaEngineEvent.Emptied: - case MediaEngineEvent.Stalled: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_RESOURCELOST: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_STREAMRENDERINGERROR: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_SUSPEND: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_ABORT: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_EMPTIED: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_STALLED: break; - case MediaEngineEvent.LoadedMetadata: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA: CompleteMediaInitialization(); break; - case MediaEngineEvent.Error: - Logger.Error($"Failed to load the video source. The file codec or format is likely not to be supported. MedieEngine error code=[{(MediaEngineErr)param1}], Windows error code=[{param2}]"); + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_ERROR: + Logger.Error($"Failed to load the video source. The file codec or format is likely not to be supported. MedieEngine error code=[{(MF_MEDIA_ENGINE_ERR)param1}], Windows error code=[{param2}]"); ReleaseMedia(); break; - case MediaEngineEvent.FirstFrameReady: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY: break; - case MediaEngineEvent.LoadedData: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_LOADEDDATA: break; - case MediaEngineEvent.CanPlay: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_CANPLAY: break; - case MediaEngineEvent.Seeked: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_SEEKED: break; - case MediaEngineEvent.Ended: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_ENDED: reachedEOF = true; break; - case MediaEngineEvent.LoadStart: - case MediaEngineEvent.Progress: - case MediaEngineEvent.Waiting: - case MediaEngineEvent.Playing: - case MediaEngineEvent.CanPlayThrough: - case MediaEngineEvent.Seeking: - case MediaEngineEvent.Play: - case MediaEngineEvent.Pause: - case MediaEngineEvent.TimeUpdate: - case MediaEngineEvent.RateChange: - case MediaEngineEvent.DurationChange: - case MediaEngineEvent.VolumeChange: - case MediaEngineEvent.FormatChange: - case MediaEngineEvent.PurgeQueuedEvents: - case MediaEngineEvent.TimelineMarker: - case MediaEngineEvent.BalanceChange: - case MediaEngineEvent.DownloadComplete: - case MediaEngineEvent.BufferingStarted: - case MediaEngineEvent.BufferingEnded: - case MediaEngineEvent.FrameStepCompleted: - case MediaEngineEvent.NotifyStableState: - case MediaEngineEvent.Trackschange: - case MediaEngineEvent.OpmInformation: - case MediaEngineEvent.DelayloadeventChanged: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_LOADSTART: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_PROGRESS: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_WAITING: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_PLAYING: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_SEEKING: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_PLAY: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_PAUSE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_TIMEUPDATE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_RATECHANGE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_FORMATCHANGE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_TIMELINE_MARKER: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_BALANCECHANGE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_DOWNLOADCOMPLETE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_NOTIFYSTABLESTATE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_TRACKSCHANGE: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_OPMINFO: + case MF_MEDIA_ENGINE_EVENT.MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED: break; default: throw new ArgumentOutOfRangeException(nameof(playEvent), playEvent, null); } } + + sealed class Notify : IMFMediaEngineNotify.Interface + { + private readonly VideoInstance instance; + + public Notify(VideoInstance instance) + { + this.instance = instance; + } + + public HRESULT EventNotify(uint @event, nuint param1, uint param2) + { + instance.OnPlaybackCallback((MF_MEDIA_ENGINE_EVENT)@event, param1, param2); + return default; + } + } + + sealed class ComStreamWrapper : IStream.Interface + { + private readonly Stream stream; + + public ComStreamWrapper(Stream stream) + { + this.stream = stream; + } + + public HRESULT Stat(STATSTG* pstatstg, uint grfStatFlag) + { + pstatstg->cbSize = (ulong)stream.Length; + return default; + } + + public HRESULT Seek(long dlibMove, SeekOrigin dwOrigin, [Optional] ulong* plibNewPosition) + { + var newPosition = stream.Seek(dlibMove, dwOrigin); + if (plibNewPosition != null) + *plibNewPosition = (ulong)newPosition; + return default; + } + + public HRESULT Read(void* pv, uint cb, [Optional] uint* pcbRead) + { + var buffer = new Span(pv, (int)cb); + *pcbRead = (uint)stream.Read(buffer); + return default; + } + + public HRESULT Write(void* pv, uint cb, [Optional] uint* pcbWritten) + { + throw new NotImplementedException(); + } + + public HRESULT SetSize(ulong libNewSize) + { + throw new NotImplementedException(); + } + + public HRESULT CopyTo(IStream* pstm, ulong cb, [Optional] ulong* pcbRead, [Optional] ulong* pcbWritten) + { + throw new NotImplementedException(); + } + + public HRESULT Commit(uint grfCommitFlags) + { + throw new NotImplementedException(); + } + + public HRESULT Revert() + { + throw new NotImplementedException(); + } + + public HRESULT LockRegion(ulong libOffset, ulong cb, uint dwLockType) + { + throw new NotImplementedException(); + } + + public HRESULT UnlockRegion(ulong libOffset, ulong cb, uint dwLockType) + { + throw new NotImplementedException(); + } + + public HRESULT Clone(IStream** ppstm) + { + throw new NotImplementedException(); + } + } + } +} + +namespace Windows.Win32 +{ + static unsafe partial class ComHelpers + { + public static MFComPtr CreateCCW(object instance) where T : unmanaged, IVTable, IComIID + { + var unknown = (IUnknown*)MFComWrappers.Instance.GetOrCreateComInterfaceForObject(instance, CreateComInterfaceFlags.None); + unknown->QueryInterface(out var ppv); + unknown->Release(); + return new MFComPtr(ppv); + } + + public static unsafe MFComPtr AsPtr(T* ptr) where T : unmanaged, IComIID => new MFComPtr(ptr); + + public static BSTRPtr AsBSTR(string str) + { + var bstr = Marshal.StringToBSTR(str); + return new BSTRPtr(bstr); + } + + // Called by CsWin32 + static partial void PopulateIUnknownImpl(IUnknown.Vtbl* vtable) + where TComInterface : unmanaged + { + ComWrappers.GetIUnknownImpl(out IntPtr fpQueryInterface, out IntPtr fpAddRef, out IntPtr fpRelease); + vtable->QueryInterface_1 = (delegate* unmanaged[Stdcall])fpQueryInterface; + vtable->AddRef_2 = (delegate* unmanaged[Stdcall])fpAddRef; + vtable->Release_3 = (delegate* unmanaged[Stdcall])fpRelease; + } + + // https://github.com/microsoft/CsWin32/issues/751#issuecomment-1304268295 + private sealed class MFComWrappers : ComWrappers + where T : IVTable, IComIID + { + public static readonly ComWrappers Instance = new MFComWrappers(); + + private static readonly ComInterfaceEntry* s_comInterfaceEntries = CreateComInterfaceEntries(); + + private static ComInterfaceEntry* CreateComInterfaceEntries() + { + var comInterfaceEntries = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(T), sizeof(ComInterfaceEntry)); + comInterfaceEntries->IID = T.Guid; + comInterfaceEntries->Vtable = new IntPtr(T.VTable); + return comInterfaceEntries; + } + + protected override ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + count = 1; + return s_comInterfaceEntries; + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + throw new NotImplementedException(); + } + + protected override void ReleaseObjects(IEnumerable objects) + { + throw new NotImplementedException(); + } + } + + // Little helper to ensure Release is called + public ref struct MFComPtr : IDisposable where T : unmanaged, IComIID + { + public readonly T* Ptr; + public MFComPtr(T* ptr) + { + Ptr = ptr; + } + + public void Dispose() => ((IUnknown*)Ptr)->Release(); + public static implicit operator T*(in MFComPtr comPtr) => comPtr.Ptr; + public static implicit operator IUnknown*(in MFComPtr comPtr) => (IUnknown*)comPtr.Ptr; + public static explicit operator MFComPtr(T* ptr) => new MFComPtr(ptr); + } + + // Little helper to ensure FreeBSTR is called + public ref struct BSTRPtr : IDisposable + { + public readonly nint Ptr; + public BSTRPtr(nint ptr) + { + Ptr = ptr; + } + public void Dispose() => Marshal.FreeBSTR(Ptr); + public static implicit operator BSTR(in BSTRPtr bstrPtr) => new BSTR(bstrPtr.Ptr); + } } } diff --git a/sources/engine/Stride.Video/VideoSystem.Direct3D.cs b/sources/engine/Stride.Video/VideoSystem.Direct3D.cs index 63e0a67374..1796e50461 100644 --- a/sources/engine/Stride.Video/VideoSystem.Direct3D.cs +++ b/sources/engine/Stride.Video/VideoSystem.Direct3D.cs @@ -2,18 +2,21 @@ // Distributed under the MIT license. See the LICENSE.md file in the project root for more information. #if STRIDE_GRAPHICS_API_DIRECT3D11 +#pragma warning disable CA1416 // Validate platform compatibility (no need, we check for Windows already at a higher level) using System; -using SharpDX.MediaFoundation; using Silk.NET.Core.Native; using Silk.NET.Direct3D11; using Stride.Games; +using Windows.Win32.Media.MediaFoundation; +using static Windows.Win32.PInvoke; +using IUnknown = Windows.Win32.System.Com.IUnknown; namespace Stride.Video; -public partial class VideoSystem +public unsafe partial class VideoSystem { - public DXGIDeviceManager DxgiDeviceManager; // TODO: Remove when Silk includes Media Foundation + internal IMFDXGIDeviceManager* DxgiDeviceManager; public override unsafe void Initialize() { @@ -22,10 +25,11 @@ public override unsafe void Initialize() var graphicsDevice = Services.GetService().GraphicsDevice; var d3d11Device = graphicsDevice.NativeDevice; - var d3d11DeviceSharpDX = new SharpDX.ComObject((IntPtr) d3d11Device.Handle); - DxgiDeviceManager = new DXGIDeviceManager(); - DxgiDeviceManager.ResetDevice(d3d11DeviceSharpDX); + IMFDXGIDeviceManager* manager; + MFCreateDXGIDeviceManager(out var resetToken, &manager).ThrowOnFailure(); + DxgiDeviceManager = manager; + DxgiDeviceManager->ResetDevice((IUnknown*)(d3d11Device.Handle), resetToken); // Add multi-thread protection on the device HResult result = d3d11Device.QueryInterface(out ComPtr multiThread); @@ -36,7 +40,15 @@ public override unsafe void Initialize() multiThread.SetMultithreadProtected(true); multiThread.Dispose(); - MediaManager.Startup(); + MFStartup(MF_VERSION, 0).ThrowOnFailure(); + } + + protected override void Destroy() + { + DxgiDeviceManager->Release(); + MFShutdown().ThrowOnFailure(); + + base.Destroy(); } }