008 开发Transform filter 选择自 dankes 的 Blog

Post date: 2015/4/8 上午 08:31:54

Transform filter的輸出pin選擇下游的allocator, 步驟如下 : 1 如果下游的filter可以提供一個allocator, 那麼輸出pin就使用這個allocator, 否則, 輸出pin就創建一個新的allocator。 2 輸出pin通過下游filter的輸入pin上的IMemInputPin::GetAllocatorRequirements.方法來確定下游filter的allocator的要求。 3 輸出pin調用transform filter上的CTransformFilter::DecideBufferSize函數, 這個函數也是一個純虛的函數, virtual HRESULT DecideBufferSize( IMemAllocator * pAllocator,ALLOCATOR_PROPERTIES *pprop) PURE; 這個函數有一個指向allocator的指針, 和一個指向ALLOCATOR_PROPERTIES結構的指針, 這個指針包含了對allocator的屬性的設置, 如果下游的filter對allocator沒有設置屬性, 那麼這個結構就是NULL。 4 在DecideBufferSize方法中, 派生類的函數通過調用IMemAllocator::SetProperties.函數來設置allocator的屬性。 通常, 派生類會根據輸出的格式, 下游filter得要求, 自身得要求來設置allocator的屬性, allocator屬性的設置要符合下游filter的要求, 否則的話, 連接就可能被拒絕。 下面的例子中, HRESULT CRleFilter::DecideBufferSize( IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProp) { AM_MEDIA_TYPE mt; HRESULT hr = m_pOutput->ConnectionMediaType(&mt); if (FAILED(hr)) { return hr; } ASSERT(mt.formattype == FORMAT_VideoInfo); BITMAPINFOHEADER *pbmi = HEADER(mt.pbFormat); pProp->cbBuffer = DIBSIZE(*pbmi) * 2; if (pProp->cbAlign == 0) { pProp->cbAlign = 1; } if (pProp->cBuffers == 0) { pProp->cBuffers = 1; } // Release the format block. FreeMediaType(mt); // Set allocator properties. ALLOCATOR_PROPERTIES Actual; hr = pAlloc->SetProperties(pProp, &Actual); if (FAILED(hr)) { return hr; } // Even when it succeeds, check the actual result. if (pProp->cbBuffer > Actual.cbBuffer) { return E_FAIL; } return S_OK; } 即使SetProperties函數成功, 你也要檢查結果, 以確保滿足你的需要缺省的情況下, 所有的filter都採用CMemAllocator類類分配內存, 這個類從客戶進程的虛擬地址中分配內存, 如果你的filter需要其它的內存, 比如, DirectDraw表面, 你可以派生一個通用的allocator, 你可以從CBaseAllocator類派生一個新的類, 根據不同的pin使用你的派生的新的allocator類, 你需要繼承不同的函數, Input pin: CBaseInputPin::GetAllocator and CBaseInputPin::NotifyAllocator. Output pin: CBaseOutputPin::DecideAllocator. 如果其它的filter拒絕使用你的custom allocator, 你的filter和其它filter連接的時候就會失敗, 第五步 傳遞媒體數據上游filter通過調用filter上輸入pin上的IMemInputPin::Receive方法, 將sample傳遞到filter, filter調用CTransformFilter::Transform方法來處理數據, 注意, 這個方法也是一個純虛的函數, 你要是想用, 你必須提供函數實現。 CTransformFilter::Transform有兩個指針, 一個指向輸入sample, 一直只想輸出smaple, 再調用這個方法之前, 要將sample從輸入sample拷貝到輸出sample。 如果transform返回S_ok, filter就將sample傳遞到下游的filter。 下面的代碼演示了RLE encoder如何實現這個函數的, 你可以參考一下, 當然你的函數和這個是不一樣的。 要注意 HRESULT CRleFilter::Transform(IMediaSample *pSource, IMediaSample *pDest) { // Get pointers to the underlying buffers. BYTE *pBufferIn, *pBufferOut; hr = pSource->GetPointer(&pBufferIn); if (FAILED(hr)) { return hr; } hr = pDest->GetPointer(&pBufferOut); if (FAILED(hr)) { return hr; } // Process the data. DWORD cbDest = EncodeFrame(pBufferIn, pBufferOut); KASSERT((long)cbDest <= pDest->GetSize()); pDest->SetActualDataLength(cbDest); pDest->SetSyncPoint(TRUE); return S_OK; } 需要注意的幾個問題 1 時間戳, CTransformFilter在調用Transform方法之前就給輸出sample打上了時間戳, 它僅僅是從輸入的stample上講時間戳拷貝過來, 不做任何的改動, 如果你的filter需要改動時間戳, 你可以調用輸出sample上的IMediaSample::SetTime方法 2 數據格式的改變上游的filter可以 動態的改變數據的格式, 在改動數據的格式之前, 它要調用你的輸入pin上的IPin::QueryAccept方法, 在filter上, 這個方法的調用會引起CheckInputType, 和CheckTransform的方法的調用。 下游的filter也可以改變數據格式, 機理和這個一樣。 在你的filter中, 需要做兩件事情 1 )要確保QueryAccept返回正確 2 )如果你的filter不接受數據格式的改變, 那麼就在你的filter的Transform方法中調用IMediaSample::GetMediaType.方法, 如果這個方法返回s_ok, 那麼你的filter就要適用數據的改變。 3 )線程, 在CTransformFilter中, filter在Receive方法同步的發送輸出sample。 Filter沒有創建任何的線程來處理數據。 第六步支持COM特性 最後一步是支持com屬性 添加com支持的步驟和前面一樣, 並且在前面也講述的很清楚了, 下面列出必須的幾個要素 1 引用計數Reference Counting, 接口查詢QueryInterface 很簡單, 從基類派生即可 CMyFilter : public CBaseFilter, public IMyCustomInterface { public: DECLARE_IUNKNOWN STDMETHODIMP NonDelegatingQueryInterface(REFIID iid, void **ppv); }; STDMETHODIMP CMyFilter::NonDelegatingQueryInterface(REFIID iid, void **ppv) { if (riid == IID_IMyCustomInterface) { return GetInterface(static_cast<IMyCustomInterface*>(this), ppv); } return CBaseFilter::NonDelegatingQueryInterface(riid,ppv); } 2對象的創建Object Creation CUnknown * WINAPI CRleFilter::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) { CRleFilter *pFilter = new CRleFilter(); if (pFilter== NULL) { *pHr = E_OUTOFMEMORY; } return pFilter; } 模板數組 static WCHAR g_wszName[] = L"My RLE Encoder"; CFactoryTemplate g_Templates[] = { { g_wszName, &CLSID_RLEFilter, CRleFilter::CreateInstance, NULL, NULL } }; int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 3組件的註冊 // Declare media type information. FOURCCMap fccMap = FCC('MRLE'); REGPINTYPES sudInputTypes = { &MEDIATYPE_Video, &GUID_NULL }; REGPINTYPES sudOutputTypes = { &MEDIATYPE_Video, (GUID*)&fccMap }; // Declare pin information. REGFILTERPINS sudPinReg[] = { // Input pin. { 0, FALSE, // Rendered? FALSE, // Output? FALSE, // Zero? FALSE, // Many? 0, 0, 1, &sudInputTypes // Media types. }, // Output pin. { 0, FALSE, // Rendered? TRUE, // Output? FALSE, // Zero? FALSE, // Many? 0, 0, 1, &sudOutputTypes // Media types. } }; // Declare filter information. REGFILTER2 rf2FilterReg = { 1, // Version number. MERIT_DO_NOT_USE, // Merit. 2, // Number of pins. sudPinReg // Pointer to pin information. }; STDAPI DllRegisterServer(void) { HRESULT hr = AMovieDllRegisterServer2(TRUE); if (FAILED(hr)) { return hr; } IFilterMapper2 *pFM2 = NULL; hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void **)&pFM2); if (SUCCEEDED(hr)) { hr = pFM2->RegisterFilter( CLSID_RLEFilter, // Filter CLSID. g_wszName, // Filter name. NULL, // Device moniker. &CLSID_VideoCompressorCategory, // Video compressor category. g_wszName, // Instance data. &rf2FilterReg // Filter information. ); pFM2->Release(); } return hr; } STDAPI DllUnregisterServer() { HRESULT hr = AMovieDllRegisterServer2(FALSE); if (FAILED(hr)) { return hr; } IFilterMapper2 *pFM2 = NULL; hr = CoCreateInstance(CLSID_FilterMapper2, NULL, CLSCTX_INPROC_SERVER, IID_IFilterMapper2, (void **)&pFM2); if (SUCCEEDED(hr)) { hr = pFM2->UnregisterFilter(&CLSID_VideoCompressorCategory, g_wszName, CLSID_RLEFilter); pFM2->Release(); } return hr; } 有時候, 你的filter並不總是通過DLL提供的, 有時, 你可能給一個特定的程序寫了一個特定的filter, 那麼你就可以直接用你的filter, 你可以直接用new方法, 如下 #include "MyFilter.h" // Header file that declares the filter class. // Compile and link MyFilter.cpp. int main() { IBaseFilter *pFilter = 0; { // Scope to hide pF. CMyFilter* pF = new MyFilter(); if (!pF) { printf("Could not create MyFilter.\n"); return 1; } pF->QueryInterface(IID_IBaseFilter, reinterpret_cast<void**>(&pFilter)); } /* Now use pFilter as normal. */ pFilter->Release(); // Deletes the filter. return 0; }