views:

1007

answers:

2

This code works in a windows forms application (it shows the preview) but not in a WPF application.

WMEncoder _encoder;
WMEncDataView _preview;
_encoder = new WMEncoder();

IWMEncSourceGroupCollection SrcGrpColl = _encoder.SourceGroupCollection;
IWMEncSourceGroup2 sourceGroup = (IWMEncSourceGroup2)SrcGrpColl.Add("SG_1");
IWMEncVideoSource2 videoDevice = (IWMEncVideoSource2)sourceGroup.AddSource(WMENC_SOURCE_TYPE.WMENC_VIDEO);
videoDevice.SetInput("Default_Video_Device", "Device", "");
IWMEncAudioSource audioDevice = (IWMEncAudioSource)sourceGroup.AddSource(WMENC_SOURCE_TYPE.WMENC_AUDIO);
audioDevice.SetInput("Default_Audio_Device", "Device", "");

IWMEncProfile2 profile = new WMEncProfile2();
profile.LoadFromFile("Recording.prx");
sourceGroup.set_Profile(profile);

_encoder.PrepareToEncode(true);

_preview = new WMEncDataView();
int lpreviewStream = videoDevice.PreviewCollection.Add(_preview);

_encoder.Start();

_preview.SetViewProperties(lpreviewStream, (int)windowsFormsHost1.Handle);
_preview.StartView(lpreviewStream);

I've tried to use the WindowsFormsHost control to get a handle to pass (as shown in the sample), but still no luck.

+1  A: 

I've recently done something similar to integrate an existing DirectShow video component with a new WPF UI. There are a variety of ways to do it, but probably the easiest is to derive a new class from HwndHost. You then simply override a couple of methods, give the window handle to your preview stream and it should all just work. depending on your requirements you may need to handle a couple of Windows messages in your WndProc to handle display changes and redrawing when the video's not playing.

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;

namespace SOQuestion
{
    public class VideoHost : HwndHost
    {
        protected override HandleRef BuildWindowCore(HandleRef hwndParent)
        {
            IntPtr hwndHost = IntPtr.Zero;
            int hostHeight = (int) this.ActualHeight;
            int hostWidth = (int) this.ActualWidth;

            hwndHost = CreateWindowEx(0, "static", "",
                WS_CHILD | WS_VISIBLE,
                0, 0,
                hostHeight, hostWidth,
                hwndParent.Handle,
                (IntPtr)HOST_ID,
                IntPtr.Zero,
                0);

            return new HandleRef(this, hwndHost);
        }

        protected override void DestroyWindowCore(HandleRef hwnd)
        {
            DestroyWindow(hwnd.Handle);
        }

        protected override void OnWindowPositionChanged(Rect rcBoundingBox)
        {
            base.OnWindowPositionChanged(rcBoundingBox);

            _preview.SetViewProperties(lpreviewStream, (int)this.Handle);
        }

        protected override IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
        {
            // Handle a couple of windows messages if required - see MSDN for details

            return base.WndProc(hwnd, msg, wParam, lParam, ref handled);
        }

        [DllImport("user32.dll", EntryPoint = "CreateWindowEx", CharSet = CharSet.Auto)]
        internal static extern IntPtr CreateWindowEx(int dwExStyle,
        string lpszClassName,
        string lpszWindowName,
        int style,
        int x, int y,
        int width, int height,
        IntPtr hwndParent,
        IntPtr hMenu,
        IntPtr hInst,
        [MarshalAs(UnmanagedType.AsAny)] object pvParam);

        [DllImport("user32.dll", EntryPoint = "DestroyWindow", CharSet = CharSet.Auto)]
        internal static extern bool DestroyWindow(IntPtr hwnd); 

        internal const int WS_CHILD = 0x40000000;
        internal const int WS_VISIBLE = 0x10000000;
        internal const int HOST_ID = 0x00000002;
    }
}
Stu Mackellar
This works very well! Cannot access the _preview variable from the control. Do you know a way to enable to resize the preview when the control change size?
loraderon
This is exactly why my sample resets the handle whenever the window position or size changes - this causes the DirectShow renderer to reset. What happens if you call StopView and StartView in succession when responding to the control sizing?
Stu Mackellar
It doesn't work even when calling StopView, SetViewProperties and StartView in OnWindowPositionChanged or a separate button. I have to start/stop the encoding (might be issues with windows media encoder)
loraderon
No idea then I'm afraid. I just had a look at the WME docs - they're not exactly overflowing with detail, are they? This may not be possible - it depends how the preview is implemented internally.
Stu Mackellar
No, they are not.. Thank you for this answer, it does solve my primary problem!
loraderon
Ha! I solved the resizing issue by using WMEncPrepreview instead of WMEncDataView!
loraderon
Good to hear that you got it working!
Stu Mackellar
A: 

Anders - Any chance you could post your code connecting the VideoHost to the WPF UI, and the change you made to Stu's code for the resizing?

yllams
I added xmlns:my="clr-namespace:WpfMediaEncoder" to the Window declaration and then you can add it with <my:VideoHost x:Name="myVideoHost"></my:VideoHost>, then I removed the OnWindowPositionChanged overload and instead used myVideoHost_SizeChanged to set _preview.SetCaptureParent((int)myVideoHost.Handle). The thing that made everything working was to use _preview = (WMEncPrepreview)videoDevice.GetSourcePlugin()
loraderon