Preview process
Both MPlatform and MFormats SDK previews are based on Direct3D. But by design it requires a handle of a control to draw video on - you use PreviewWindowSet method for this.
When you work with WPF, controls don't have handles, so the approach to get video preview with MPreview (or MFPreview in MFormats SDK) is different.
The following description is common for both MFormats and MPlatform SDKs and followed with comments if something is different:
1. You create an MFPreview (MPreview for MPlatform SDK) object:
_myPreview = new MFPreviewClass();
2. You specify "wpf_preview" mode for MFPreview with:
_myPreview.PropsSet("wpf_preview", "true");
3. Enable preview
_myPreview.PreviewEnable("", 1, 1);
4. Use OnEventSafe for MFPreview object:
_myPreview.OnEventSafe += _myPreview_OnEventSafe;
5. Create a D3DImage object:
private D3DImage previewSource; previewSource = new D3DImage();
6. Start playback of your source.
The moment you a frame is sent to preview in MFormats SDK with:
_myPreview.ReceiverFramePut(sourceFrame, -1, "");
or after you call
_myPreview.ObjectStart(myPlaylist);
and frames start to reach MPreview object, a "wpf_nextframe" event is raised. Note please that previewImage in the following code is an Image control on your WPF window.
private IntPtr _mSavedPointer; // required to update image context private void _myPreview_OnEventSafe(string bsChannelId, string bsEventName, string bsEventParam, object pEventObject) { if (bsEventName == "wpf_nextframe") { IntPtr pEventObjectPtr = Marshal.GetIUnknownForObject(pEventObject); if (pEventObjectPtr != _mSavedPointer) { if (_mSavedPointer != IntPtr.Zero) Marshal.Release(_mSavedPointer); _mSavedPointer = pEventObjectPtr;
Marshal.AddRef(_mSavedPointer);
previewSource.Lock(); previewSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero); //enableSoftwareFallback = true,
//enable an ability D3DImage display the Direct3D content in software renderer like RDP previewSource.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _mSavedPointer, true);
previewSource.Unlock(); previewImage.Source = previewSource; } if (pEventObjectPtr != IntPtr.Zero) Marshal.Release(pEventObjectPtr); previewSource.Lock(); previewSource.AddDirtyRect(new Int32Rect(0, 0, _previewSource.PixelWidth, _previewSource.PixelHeight)); previewSource.Unlock(); } Marshal.ReleaseComObject(pEventObject); }
Please, pay your attention that if you design an application without GUI, it's needed to set the following prop as "false" for preview to enable the event rising:
_myPreview.PropsSet("events.use_window", "false");
WPF-related preview properties
There are several properties for WPF preview for MPreview and MFPreview objects.
You can find them in system registry for MPlatform SDK:
HKEY_CURRENT_USER\SOFTWARE\Medialooks\MPlatform\MPreview
And for MFormats SDK:
HKEY_CURRENT_USER\SOFTWARE\Medialooks\MFormats\MFPreview
Or specify them with the PropsSet method of IMProps (MPlatform) or IMFProps (MFormats) interfaces.
The properties are:
"wpf_preview"
It just enables WPF preview mode. So if "wpf_preview" = true then MPreview object has a possibility to use OnEvent that you use in your code now and sends a pointer to 3D Surface.
"wpf_preview.downscale"
By default, as a result, you have a full-frame sized D3DImage in the event. Because this is processed by DirectX there is no much effect on CPU usage. But still, there is a possibility to reduce size.
For example, if your preview panel is 300x200, why do you need to feed it with 1920x1080 image? For this purpose, there is "wpf_preview.downscale" property. With "1" as value, result D3DImage is like it lost each other row (so the horizontal quality is the same, but vertical is reduced 2 times).
With "2" it cuts each other vertical and horizontal lines. So, as a result, the output 3D surface is 4 times smaller than the original one.
It leads to lower CPU usage for drawing the image. But it is useful only for small preview panels.
In the case of the GPU pipeline, this property works only with an Interlaced video format and removes the whole frame field instead of frame rows.
"wpf_preview.update"
The property sets a number of frames before a context is updated - so a blank frame is returned. As a result, you have a "flickering" preview. By default, it is set to 30 so once per 30 frames there is such an artifact.
We set it to "30" by default because with our investigations the problem may appear at random. But it looks like it depends on the overall performance of a video card so it may work fine with "0" - no flickering at all should be in this case.
"wpf_preview.sync_texture"
This property, once set to "true", enables a mode for WPF preview where underlying textures are synchronized between multiple previews. This mode guarantees that all the textures are processed by correct preview objects. So you should use this property enabled if you have multiple preview objects that are connected with different sources - then you avoid missing or wrong frames on previews.
Possible issues
In MPlatform WPF Preview does not restart after close/start of the source object. How to solve?
When you close MLive object the matched MPreview keeps referring to its initial source. So you have to close the preview object with
myPreview.ObjectClose();
together with its source object and then start them together again with
mySource.ObjectStart(null); myPreview.ObjectStart(null);
In WPF Preview goes black after the computer is locked and is not returning back when the computer is unlocked, but the file is still playing.
You should wait for the event when a computer is unlocked
and refresh the buffer and set the last frame.
PreviewSurface.IsFrontBufferAvailableChanged += (sender, args) =>
{
if ((bool)args.NewValue)
{
PreviewSurface.Dispatcher.Invoke(new Action(() =>
{
try
{
PreviewSurface.Lock();
PreviewSurface.SetBackBuffer(D3DResourceType.IDirect3DSurface9, IntPtr.Zero);
PreviewSurface.SetBackBuffer(D3DResourceType.IDirect3DSurface9, _mSavedPointer);
PreviewSurface.AddDirtyRect(new Int32Rect(0, 0, PreviewSurface.PixelWidth, PreviewSurface.PixelHeight));
PreviewSurface.Unlock();
}
catch (Exception ex)
{
//...
}
}), DispatcherPriority.Render);
}
};
You also can check out our WPF MVVM Live Source Sample.