使用擷取圖形產生器建置圖形
[與此頁面相關聯的功能,DirectShow是舊版功能。 它已被 MediaPlayer、IMFMediaEngine,以及媒體基金會中的音訊/視訊擷取 取代。 這些功能已針對 Windows 10 和 Windows 11 進行優化。 Microsoft強烈建議新程式代碼盡可能在媒體 基礎中使用 MediaPlayer、IMFMediaEngine 和 音訊/視訊擷取,而不是 DirectShow。 Microsoft建議使用舊版 API 的現有程式代碼,盡可能改寫成使用新的 API。]
儘管其名稱如此,擷取圖形產生器仍可用於建置許多種類的自定義篩選圖形,而不只是擷取圖形。 本文提供如何使用這個對象的簡短概觀。
擷取圖形產生器會公開 ICaptureGraphBuilder2 介面。 首先,呼叫 CoCreateInstance 來建立擷取圖形產生器和篩選圖形管理員。 然後,使用 Filter Graph 管理員的指標呼叫 ICaptureGraphBuilder2::SetFiltergraph,以初始化擷取圖形產生器,如下所示:
IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuilder = NULL;
// Create the Filter Graph Manager.
HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL,
CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);
if (SUCCEEDED(hr))
{
// Create the Capture Graph Builder.
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2,
(void **)&pBuilder);
if (SUCCEEDED(hr))
{
pBuilder->SetFiltergraph(pGraph);
}
};
連接篩選
ICaptureGraphBuilder2::RenderStream 方法會將鏈結中的兩或三個篩選連接在一起。 一般而言,當每個濾鏡沒有超過一個相同類型的輸入針腳或輸出針腳時,此方法效果最佳。 此討論一開始會忽略 renderStream 的前兩個參數,並將焦點放在最後三個參數。 第三個參數是 IUnknown 指標,可以指定篩選器(作為 IBaseFilter 介面指標)或輸出接點(作為 IPin 介面指標)。 第四個和第五個參數指定 IBaseFilter 指標。 RenderStream 方法會連接鏈結中的所有三個篩選。 例如,假設 A、B和 C 是篩選條件。 假設每個過濾器都有一個輸入接腳和一個輸出接腳。 下列呼叫會將 A 連接到 B,然後將 B 連接到 C:
- 'RenderStream(NULL、NULL、A、B、C)'
所有連線都是「智慧型」,這表示會視需要將其他篩選新增至圖表。 如需詳細資訊,請參閱 Intelligent Connect。 若要只連線兩個篩選,請將中間值設定為 NULL。 例如,此呼叫會將 A 連接到 C:
- 'RenderStream(NULL、NULL、A、NULL、C)'
您可以呼叫 方法兩次來建立較長的鏈結:
- 'RenderStream(NULL, NULL, A, B, C)' 'RenderStream(NULL, NULL, C, D, E)'
如果最後一個參數 NULL,此方法會自動找到預設轉譯器。 它會針對視訊使用 視訊轉譯器,以及音訊 DirectSound 轉譯器。 因此:
- RenderStream(NULL、NULL、A、NULL、NULL)
相當於
- 'RenderStream(NULL、NULL、A、NULL、R)'
其中 R 是適當的轉譯器。 若要連接視訊混合轉譯器篩選,而不是影片轉譯器,不過,您必須明確指定它。
如果您在第三個參數中指定過濾器,而不是針腳,您可能需要指定應該用於連接的輸出針腳。 這是方法前兩個參數的目的。 第一個參數只適用於擷取篩選。 它會指定表示針腳類別的 GUID。 如需類別的完整清單,請參閱 Pin 屬性集。 這兩個類別適用於所有擷取篩選條件:
- PIN_CATEGORY_CAPTURE
- PIN_CATEGORY_PREVIEW
如果擷取濾鏡沒有提供擷取和預覽的個別接腳,RenderStream 方法會插入 Smart Tee 濾鏡,這會將串流分割成擷取流和預覽流。 從應用程式的觀點來看,您可以直接將所有擷取過濾器視為具有個別的接腳,並忽略圖的基礎拓撲。
針對檔案擷取,請將擷取接腳連接到多工濾波器。 要進行即時預覽,將預覽端口連接到渲染器。 如果您切換這兩個類別,圖表可能會在檔案擷取期間卸除過多的畫面格;但如果圖表已正確連接,則會視需要卸除預覽畫面,以維持擷取數據流的輸送量。
下列範例示範如何連接這兩個數據流:
// Capture to file:
pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE, NULL, pCapFilter, NULL, pMux);
// Preview:
pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW, NULL, pCapFilter, NULL, NULL);
某些擷取篩選器也支援隱藏式字幕,由 PIN_CATEGORY_VBI表示。 若要擷取檔案的隱藏式輔助字幕,請將此類別轉譯為多任務篩選器。 若要在預覽視窗中檢視隱藏式輔助字幕,請連線到轉譯器:
// Capture to file:
pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL, pCapFilter, NULL, pMux);
// Preview on screen:
pBuilder->RenderStream(&PIN_CATEGORY_VBI, NULL, pCapFilter, NULL, NULL);
RenderStream 的第二個參數 用於識別媒體類型,通常是以下其中一項:
- 媒體類型_音頻
- 影片類型
- MEDIATYPE_Interleaved (DV)
每當篩選的輸出針腳支援慣用媒體類型的列舉時,您就可以使用此參數。 針對檔案來源,擷取圖形產生器會視需要自動新增剖析器篩選,然後在剖析器上查詢媒體類型。 (如需範例,請參閱 重新壓縮 AVI 檔案。)此外,如果鏈結中的最後一個濾鏡有數個輸入接腳,此方法會嘗試列舉它們的媒體類型。 不過,並非所有篩選都支援這項功能。
尋找篩選和 Pins 上的介面
建置圖表之後,您通常需要找出圖表中篩選器和接針所公開的各種介面。 例如,擷取篩選可能會公開 IAMDroppedFrames 介面,而篩選的輸出針腳可能會公開 IAMStreamConfig 介面。
尋找介面最簡單的方式是使用 ICaptureGraphBuilder2::FindInterface 方法。 此方法會遍歷圖(篩選和釘選),直到找到所需的介面為止。 您可以指定搜尋的起點,而且您可以將搜尋限制為從起點篩選上游或下游。
下列範例會在影片預覽針腳上搜尋 IAMStreamConfig 介面:
IAMStreamConfig *pConfig = NULL;
HRESULT hr = pBuild->FindInterface(
&PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video,
pVCap,
IID_IAMStreamConfig,
(void**)&pConfig
);
if (SUCCESSFUL(hr))
{
/* ... */
pConfig->Release();
}
注意
本主題 "在篩選器或釘上尋找介面" 展示使用 IGraphBuilder 介面的替代方法,而非使用 ICaptureGraphBuilder2。 使用的方法取決於您的應用程式。 如果您的應用程式已經使用 ICaptureGraphBuilder2 來建置圖形,則 ICaptureGraphBuilder2::FindInterface 是很好的方法。 否則,請考慮使用 IGraphBuilder 方法。
尋找釘子
在較不常見的情況下,您可能需要在濾鏡上定位個別的釘選,不過在大部分情況下,RenderStream 和 FindInterface 方法會省去您的麻煩。 如果您需要在篩選器上尋找特定釘選,ICaptureGraphBuilder2::FindPin 輔助方法很有用。 指定類別、媒體類型(影片或音訊)、方向,以及針腳是否需要保持未連接。
例如,下列程式代碼會在捕捉濾鏡中搜尋未連接的影片預覽引腳:
IPin *pPin = NULL;
hr = pBuild->FindPin(
pCap, // Pointer to the filter to search.
PINDIR_OUTPUT, // Search for an output pin.
&PIN_CATEGORY_PREVIEW, // Search for a preview pin.
&MEDIATYPE_Video, // Search for a video pin.
TRUE, // The pin must be unconnected.
0, // Return the first matching pin (index 0).
&pPin); // This variable receives the IPin pointer.
if (SUCCESSFUL(hr))
{
/* ... */
pPin->Release();
}
相關主題