明輝手游網(wǎng)中心:是一個(gè)免費(fèi)提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺(tái)!

使用Directshow開發(fā)源Filter

[摘要]摘要:我們一般不推薦自己開發(fā)音頻或者視頻捕捉過濾器,因?yàn)閐iectshow對(duì)于音視頻的捕捉設(shè)備以經(jīng)提供了支持。所以,這篇文檔,對(duì)于某些用戶需要從特定設(shè)備捕捉一些數(shù)據(jù)提供一些幫助。這篇文檔主要包括以下內(nèi)容。1捕捉filter 對(duì)pin的要求2如何完成一個(gè)預(yù)覽pin3如何產(chǎn)生源數(shù)據(jù) 1 對(duì)pin的要求...
摘要:
我們一般不推薦自己開發(fā)音頻或者視頻捕捉過濾器,因?yàn)閐iectshow對(duì)于音視頻的捕捉設(shè)備以經(jīng)提供了支持。所以,這篇文檔,對(duì)于某些用戶需要從特定設(shè)備捕捉一些數(shù)據(jù)提供一些幫助。這篇文檔主要包括以下內(nèi)容。
1捕捉filter 對(duì)pin的要求
2如何完成一個(gè)預(yù)覽pin
3如何產(chǎn)生源數(shù)據(jù)
1 對(duì)pin的要求Pin Requirements for Capture Filters
Pin的名字
你可以給你的filter起任何名字,如果你的pin的名字以~符號(hào)開頭,那么當(dāng)應(yīng)用程序調(diào)用IGraphBuilder::RenderFile方法時(shí),filter圖表管理器不會(huì)自動(dòng)render這個(gè)pin的。例如,如果一個(gè)filter具有一個(gè)捕捉pin和預(yù)覽pin,相應(yīng)的你給他們命名為”~Capture”和“Preview”。如果一個(gè)應(yīng)用程序在graph中render這個(gè)filter ,那么預(yù)覽pin就會(huì)自動(dòng)和它缺省的render相連接,但是,capture pin上卻不連接任何東西,這是一個(gè)合理的缺省行為。這個(gè)也可以應(yīng)用到那些傳輸不準(zhǔn)備被rendered數(shù)據(jù)的pin,也可以應(yīng)用到需要屬性設(shè)置的pin上。
注:名字中含有~符號(hào)的pin是可以手動(dòng)連接的。
Pin的種類
一個(gè)捕捉filter通常用一個(gè)捕捉pin,也許還有一個(gè)預(yù)覽pin。一些捕捉filter除了這兩種pin之外還有其他的pin,用來傳遞其他的數(shù)據(jù),例如控制信息。每一個(gè)輸出pin都必須暴露IKsPropertySet接口,應(yīng)用程序通過這些接口來判斷pin的種類,pin一般都會(huì)返回PIN_CATEGORY_CAPTURE or PIN_CATEGORY_PREVIEW。下面的例子演示了一個(gè)捕捉pin如何通過IKsPropertySet來返回pin的種類
// Set: Cannot set any properties.
HRESULT CMyCapturePin::Set(REFGUID guidPropSet, DWORD dwID,
void *pInstanceData, DWORD cbInstanceData, void *pPropData,
DWORD cbPropData)
{
return E_NOTIMPL;
}

// Get: Return the pin category (our only property).
HRESULT CMyCapturePin::Get(
REFGUID guidPropSet, // Which property set.
DWORD dwPropID, // Which property in that set.
void *pInstanceData, // Instance data (ignore).
DWORD cbInstanceData, // Size of the instance data (ignore).
void *pPropData, // Buffer to receive the property data.
DWORD cbPropData, // Size of the buffer.
DWORD *pcbReturned // Return the size of the property.
)
{
if (guidPropSet != AMPROPSETID_Pin)
return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
return E_PROP_ID_UNSUPPORTED;
if (pPropData == NULL && pcbReturned == NULL)
return E_POINTER;
if (pcbReturned)
*pcbReturned = sizeof(GUID);
if (pPropData == NULL) // Caller just wants to know the size.
return S_OK;
if (cbPropData < sizeof(GUID)) // The buffer is too small.
return E_UNEXPECTED;
*(GUID *)pPropData = PIN_CATEGORY_CAPTURE;
return S_OK;
}

// QuerySupported: Query whether the pin supports the specified property.
HRESULT CMyCapturePin::QuerySupported(REFGUID guidPropSet, DWORD dwPropID,
DWORD *pTypeSupport)
{
if (guidPropSet != AMPROPSETID_Pin)
return E_PROP_SET_UNSUPPORTED;
if (dwPropID != AMPROPERTY_PIN_CATEGORY)
return E_PROP_ID_UNSUPPORTED;
if (pTypeSupport)
// We support getting this property, but not setting it.
*pTypeSupport = KSPROPERTY_SUPPORT_GET;
return S_OK;
}

2如何完成一個(gè)預(yù)覽pin Implementing a Preview Pin (Optional)
如果你的filter有一個(gè)預(yù)覽pin,預(yù)覽pin發(fā)送的數(shù)據(jù)是捕捉pin傳遞的數(shù)據(jù)的拷貝。預(yù)覽pin發(fā)送的數(shù)據(jù)不會(huì)降低捕捉pin的楨率,捕捉pin比預(yù)覽pin有優(yōu)先權(quán)。
捕捉pin和預(yù)覽pin必須發(fā)送一個(gè)相同格式的數(shù)據(jù)。這樣,他們連接都是通過同一種媒體數(shù)據(jù)類型,如果捕捉pin先連接,預(yù)覽pin應(yīng)該提供相同的媒體類型,對(duì)于其他類型的數(shù)據(jù)媒體,則拒絕。如果預(yù)覽pin先連接,然后,如果捕捉pin以另一種媒體類型和其他pin連接,那么預(yù)覽pin就應(yīng)該用新的媒體類型重新連接,如果和filter的預(yù)覽pin連接的下游filter拒絕新的數(shù)據(jù)類型,捕捉pin應(yīng)該拒絕新的媒體類型?梢酝ㄟ^IPin::QueryAccept方法察看filter的預(yù)覽pin連接的下游filter連接的數(shù)據(jù)媒體,然后通過IFilterGraph::Reconnect方法重新連接pin。
這條規(guī)則也適用于圖表管理器重新連接捕捉pin。
下面的代碼大體上描述了上面的過程
// Override CBasePin::CheckMediaType.
CCapturePin::CheckMediaType(CMediaType *pmt)
{
if (m_pMyPreviewPin->IsConnected())
{
// The preview pin is already connected, so query the pin it is
// connected to. If the other pin rejects it, so do we.
hr = m_pMyPreviewPin->GetConnected()->QueryAccept(pmt);
if (hr != S_OK)
{
// The preview pin cannot reconnect with this media type.
return E_INVALIDARG;
}
// The preview pin will reconnect when SetMediaType is called.
}
// Decide whether the capture pin accepts the format.
BOOL fAcceptThisType = ... // (Not shown.)
return (fAcceptThisType? S_OK : E_FAIL);
}

// Override CBasePin::SetMediaType.
CCapturePin::SetMediaType(CMediaType *pmt);
{
if (m_pMyPreviewPin->IsConnected())
{
// The preview pin is already connected, so it must reconnect.
if (m_pMyPreviewPin->GetConnected()->QueryAccept(pmt) == S_OK)
{
// The downstream pin will accept the new type, so it''s safe
// to reconnect.
m_pFilter->m_pGraph->Reconnect(m_pMyPreviewPin);
}
else
{
return VFW_E_INVALIDMEDIATYPE;
}
}
// Now do anything that the capture pin needs to set the type.
hr = MyInternalSetMediaType(pmt);

// And finally, call the base-class method.
return CBasePin::SetMediaType(pmt);
}

CPreviewPin::CheckMediaType(CMediaType *pmt)
{
if (m_pMyCapturePin->IsConnected())
{
// The preview pin must connect with the same type.
CMediaType cmt = m_pMyCapturePin->m_mt;
return (*pmt == cmt ? S_OK : VFW_E_INVALIDMEDIATYPE);
}
// Decide whether the preview pin accepts the format. You can use your
// knowledge of which types the capture pin will accept. Regardless,
// when the capture pin connects, the preview pin will reconnect.
return (fAcceptThisType? S_OK : E_FAIL);
}

3在源filter中產(chǎn)生數(shù)據(jù) Producing Data in a Capture Filter
狀態(tài)改變
一個(gè)捕捉filter在運(yùn)行時(shí)會(huì)產(chǎn)生數(shù)據(jù)流。當(dāng)filter paused的時(shí)候,不要發(fā)送數(shù)據(jù),并且此時(shí),圖表管理器調(diào)用CBaseFilter::GetState方法會(huì)返回VFW_S_CANT_CUE,這個(gè)返回值告訴圖表管理器,filter 正處于paused狀態(tài),停止發(fā)送數(shù)據(jù)。下面的代碼顯示如何派生GetState方法
CMyVidcapFilter::GetState(DWORD dw, FILTER_STATE *pState)
{
CheckPointer(pState, E_POINTER);
*pState = m_State;
if (m_State == State_Paused)
return VFW_S_CANT_CUE;
else
return S_OK;
}
控制不同的數(shù)據(jù)流
一個(gè)捕捉filter應(yīng)該支持IAMStreamControl接口,因此應(yīng)用程序應(yīng)該分別的打開和關(guān)閉每一個(gè)pin,例如,一個(gè)應(yīng)用程序可以只預(yù)覽而沒有捕捉,然后可以轉(zhuǎn)換到捕捉模式不用重新構(gòu)建graph圖表。你可以通過CBaseStreamControl類來實(shí)現(xiàn)這個(gè)接口
時(shí)間戳
當(dāng)一個(gè)filter捕捉了一個(gè)sample,將在每一個(gè)sample上標(biāo)上一個(gè)當(dāng)前時(shí)間的時(shí)間戳。結(jié)束時(shí)間是開始時(shí)間加上持續(xù)時(shí)間。例如,如果一個(gè)filter每秒捕捉十個(gè)sample,and the stream time is 200,000,000 units when the filter captures the sample, the time stamps should be 200000000 and 201000000. (There are 10,000,000 units per second.) 可以通過IReferenceClock::GetTime方法獲得當(dāng)前的參考時(shí)間,通過IMediaSample::SetTime給sample設(shè)置時(shí)間戳。
相連的sample上的時(shí)間戳必須是遞增的,即使filter pauses也是這樣,如果一個(gè)filter運(yùn)行,停止,然后又運(yùn)行,重新開始后產(chǎn)生的sample上的時(shí)間戳必須比停止前的sample上的時(shí)間戳要大一些。
預(yù)覽pin上的sample沒有時(shí)間戳,但是,預(yù)覽pin一般比捕捉pin到達(dá)render pin稍晚,因此,render filter將預(yù)覽pin上的sample視作遲到的sample,為了趕上其他的時(shí)間進(jìn)度,也許會(huì)丟失一些數(shù)據(jù)。