In meinem letzten Blogeintrag habe ich meine ersten Gehversuche mit der Media Foundation (MF) beschrieben. Herausgekommen ist ein einfacher Media Player.. und wenig wieder verwendbarer Code. Aus diesem Grund habe ich etwas aufgeräumt und eine Komponente daraus entwickelt, über die relativ einfach ein kleiner Video Player in eine Windows.Forms basierte Anwendung integriert werden kann. Die Details der MF-API liegen dabei vollständig in dieser Komponente und sind für den Verwender der Komponente schlicht nicht sichtbar. In diesem Blogeintrag stelle ich die Komponente kurz vor, als was sie macht, welche Schnittstelle sie bereitstellt usw.
Die Komponente MFMediaPlayer
Nachfolgender Screenshot zeigt meinen Ansatz. Der Bereich, in welchem das Video gerendert wird, wird im Designer als normales Panel (System.Windows.Forms.Panel) angelegt und frei platziert. Einstellungen wie Hintergrundfarbe usw. können frei gesetzt werden. Die Komponente MFMediaPlayer, im Screenshot in der Variable m_mediaPlayer, kapselt schließlich alle Funktionen für die Video Wiedergabe. Die Wichtigste Einstellung im Designer ist die Eigenschaft TargetControl. Mit dieser Eigenschaft wird festgelegt, in welches Control das Video gerendert werden soll. Das Panel ist im Screenshot die große weiße Fläche, markiert durch den obersten Pfeil.
Gestartet und kontrolliert wird die Video-Wiedergabe im Code-Behind. Nachfolgend ein kleines Klassendiagramm mit den Methoden, welche aktuell verwendet werden können.
Wie im Klassendiagramm zu sehen, setze ich stark auf asynchrone Methoden nach dem modernen async-await Muster. Grund dafür ist schlicht die Natur der MF-API im Hintergrund. Auch dort sind viele Funktionen asynchron, wie z. B. das Starten eines Videos, das Pausieren oder das Ändern der Größe.
Probleme bei der Entwicklung mit MF
Die größten Schwierigkeiten hatte ich damit, dass der von mir verwendete Wrapper SharpDX bei der MF-API lange nicht so ausgereift ist, wie beispielsweise bei Direct3D. Bei Direct3D hatte ich noch nie Probleme damit, ein C++ Tutorial durchzulesen und die Inhalte dann entsprechend in C# zu übersetzen. Klassen, Funktionen, Methoden und Eigenschaften sind i. d. R. so, wie man Sie aus einen C++-Tutorial entnimmt und als C#-Entwickler dann auch erwartet. Bei MF war das bis jetzt nicht so. Nachfolgend ein Beispiel.
Damit man das Rendering des Videos beeinflussen kann, bietet MF die Schnittstelle IMFVideoDisplayControl. Ein entsprechendes Objekt kann nach einem C++-Beispiel relativ einfach wie folgt aus dem Session-Objekt abgefragt werden. Das Session-Objekt ist dabei im Prinzip das Zentrale Objekt für die Video-Wiedergabe.
// Ask for the IMFVideoDisplayControl interface. This interface is // implemented by the EVR and is exposed by the media session as a service. // Note: This call is expected to fail if the source does not have video. MFGetService(m_pSession, MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&m_pVideoDisplay));
Die Variable m_pVideoDisplay bekommt die Referenz auf das Objekt. So weit, so verständlich. Vom C#-Wrapper könnte jetzt erwartet werden, dass es auch irgendwo eine Methode GetService gibt, über die ein entsprechendes Objekt abgefragt werden kann. Diese Methode gibt es zum Glück auch, allerdings funktioniert diese etwas anders bzw. komplizierter. Nach ca. einer Stunde suchen und probieren ist dann folgendes C#-Coding dabei heraus gekommen.
// Query for display control service using (MF.ServiceProvider serviceProvider = m_mediaSession.QueryInterface<MF.ServiceProvider>()) { m_displayControl = serviceProvider.GetService<MF.VideoDisplayControl>( new Guid("{0x1092a86c, 0xab1a, 0x459a,{0xa3, 0x36, 0x83, 0x1f, 0xbc, 0x4d, 0x11, 0xff}}")); }
Sieht zwar grundsätzlich auch nicht gerade unsauber oder umständlich aus, der Weg ist aber doch ein etwas anderer. Vor allem weil das Session Objekt z. B. nicht von vorne herein die GetService Methode zur Verfügung stellt. Die seltsame Schreibweise der Guid kommt übrigens daher, weil ich keine entsprechende Konstante in SharpDX gefunden habe. Eigentlich komisch, für viele andere Sachen gibt es entsprechende..
Quellcode
www.rolandk.de/files/Testing.SimpleMFPlayerV2.zip
Verweise
- http://archive.msdn.microsoft.com/mediafoundation
- http://www.sharpdx.org/documentation/api/n-sharpdx-mediafoundation
- Developing Microsoft Media Foundation Applications von Anton Polinger
Hi,
I have read your article and is very helpful for me. The problem i am facing is that your code does not play video with SharpDX and Media Foundation version 3.0.2. If possible can you please provide the same code with the new version of media foundation ver. 3.0.2. I would be very grateful to you.
Hi,
You can look into this piece of code: https://github.com/RolandKoenig/SeeingSharp/blob/master/SeeingSharp.Multimedia/Views/_Video/MediaPlayerComponent.cs
I migrated the code to the most current version of SharpDX there.
Hello, I just tested your code example. It works fine when playing back only one file. But when playing multiple files at once (I need 4 independant streams), things get very slow and laggy. Oddly I can see no major spikes in CPU, GPU or RAM usage… Do you have any idea why this approach is so horribly slow?