Directshow的中filter (濾鏡) 其實可以看作是一個功能模組,它規定了標準的介面,用於不同的濾鏡直接傳遞媒體資料。各個濾鏡也不用知道其他濾鏡具體是怎麼樣工作的,他只要知道傳過來的 是什麼樣格式的媒體資料就行了,然後他自己又由什麼樣的格式傳出去給下個濾鏡。
一個典型的在播放器中播放mp3檔案的filter出來大概是這樣子到;
檔案源濾鏡(File source filter)-》 mp3 解碼filter -》音頻渲染filter
上面這個是加上dsp 音效處理的。
最開始的叫做 “源濾鏡” ,最後的叫做“renderer filter” ,每個播放流程,這兩種類型的濾鏡都是一定存在的,中間 到按照功能等分類,還可以分為“transfer filter”(變換濾鏡)、“分離濾鏡(把音視頻資料分離)”等等各種類型。有的還能夠一個接受介面,輸出多個介面。
濾鏡和濾鏡的串連還可以分為推模式(push filter)和拉模式(pull filter)兩種。
推模式的特點就是資料由上遊的濾鏡決定,就是上遊的濾鏡一有資料了可以推給下遊的去處理。
拉模式的呢就是下遊什麼時候處理資料完了,才通過介面像上遊濾鏡要資料。
兩種的不同就是資料的傳遞方法不同,所以中間的快取資料策略也不一樣,具體可以去查看msdn。
總的來說拉模式的適合用來播放檔案,而推模式適合用來網路傳輸即時媒體。因為對即時性要求比較高的,我在自己的源濾鏡中收到資料,就馬上要求播放這 個資料了呀,推模式呢就可以馬上在這個源濾鏡中把資料傳給下遊濾鏡,要求播放。如果是拉模式就不一樣咯,下遊濾鏡決定了播放的話語權,我什麼時候播放完 了,我再什麼時候找你源濾鏡要資料,這樣有可能慢吞吞的在放,而源濾鏡那有可能已經累積了很多資料在那了來不及播放,這樣即時性就不好。
我在寫語音交談的時候,也是試了這兩種濾鏡的,採用push filter到時候,即時性確實要好些的。
關於學習,可以參考directshow sdk內建的兩個例子
memfile(pull filter到)
ball (push filter的)
仔細看一些,稍稍修改就可以工作了。
=================
pull filter的
下遊需要資料時會調用這個函數向上遊filter要資料
HRESULT Read(PBYTE pbBuffer,
DWORD dwBytesToRead,
BOOL bAlign, LPDWORD pdwBytesRead)
在這個函數可以設定我們推薦的緩衝的大小,可以在這裡改一下把資料大小改一下,緩衝越小延時就,不過這裡是有要求的,不能亂改,亂改了,決定權在下 遊filter,他就會拒接串連,網上那些人說是要是32768 乘以6的大小,我測試的時候,播放mp3檔案時這裡最小可以調到4096 *48 。
// we need to return an addrefed allocator, even if it is the preferred
// one, since he doesn't know whether it is the preferred one or not.
STDMETHODIMP
CAsyncOutputPin::RequestAllocator(
IMemAllocator* pPreferred,
ALLOCATOR_PROPERTIES* pProps,
IMemAllocator ** ppActual)
{
CheckPointer(pPreferred,E_POINTER);
CheckPointer(pProps,E_POINTER);
CheckPointer(ppActual,E_POINTER);
ASSERT(m_pIo);
// we care about alignment but nothing else
if(!pProps->cbAlign || !m_pIo->IsAligned(pProps->cbAlign))
{
m_pIo->Alignment(&pProps->cbAlign);
}
ALLOCATOR_PROPERTIES Actual;
HRESULT hr;
/////// 設定IMediaSample的長度更小一點,可以使語音的延時小一點。
///預設值為 32768 ,可能延時太大了
pProps->cbBuffer = 4096 ; /////4096 *48 可以成功
pProps->cBuffers = 48;
if(pPreferred)
{
hr = pPreferred->SetProperties(pProps, &Actual);
if(SUCCEEDED(hr) && m_pIo->IsAligned(Actual.cbAlign))
{
pPreferred->AddRef();
*ppActual = pPreferred;
return S_OK;
}
}
// create our own allocator
IMemAllocator* pAlloc;
hr = InitAllocator(&pAlloc);
if(FAILED(hr))
{
return hr;
}
//...and see if we can make it suitable
hr = pAlloc->SetProperties(pProps, &Actual);
if(SUCCEEDED(hr) && m_pIo->IsAligned(Actual.cbAlign))
{
// we need to release our refcount on pAlloc, and addref
// it to pass a refcount to the caller - this is a net nothing.
*ppActual = pAlloc;
return S_OK;
}
// failed to find a suitable allocator
pAlloc->Release();
// if we failed because of the IsAligned test, the error code will
// not be failure
if(SUCCEEDED(hr))
{
hr = VFW_E_BADALIGN;
}
return hr;
}
--------------
push模式時,
他會在後台啟動一個進程不停調用FillBuffer函數,我們只要在源濾鏡中有資料來時,把資料傳下去就可以了。在這個函數裡面我們可以用鎖來等 待資料的到來,一來了馬上push就行了。
===================
寫filter的時候,還需要注意設定filter的可以接受的和輸出的 媒體類型,CMediaType 這個類的設定很關鍵 。設定好了,各個filter才能連的起來,雖然directshow是會進行一些智能串連嘗試的。