替代視訊轉譯器

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

本主題描述如何撰寫 DirectShow 的自訂視訊轉譯器。

注意

建議您為視訊混合轉譯器 (VMR) 或 增強的視 訊轉譯器 (EVR) 撰寫外掛程式配置器呈現器,而不是撰寫自訂視訊轉譯器。 這種方法會為您提供 VMR/EVR 的所有優點,包括支援 DirectX 影片加速 (DXVA) 、硬體反交錯和畫面逐步執行,而且可能比自訂視訊轉譯器更強固。 如需詳細資訊,請參閱下列主題:

 

撰寫替代轉譯器

Microsoft DirectShow 提供以視窗為基礎的視訊轉譯器;它也會在執行時間安裝中提供全螢幕轉譯器。 您可以使用 DirectShow 基類來撰寫替代的視訊轉譯器。 若要讓替代轉譯器與 DirectShow 型應用程式正確互動,轉譯器必須遵守本文中所述的指導方針。 您可以使用 CBaseRendererCBaseVideoRenderer 類別,在實作替代視訊轉譯時協助遵循這些指導方針。 由於 DirectShow 的持續開發,請定期檢閱您的實作,以確保轉譯器與最新版 DirectShow 相容。

本主題討論轉譯器負責處理的許多通知。 DirectShow 通知的簡短檢閱可能有助於設定階段。 DirectShow 中基本上有三種通知:

  • 串流通知,這些是媒體資料流程中發生的事件,並且會從一個篩選準則傳遞至下一個篩選。 這些可以是開始排清、端排清或串流結束通知,並透過在下游篩選的輸入針腳上呼叫適當的方法傳送 (,例如 IPin::BeginFlush) 。
  • 篩選圖表通知,這些是從篩選傳送至篩選圖形管理員的事件,例如 EC_COMPLETE。 這可藉由在 Filter Graph Manager 上呼叫 IMediaEventSink::Notify 方法來完成。
  • 由控制應用程式從篩選圖形管理員擷取的代理程式更新。 應用程式會呼叫 Filter Graph Manager 上的 IMediaEvent::GetEvent 方法,以擷取這些事件。 篩選圖形管理員通常會通過它接收給應用程式的事件。

本主題討論轉譯器篩選在處理接收的資料流程通知以及傳送適當的篩選圖形通知時的責任。

處理資料流程結束和排清通知

串流結束通知會在上游篩選 (開始,例如來源篩選) 當篩選偵測到無法傳送更多資料時。 它會通過圖形中的每個篩選,最終會在轉譯器結束,負責後續將 EC_COMPLETE 通知傳送至篩選圖形管理員。 轉譯器在處理這些通知時有特殊責任。

當上游篩選呼叫其輸入針腳的 IPin::EndOfStream 方法時,轉譯器會收到資料流程結束通知。 轉譯器應該注意此通知,並繼續轉譯它已收到的任何資料。 一旦收到所有剩餘的資料,轉譯器應該傳送 EC_COMPLETE 通知給篩選圖形管理員。 每次到達資料流程結尾時,轉譯器應該只傳送一次 EC_COMPLETE 通知。 此外,除非篩選圖表正在執行,否則絕對不能傳送 通知EC_COMPLETE 通知。 因此,如果在來源篩選傳送串流結束通知時暫停篩選圖表,則除非最後執行篩選圖形,否則不應該傳送 EC_COMPLETE

在發出資料流程結束通知之後,應該拒絕對 IMemInputPin::Receive或 IMemInputPin::ReceiveMultiple 方法的任何呼叫。 E_UNEXPECTED 是在此案例中傳回的最適當錯誤訊息。

當篩選圖表停止時,應該清除任何快取的資料流程結束通知,且下次啟動時不會重新傳送。 這是因為 Filter Graph 管理員一律會先暫停所有篩選,再執行它們,以便進行適當的排清。 例如,如果篩選圖表已暫停,並收到串流結束通知,然後篩選圖表就會停止,則轉譯器不應該在後續執行時傳送 EC_COMPLETE 通知。 如果沒有發生搜尋,來源篩選準則會在執行狀態之前的暫停狀態期間自動傳送另一個串流結束通知。 如果另一方面,當篩選圖形停止時發生搜尋,則來源篩選可能會有要傳送的資料,因此不會傳送資料流程結束通知。

視訊轉譯器通常取決於串流結束通知,而不是傳送 EC_COMPLETE 通知。 例如,如果資料流程已完成播放 (,則會) 傳送串流結束通知,並將另一個視窗拖曳到視訊轉譯器視窗上,就會產生一些 WM_PAINT 視窗訊息。 執行視訊轉譯器的一般作法是根據假設要繪製的另一個畫面,在收到 WM_PAINT (訊息時避免重新繪製目前的畫面格) 。 不過,當傳送資料流程結束通知時,轉譯器處於等候狀態;它仍在執行中,但請注意它不會收到任何其他資料。 在這些情況下,轉譯器會自訂繪製播放區域黑色。

處理排清是轉譯器的額外複雜功能。 Flushing 是透過一組稱為BeginFlushEndFlushIPin方法來執行。 排清基本上是轉譯器必須處理的其他狀態。 來源篩選在未呼叫EndFlush 的情況下呼叫 BeginFlush合法,因此希望狀態是簡短且離散的;不過,轉譯器必須在排清轉換期間正確處理它收到的資料或通知。

呼叫 BeginFlush 之後收到的任何資料都應該立即拒絕,方法是傳回 S_FALSE。 此外,在清除轉譯器時,也應該清除任何快取的資料流程結束通知。 轉譯器通常會排清以回應搜尋。 排清可確保在傳送全新樣本之前,會先從篩選圖形清除舊資料。 (一般而言,串流兩個區段的播放,一個接著一個,最好透過延後命令來處理,而不是等待一個區段完成,然後發出 seek 命令。)

處理狀態變更和暫停完成

當轉譯器篩選準則的狀態變更時,轉譯器篩選與篩選圖形中任何其他篩選的行為相同,但有下列例外狀況。 暫停之後,轉譯器會有一些已排入佇列的資料,以供後續執行時轉譯。 當影片轉譯器停止時,它會保留此佇列資料。 這是 DirectShow 規則的例外狀況,篩選圖形停止時,篩選準則不應保留任何資源。

此例外狀況的原因是藉由保存資源,轉譯器一律會有一個影像,如果它收到 WM_PAINT 訊息,就會重新繪製視窗。 它也有一個映射來滿足要求目前映射複本的方法,例如 CBaseControlVideo::GetStaticImage。 保留資源的另一個效果是,保留至映射會停止配置器遭到取消認可,進而讓下一個狀態變更更快速,因為已配置映射緩衝區。

視訊轉譯器應該只在執行時轉譯和釋放範例。 暫停時,篩選準則可能會 (轉譯它們,例如,在視窗) 繪製靜態海報影像時,但不應該放開它們。 雖然音訊轉譯器可以執行其他活動,例如準備波浪裝置,例如) ,但暫停 (時不會執行任何轉譯。 應該轉譯樣本的時間,方法是將範例中的資料流程時間與傳遞為 IMediaControl::Run 方法的參數傳遞的參考時間結合在一起。 轉譯器應該拒絕開始時間小於或等於結束時間的樣本。

當應用程式暫停篩選圖形時,篩選圖表不會從其 IMediaControl::P ause 方法傳回,直到轉譯器上有排入佇列的資料為止。 為了確保這樣做,當轉譯器暫停時,如果尚未等候轉譯的資料,它應該傳回S_FALSE。 如果資料已排入佇列,則可以傳回 S_OK

篩選圖表管理員會在暫停篩選圖形時檢查所有傳回值,以確保轉譯器已排入佇列。 如果一或多個篩選尚未就緒,則篩選圖形管理員會呼叫 IMediaFilter::GetState來輪詢圖表中的篩選。 GetState方法需要逾時參數。 篩選 (通常是轉譯器) ,在完成狀態變更之前仍等候資料抵達,如果GetState方法到期,則會傳回VFW_S_STATE_INTERMEDIATE。 一旦資料到達轉譯器, GetState 應該立即傳回 S_OK

在中繼和已完成狀態中,報告篩選狀態將會State_Paused。 只有傳回值表示篩選是否真的就緒。 如果轉譯器正在等候資料抵達,其來源篩選會傳送資料流程結束通知,則也應該完成狀態變更。

一旦所有篩選實際上都有等待轉譯的資料,篩選圖表就會完成其暫停狀態變更。

處理終止

視訊轉譯器必須正確地處理使用者的終止事件。 這表示正確地隱藏視窗,並知道後續強制顯示視窗時該怎麼做。 此外,當轉譯器視窗損毀 (或更精確時,當轉譯器從篩選圖形中移除時,視訊轉譯器必須通知篩選圖形管理員) 釋放資源。

如果使用者按下 ALT+F4) 關閉視訊視窗 (,慣例就是立即隱藏視窗,並將 EC_USERABORT 通知傳送給篩選圖形管理員。 此通知會傳遞至應用程式,這會停止播放圖形。 傳送 EC_USERABORT之後,視訊轉譯器應該拒絕傳遞給它的任何其他樣本。

圖形停止旗標應該由轉譯器保留,直到其後續停止為止,此時應該重設它,讓應用程式可以覆寫使用者動作,並視需要繼續播放圖形。 如果在影片執行時按下 ALT+F4,則會隱藏視窗,而且傳遞的所有進一步範例都會遭到拒絕。 如果視窗後續顯示 (可能是透過 IVideoWindow::p ut_Visible) ,則應該不會產生 任何EC_REPAINT 通知。

影片轉譯器也應該在影片轉譯器終止時,將 EC_WINDOW_DESTROYED 通知傳送至篩選圖形。 事實上,當轉譯器的 IBaseFilter::JoinFilterGraph 方法呼叫時,最好使用 null 參數 (指出轉譯器即將從篩選圖形中移除) ,而不是等到實際的視訊視窗終結為止。 傳送此通知可讓 Filter Graph 管理員中的外掛程式散發者將相依于視窗焦點的資源傳遞給其他篩選,例如音訊裝置。

處理動態格式變更

在某些情況下,轉譯器的上游篩選可能會嘗試在播放視訊時變更視訊格式。 它通常是起始動態格式變更的視訊解壓縮程式。

嘗試動態變更格式的上游篩選應該一律在轉譯器輸入釘選上呼叫 IPin::QueryAccept 方法。 視訊轉譯器有一些引數,說明其應該支援的動態格式變更類型。 至少應該允許上游篩選變更調色盤。 當上游篩選變更媒體類型時,會將媒體類型附加至以新格式傳遞的第一個範例。 如果轉譯器在用於轉譯的佇列中保存樣本,則除非轉譯具有類型變更的樣本,否則不應該變更格式。

視訊轉譯器也可以從解碼器要求格式變更。 例如,它可能會要求解碼器提供與 DirectDraw 相容的格式與負 biHeight。 當轉譯器暫停時,它應該在上游釘選上呼叫 QueryAccept ,以查看解碼器可以提供的格式。 不過,解碼器可能不會列舉它可以接受的所有類型,因此轉譯器應該提供某些類型,即使解碼器未公告它們也一樣。

如果解碼器可以切換到要求的格式,它會從QueryAccept傳回S_OK。 轉譯器接著會將新的媒體類型附加至上游配置器上的下一個媒體範例。 若要這樣做,轉譯器必須提供自訂配置器,以實作私用方法,以便將媒體類型附加至下一個範例。 (在此私用方法中,呼叫 IMediaSample::SetMediaType 以設定 type.)

轉譯器的輸入針腳應該會在 IMemInputPin::GetAllocator 方法中傳回轉譯器的自訂配置器。 覆寫 IMemInputPin::NotifyAllocator ,以便在上游篩選準則不使用轉譯器的配置器時失敗。

使用某些解碼器時,將 biHeight 設定為 YUV 類型的正數會導致解碼器向下繪製影像。 (不正確,而且應該視為解碼器.) 中的 Bug

每當視訊轉譯器偵測到格式變更時,應該傳送 EC_DISPLAY_CHANGED 通知。 大部分的視訊轉譯器會在連線期間挑選格式,以便透過 GDI 有效率地繪製格式。 如果使用者在不重新開機電腦的情況下變更目前的顯示模式,轉譯器可能會發現本身有不正確的影像格式連線,而且應該傳送此通知。 第一個參數應該是需要重新連線的針腳。 Filter Graph 管理員會安排要停止的篩選圖形,並重新連接釘選。 在後續重新連線期間,轉譯器可以接受更適當的格式。

每當視訊轉譯器偵測到資料流程中的調色盤變更時,應該將 EC_PALETTE_CHANGED 通知傳送至篩選圖形管理員。 DirectShow 視訊轉譯器會偵測調色盤是否真的以動態格式變更。 視訊轉譯器不僅能篩選出傳送 的EC_PALETTE_CHANGED 通知數目,還能減少所需的選擇區建立、安裝和刪除量。

最後,視訊轉譯器也可能偵測到視訊的大小已變更,在此情況下,它應該傳送 EC_VIDEO_SIZE_CHANGED 通知。 應用程式可能會使用此通知來交涉複合檔案中的空間。 實際的視訊維度可透過 IBasicVideo 控制項介面取得。 DirectShow 轉譯器會偵測影片是否已在傳送這些事件之前實際變更大小。

處理永續性屬性

透過 IBasicVideoIVideoWindow 介面設定的所有屬性都是跨連線的持續性。 因此,中斷和重新連接轉譯器應該不會對視窗大小、位置或樣式造成任何影響。 不過,如果視訊維度在連接之間變更,轉譯器應該將來源和目的地矩形重設為預設值。 來源和目的地位置是透過 IBasicVideo 介面來設定。

IBasicVideoIVideoWindow都提供足夠的屬性存取權,讓應用程式以永續性格式儲存和還原介面中的所有資料。 這對於必須在編輯會話期間儲存篩選圖形的確切組態和屬性,並在稍後還原它們的應用程式很有用。

處理EC_REPAINT通知

只有在轉譯器暫停或停止時,才會傳送 EC_REPAINT 通知。 此通知會向篩選圖形管理員發出訊號,表示轉譯器需要資料。 如果篩選圖表在收到其中一個通知時停止,它會暫停篩選圖形,等候所有篩選準則透過呼叫 GetState () 接收資料,然後再次停止。 停止時,視訊轉譯器應該保留影像,以便處理後續 WM_PAINT 訊息。

因此,如果視訊轉譯器在停止或暫停時收到 WM_PAINT 訊息,而且沒有繪製視窗的訊息,則應該將 EC_REPAINT 傳送至篩選圖形管理員。 如果暫停時收到 EC_REPAINT 通知,則 Filter Graph 管理員會呼叫 IMediaPosition::p ut_CurrentPosition ,並將目前的位置 (搜尋至目前的位置) 。 這會導致來源篩選排清篩選圖形,並讓新的資料透過篩選圖形傳送。

轉譯器一次只能傳送其中一個通知。 因此,轉譯器傳送通知之後,它應該確保不會再傳送一些樣本。 執行這項操作的慣例方式是讓旗標表示可以傳送重新繪製,這會在傳送 EC_REPAINT 通知之後關閉。 在傳遞資料或排清輸入針腳時,應該重設此旗標,但如果輸入針腳上發出資料流程結尾的訊號,則不應該重設。

如果轉譯器未監視其 EC_REPAINT 通知,它會在篩選圖形管理員中填入 EC_REPAINT要求, (處理) 成本相對昂貴。 例如,如果轉譯器沒有要繪製的影像,而另一個視窗則會在全拖曳作業中跨轉譯器的視窗拖曳,則轉譯器會收到多個 WM_PAINT 訊息。 只有第一個應該從轉譯器產生 EC_REPAINT 事件通知至篩選圖形管理員。

轉譯器應該將其輸入針腳當做第一個參數傳送至 EC_REPAINT 通知。 如此一來,附加的輸出針腳將會查詢 IMediaEventSink,如果支援,則會先將 EC_REPAINT 通知傳送到該處。 這可讓輸出釘選在必須觸碰篩選圖形之前處理重繪。 如果篩選圖表已停止,則不會這麼做,因為不會從已認可的轉譯器配置器取得任何緩衝區。

如果輸出釘選無法處理要求,且篩選圖表正在執行,則會忽略 EC_REPAINT 通知。 輸出針腳必須從IMediaEventSink::Notify傳回S_OK,以通知它已成功處理重繪要求。 輸出釘選會在 Filter Graph Manager 背景工作執行緒上呼叫,以避免轉譯器直接呼叫輸出釘選,因此會避開任何死結問題。 如果篩選圖表已停止或暫停,且輸出不會處理要求,則會完成預設處理。

處理Full-Screen模式中的通知

篩選圖形中的 IVideoWindow 外掛程式散發者 (PID) 會管理全螢幕播放。 它會為專家的全螢幕轉譯器交換視訊轉譯器、將轉譯器的視窗延展至全螢幕,或讓轉譯器直接實作全螢幕播放。 若要在全螢幕通訊協定中互動,視訊轉譯器應該會在視窗啟用或停用時傳送 EC_ACTI加值稅E 通知。 換句話說,應該針對轉譯器接收的每個WM_ACTI加值稅EAPP訊息傳送 EC_ACTI加值稅E 通知。

當轉譯器用於全螢幕模式時,這些通知會管理切換進入和移出該全螢幕模式。 當使用者按下 ALT+TAB 切換至另一個視窗時,通常會發生視窗停用,DirectShow 全螢幕轉譯器會用來當做提示返回一般轉譯模式。

EC_ACTI加值稅E 通知在切換出全螢幕模式時傳送至 Filter Graph 管理員時,Filter Graph Manager 會將 EC_FULLSCREEN_LOST 通知傳送至控制應用程式。 例如,應用程式可能會使用此通知來還原全螢幕按鈕的狀態。 DirectShow會在內部使用EC_ACTI加值稅E通知,以管理視訊轉譯器提示上的全螢幕切換。

通知摘要

本節列出轉譯器可以傳送的篩選圖形通知。

事件通知 Description
EC_ACTI加值稅E 視訊轉譯器會以全螢幕轉譯模式傳送給每個收到的WM_ACTI加值稅EAPP訊息。
EC_COMPLETE 轉譯器在轉譯所有資料之後傳送。
EC_DISPLAY_CHANGED 當顯示格式變更時,視訊轉譯器傳送。
EC_PALETTE_CHANGED 每當視訊轉譯器偵測到資料流程中的調色盤變更時傳送。
EC_REPAINT 收到WM_PAINT訊息且沒有資料可供顯示時,由停止或暫停的視訊轉譯器傳送。 這會導致 Filter Graph 管理員產生要繪製到顯示器的框架。
EC_USERABORT 由視訊轉譯器傳送,以通知使用者要求關閉 (例如,使用者關閉視訊視窗) 。
EC_VIDEO_SIZE_CHANGED 每當偵測到原生視訊大小變更時,就會由視訊轉譯器傳送。
EC_WINDOW_DESTROYED 移除或終結篩選時,視訊轉譯器傳送給視訊轉譯器,讓相依于視窗焦點的資源可以傳遞至其他篩選。

 

撰寫視訊轉譯器