2011-02-03 62 views
4

我正在使用WPF和DirectShow編寫一個應用程序,並遇到一個棘手的問題。我的應用程序通過使用DirectShowNet(DS的C#封裝類)編寫的靜態類中的靜態方法Start()和Stop()來使用DS。我在我的WPF窗口(通過WindowsFormsHost對象)中有一個Windows窗體面板,我需要該圖呈現給。這裏是應用程序的一般流程:Start()方法構建圖形並啓動它;我使用IVideoWindow界面傳遞窗口面板的句柄並渲染。 Start()返回並且圖形在後臺運行。在某個時候,Stop()被調用;此方法停止圖形並銷燬它。DirectShow/WPF線程問題

只要我從同一個線程調用Start()和Stop(),一切正常。不過,我需要從我的應用中的不同線程調用它們。在這種情況下,我會在破壞圖形的代碼部分(特別是當我嘗試枚舉過濾器時)出現異常。我發現在使用DirectShow時需要使用多線程的公寓。使用Windows Forms應用程序很容易;我只是在我的主要方法上拋出一個[MTAThread],一切正常。

對於我的WPF應用程序,這顯然不是一個選項。我的解決方法是在需要調用Start()和Stop()時啓動新的MTA線程。這擺脫了例外,但引入了另一個問題。當Start()方法返回時,視頻從渲染面板中消失。如果我在Start()方法的末尾放置了Sleep,則該視頻將可見,直到Sleep結束。另外,我已經驗證了該視頻在視頻消失後繼續運行。有沒有人有任何建議如何進行?謝謝。

凱文

+1

發佈代碼或發佈代碼說明?我會和第一個一起去。 – 2011-02-03 00:48:07

回答

0

僅供參考,Windows窗體不支持MTAThread主線程。如果它有效,那你就很幸運。

我相信你應該可以從STA線程調用DS對象 - 雖然我對DS並不熟悉,但聽起來好像你正在使用windowless mode,在我看來,它最適合STA 。

在這種情況下,爲什麼不總是從主線程調用Start/Stop?如果另一個線程需要告訴主線程停止或啓動,那麼只需讓它將任務排隊到TaskScheduler.FromCurrentSynchronizationContext以在主線程上運行它。

1

拋出哪個異常?我正在猜測一些事情:「調用線程不能訪問這個對象,因爲不同的線程擁有它。」

當出現這種情況時,請使用正確的調度程序進行呼叫,如here所述。

0

好吧,所以我遇到過一個不太類似的問題,但不是WPF,所以採取以下(非常哈希)的建議與一撮鹽。

下面的方法基本上創建了一個完全獨立的應用程序線程來運行directshow命令,但是告訴直接顯示使用您的WPF應用程序中託管的Windows窗體控件的句柄。

所以,首先我們需要一個虛擬的WinForms形成,我們可以用它來調用進行通話,但就是永遠不會得到渲染:

/// <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(); 

這種方法的好處是,你整齊沙箱器directshow到一個單獨的線程。缺點是調用調用的上下文切換,加上另一個應用程序線程的開銷。你可能會有一些有趣的東西進入你當前的架構,但它應該有所幫助。

讓我知道你如何繼續下去,我很好奇它是如何工作的。