我正在使用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结束。此外,我已经验证图表在视频消失后继续运行。有没有人对如何进行有任何建议?感谢。
凯文
答案 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调用的上下文切换,以及另一个应用程序线程的开销。你可能会对你当前的架构有一些乐趣,但它应该会有所帮助。
让我知道你是如何继续下去的,我对它的运作情况很感兴趣。