DirectShow/WPF 线程问题
我正在使用 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() 方法返回时,视频从渲染面板中消失。如果我将 Sleep 放在 Start() 方法的末尾,则视频将可见,直到 Sleep 结束。此外,我还验证了视频消失后图表继续运行。有人对如何进行有任何建议吗?谢谢。
凯文
I am writing an app using WPF and DirectShow and have run into a sticky issue. My application utilizes DS through static methods Start() and Stop() in a static class written using DirectShowNet (a C# wrapper class for DS). I have a Windows Forms panel in my WPF window (via a WindowsFormsHost object) that I need the graph to render to. Here is the general flow of the app: The Start() method builds the graph and starts it; I pass the handle of my windows form panel and render to it using the IVideoWindow interface. Start() returns and the graph runs in the background. At some point, Stop() is called; this method stops the graph and destroys it.
Everything works fine as long as I call Start() and Stop() from the same thread. However, I will need to call them from different threads in my app. When this is the case, I get an exception in the part of code that destroys the graph (specifically, when I am attempting to enumerate the filters). I discovered that I need to use a Multithreaded Apartment when working with DirectShow. This is easy with a Windows Forms app; I just throw a [MTAThread] on my main method and everything works.
For my WPF app, this is apparently not an option. My workaround has been to launch new MTA threads when I need to call Start() and Stop(). This gets rid of the exception, but has introduced another problem. When the Start() method returns, the video disappears from the render panel. If I put a Sleep at the end of the Start() method, the video will be visible until the Sleep ends. In addition, I have verified that the graph continues to run after the video disappears. Does anyone have any advice as to how to proceed? Thanks.
Kevin
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
抛出哪个异常?我猜测类似这样的事情:“调用线程无法访问该对象,因为另一个线程拥有它。”
在这种情况下,请使用正确的调度程序来拨打电话,如下所述 在这里。
Which exception is thrown? I'm guessing something along the likes of: "The calling thread cannot access this object because a different thread owns it."
When this is the case, use a correct dispatcher to do your calls, as explained here.
仅供参考,Windows 窗体不支持
MTAThread
主线程。如果它有效,那么你很幸运。我相信您应该能够从 STA 线程调用 DS 对象就好了 - 尽管我对 DS 不太熟悉,但听起来您正在使用 无窗口模式 在我看来,它最适合 STA。
在这种情况下,为什么不总是从主线程调用
Start
/Stop
呢?如果另一个线程需要告诉主线程停止或启动,则只需将其任务放入TaskScheduler.FromCurrentSynchronizationContext
中即可在主线程上运行。FYI, Windows Forms doesn't support a
MTAThread
main thread. If it worked, then you just got lucky.I believe you should be able to invoke DS objects from STA threads just fine - though I'm not that familiar with DS, it sounds like you're using windowless mode and it seems to me that it would work best with STA.
In that case, why not always call
Start
/Stop
from your main thread? If another thread needs to tell the main thread to stop or start, then just have it queue a task to aTaskScheduler.FromCurrentSynchronizationContext
to run it on the main thread.好的,我以前遇到过一个不太相似的问题,但不是 WPF,所以请对以下(非常老套的)建议持保留态度。
以下方法基本上创建一个完全独立的应用程序线程来运行 directshow 命令,但告诉 direct show 使用 WPF 应用程序中托管的 Windows 窗体控件的句柄。
因此,首先我们需要一个虚拟的 WinForms 表单,我们可以用它来调用调用,但它永远不会被渲染:
下一步是创建一个可以在其上放置消息循环的线程:
因此,一旦虚拟表单(及其线程)已经创建,您可以在 MTA 上调用
像这样关闭应用程序线程:
然后,当您完成 Directshow 的工作后,像这样关闭单独的应用程序线程:
这种方法的优点是您可以将 directshow 巧妙地沙箱到一个单独的线程中。缺点是 Invoke 调用的上下文切换,以及拥有另一个应用程序线程的开销。将其硬塞到当前的架构中可能会很有趣,但这应该会有所帮助。
让我知道你的进展如何,我很好奇它的效果如何。
Ok, so I've encountered a problem not too dissimilar before, but not with WPF, so take the following (very hacky) suggestion with a pinch of salt.
The following method basically creates an entirely separate application thread to run directshow commands in, but tells direct show to use the handle of the windows forms control hosted in your WPF application.
So, first we need a dummy WinForms form that we can use to invoke calls on, but that is never going to get rendered:
Next step is to create a thread that we can put a message loop on:
So, once the dummy form (and its thread) have been created, you can invoke calls on the MTA
application thread like so:
Then, when you're all done with the Directshow stuff, shutdown your separate application thread like so:
Advantage of this approach is that you neatly sandbox directshow into a separate thread. Disadvantage is the context switch of the Invoke calls, plus the overhead of having another application thread. You may have some fun shoehorning this into your current architecture, but it should help.
Let me know how you get on, I am intrigued as to how well this works.