DirectShow / WPF线程问题

时间:2011-02-03 00:44:01

标签: c# wpf directshow

我正在使用WPF和DirectShow编写应用程序并遇到了棘手的问题。我的应用程序在使用DirectShowNet(DS的C#包装类)编写的静态类中通过静态方法Start()和Stop()来使用DS。我的WPF窗口中有一个Windows窗体面板(通过WindowsFormsHost对象),我需要将图形渲染到。以下是应用程序的一般流程:Start()方法构建图形并启动它;我传递了我的Windows窗体面板的句柄,并使用IVideoWindow界面渲染它。 Start()返回,图表在后台运行。在某些时候,调用Stop();此方法会停止图表并将其销毁。

只要我从同一个线程调用Start()和Stop(),一切正常。但是,我需要在我的应用程序中从不同的线程调用它们。在这种情况下,我在破坏图形的代码部分中得到一个例外(具体来说,当我试图枚举过滤器时)。我发现在使用DirectShow时我需要使用多线程公寓。使用Windows窗体应用程序很容易;我只是在我的主方法上抛出一个[MTAThread],一切正常。

对于我的WPF应用程序,这显然不是一个选项。我的解决方法是在需要调用Start()和Stop()时启动新的MTA线程。这摆脱了异常,但又引入了另一个问题。当Start()方法返回时,视频将从渲染面板中消失。如果我在Start()方法结束时设置了Sleep,则视频将一直显示,直到Sleep结束。此外,我已经验证图表在视频消失后继续运行。有没有人对如何进行有任何建议?感谢。

凯文

3 个答案:

答案 0 :(得分:1)

抛出哪个异常?我猜测的是:“调用线程无法访问此对象,因为不同的线程拥有它。”

如果是这种情况,请按照here的说明使用正确的调度员进行通话。

答案 1 :(得分:0)

仅供参考,Windows Forms不支持MTAThread主线程。如果它奏效了,那么你就幸运了。

我相信你应该可以从STA线程中调用DS对象 - 虽然我对DS并不熟悉,但听起来你正在使用windowless mode而且在我看来它会起作用最好的STA。

在这种情况下,为什么不总是从主线程中调用Start / Stop?如果另一个线程需要告诉主线程停止或启动,那么只需让它将任务排队到TaskScheduler.FromCurrentSynchronizationContext以在主线程上运行它。

答案 2 :(得分:0)

好的,所以我之前遇到的问题并没有太大的不同,但是没有使用WPF,所以请注意以下(非常hacky)的建议,加点盐。

以下方法基本上创建了一个完全独立的应用程序线程来运行directshow命令,但是告诉direct show使用WPF应用程序中托管的Windows窗体控件的句柄。

所以,首先我们需要一个虚拟的WinForms表单,我们可以使用它来调用on,但是永远不会被渲染:

/// <summary>
/// Just a dummy invisible form.
/// </summary>
private class DummyForm : Form
{

    protected override void SetVisibleCore(bool value)
    {
        //just override here, make sure that the form will never become visible
        if (!IsHandleCreated)
        {
            CreateHandle();
        }

        value = false;
        base.SetVisibleCore(value);
    }
}

下一步是创建一个我们可以建立消息循环的线程:

//this will need to be a class level variable, since all the directshow
//calls will get invoked on this form
DummyForm dumbForm;
Thread separateThread;


private void CreateDummyForm() 
{

    ManualResetEvent reset = new ManualResetEvent(false);

    //create our thread
    separateThread = new Thread((ThreadStart)
    delegate
    {

        //we need a dummy form to invoke on
        dumbForm = new DummyForm();

        //signal the calling method that it can continue
        reset.Set();

        //now kick off the message loop
        Application.Run(dumbForm);
    });

    //set the apartment state of this new thread to MTA
    separateThread.SetApartmentState(ApartmentState.MTA);
    separateThread.IsBackground = true;
    separateThread.Start();

    //we need to wait for the windowing thread to have initialised before we can 
    //say that initialisation is finished
    reset.WaitOne();

    //wait for the form handle to be created, since this won't happen until the form
    //loads inside Application.Run
    while (!dumbForm.IsHandleCreated)
    {
        Thread.Sleep(0);
    }

}

因此,一旦创建了虚拟表单(及其线程),就可以在MTA上调用调用 应用程序线程如下:

/// <summary>
/// Blank delegate, used to represent any Invoke or BeginInvoke target method.
/// </summary>
public delegate void InvokeHandler();

//i'm assuming here that DSComponent is a class that all your directshow 
//code is in, and externalControl is the WinForms control you have embedded in 
//your application. 
dumbForm.Invoke(new InvokeHandler(delegate 
{
    //maybe something like this?
    DSComponent.Start(externalControl);
}));

//and to stop it...
dumbForm.Invoke(new InvokeHandler(delegate
{
    DSComponent.Stop();
}));

然后,当你完成了Directshow的所有东西时,关闭你的单独的应用程序线程,如下所示:

//to end the separate thread and application loop,
//just close your invisible form
dumbForm.Close();

这种方法的优点是你可以将sandbox directshow整齐地放入一个单独的线程中。缺点是Invoke调用的上下文切换,以及另一个应用程序线程的开销。你可能会对你当前的架构有一些乐趣,但它应该会有所帮助。

让我知道你是如何继续下去的,我对它的运作情况很感兴趣。