DirectShow 架構核心是 Filter (篩選器) 組件化處理多媒體串流。每個 Filter 是一個 COM 物件,透過 Pin (腳位) 連接形成 Filter Graph。本文完整記錄:VC 環境配置、DLL 專案建立、COM 入口函式、類別工廠模板、Filter 基類選擇、Pin 實作、媒體類型協商、資料處理流程、註冊與除錯。


$(DXSDK_DIR)\Include, $(DXSDK_DIR)\Samples\C++\DirectShow\BaseClasses$(DXSDK_DIR)\Lib\x86, $(DXSDK_DIR)\Samples\C++\DirectShow\BaseClasses\Debug (strmbasd.lib)strmbasd.lib msvcrtd.lib quartz.lib vfw32.lib winmm.lib kernel32.lib advapi32.lib version.lib largeint.lib user32.lib gdi32.lib comctl32.lib ole32.lib olepro32.lib oleaut32.lib uuid.lib.ax (Filter 慣例)// MyFilter.def
LIBRARY MyFilter.ax
EXPORTS
DllMain PRIVATE
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllRegisterServer PRIVATE
DllUnregisterServer PRIVATE
#include
#include
// DllMain → BaseClasses dllentry.cpp
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID);
BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved)
{
return DllEntryPoint((HINSTANCE)hModule, dwReason, lpReserved);
}
// 註冊/解除註冊 → BaseClasses dllsetup.cpp
STDAPI DllRegisterServer() { return AMovieDllRegisterServer2(TRUE); }
STDAPI DllUnregisterServer() { return AMovieDllRegisterServer2(FALSE); }
// 單例建立函式
CUnknown* WINAPI CMyFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT* phr)
{
CMyFilter* p = new CMyFilter(NAME("My Filter"), pUnk, phr);
if (!p) *phr = E_OUTOFMEMORY;
return p;
}
// 全域模板陣列
CFactoryTemplate g_Templates[] = {
{
L"My Filter", // 名稱
&CLSID_MyFilter, // CLSID
CMyFilter::CreateInstance, // 建立函式
NULL, // 初始化函式 (可選)
&sudMyFilter // 註冊資訊 (AMOVIESETUP_FILTER)
}
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);
// {1915C5C7-02AA-415F-890F-76D94C85AAF1}
DEFINE_GUID(CLSID_MyFilter,
0x1915c5c7, 0x02aa, 0x415f, 0x89, 0x0f, 0x76, 0xd9, 0x4c, 0x85, 0xaa, 0xf1);
| 基類 | 適用場景 | Pin 配置 |
|---|---|---|
CSource | 源頭 Filter (檔案讀取、攝影機捕獲) | 僅輸出 Pin |
CTransformFilter | 一對一轉換,輸入/輸出媒體類型可能不同 | 1 輸入 + 1 輸出 |
CTransInPlaceFilter | 就地轉換,輸入/輸出媒體類型相同 (如亮度調整) | 1 輸入 + 1 輸出 |
CBaseRenderer / CBaseVideoRenderer | 渲染器 (聲音/視訊輸出) | 僅輸入 Pin |
CBaseFilter | 完全自訂 Pin 數量/拓撲 | 任意 |
選擇原則:先定義功能 → 確定 Pin 數量/媒體類型關係 → 選最貼合基類。本文以 CTransformFilter 為例。
class CMyFilter : public CTransformFilter
{
public:
CMyFilter(TCHAR* pName, LPUNKNOWN pUnk, HRESULT* phr);
~CMyFilter();
// 覆寫純虛函式
HRESULT Transform(IMediaSample* pIn, IMediaSample* pOut); // 核心資料處理
HRESULT CheckInputType(const CMediaType* mtIn); // 輸入類型檢查
HRESULT CheckTransform(const CMediaType* mtIn, const CMediaType* mtOut); // 轉換可行性
HRESULT DecideBufferSize(IMemAllocator* pAlloc, ALLOCATOR_PROPERTIES* pProp); // 輸出緩衝區大小
HRESULT GetMediaType(int iPosition, CMediaType* pMediaType); // 支援的輸出類型列舉
// 非必要但常用
HRESULT StartStreaming(); // 開始串流前初始化
HRESULT StopStreaming(); // 停止串流清理
};
Transform:核心。從 pIn->GetPointer() 取得輸入資料指標,處理後寫入 pOut->GetPointer(),設定 pOut->SetActualDataLength()、SetSyncPoint() 等。CheckInputType:驗證輸入 CMediaType (major/subtype/format) 是否支援。CheckTransform:確認輸入→輸出轉換是否支援。DecideBufferSize:告知下游分配器每個 Sample 需多大緩衝區。GetMediaType:列舉支援的輸出類型,索引 iPosition 從 0 開始,無更多類型時回傳 VFW_S_NO_MORE_ITEMS。AMOVIESETUP_FILTER// 媒體類型陣列
AMOVIESETUP_MEDIATYPE sudMediaTypes[] = {
{ &MEDIATYPE_Video, &MEDIASUBTYPE_RGB24 },
{ &MEDIATYPE_Video, &MEDIASUBTYPE_RGB32 },
};
// 輸出 Pin 定義
AMOVIESETUP_PIN sudOutputPin = {
L"", FALSE, TRUE, FALSE, FALSE, &GUID_NULL, NULL,
2, sudMediaTypes
};
// Filter 定義
AMOVIESETUP_FILTER sudMyFilter = {
&CLSID_MyFilter,
L"My Filter",
MERIT_NORMAL, // 優先權
1, &sudOutputPin // 1 個 Pin 類型
};
regsvr32 MyFilter.ax (需管理員權限)。DbgLog 機制,DEBUGMSG 區域輸出至 DebugView。| 主題 | 關鍵點 |
|---|---|
| 自訂介面 | 繼承 IUnknown,在 NonDelegatingQueryInterface 公開,應用層 QueryInterface 呼叫 |
| 品質控制 | 實作 IQualityControl 回報上游丟幀/延遲 |
| 動態重新連接 | IPin::QueryAccept + ReconnectPin 支援格式切換 |
| 參考時鐘 | 繼承 CBaseReferenceClock 提供同步時鐘 |
| 無 Pin Filter | 繼承 CBaseFilter,實作 IFileSourceFilter 等 |
DirectShow Filter 開發核心在於:正確選擇基類 → 實作媒體類型協商 → 在 Transform 專注資料處理 → 註冊資訊完整描述 Pin/類型。BaseClasses 庫吸收了 80% 的 COM/連接/分配器樣板代碼,開發者可聚焦於多媒體演算法本身。建議配合 GraphEdit 反覆驗證,並參考 SDK 範例 (EzRGB24, Transform) 深化理解。
本文配套原始碼可供下載。