// Asked to connect to a pin.
// A pin is always attached to an owning filter object
// so we always delegate our locking to that object.
//一个pin总是连接到一个拥有filter的对象,因此Connect函数总是把我们的
//锁委托给这个对象。
// We first of all retrieve a media type enumerator for the input pin
// and see if we accept any of the formats that it would ideally like,
// Connect函数首先检索输入pin的媒体类型枚举器来查看是否有任何这个
//函数能满意接受的格式,
// failing that we retrieve our enumerator and see if it will accept
// any of our preferred types.
//失败的话Connect函数检索我们的枚举器,查看我们是否有任何它能接受的
//首选类型。
#define CheckPointer(p,ret) {if((p)==NULL) return (ret);}
#define ValidateReadPtr(p,cb) \
{if(IsBadReadPtr((PVOID)p,cb) == TRUE) \
DbgBreak("Invalid read pointer");}
STDMETHODIMP
CBasePin::Connect(
IPin * pReceivePin,
const AM_MEDIA_TYPE *pmt // optional media type
)
{
CheckPointer(pReceivePin,E_POINTER);
ValidateReadPtr(pReceivePin,sizeof(IPin));
CAutoLock cObjectLock(m_pLock);
DisplayPinInfo(pReceivePin);
// See if we are already connected
if (m_Connected)
{
DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));
return VFW_E_ALREADY_CONNECTED;
}
// See if the filter is active,一半filter只能在停止状态连接
if (!IsStopped() && !m_bCanReconnectWhenActive)
{
return VFW_E_NOT_STOPPED;
}
// Find a mutually agreeable media type -
// Pass in the template media type. If this is partially specified,
// each of the enumerated media types will need to be checked against
// it. If it is non-null and fully specified, we will just try to connect
// with this.
//开始媒体类型检查,找出一种连接双方都支持的媒体类型。
const CMediaType * ptype = (CMediaType*)pmt;
HRESULT hr = AgreeMediaType(pReceivePin, ptype);
if (FAILED(hr))
{
DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));
// Since the procedure is already returning an error code, there
// is nothing else this function can do to report the error.
EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
return hr;
}
DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));
return NOERROR;
}
// This is called to make the connection,
// including the taask of finding a media type for the pin connection.
// pmt is the proposed推荐 media type from the Connect call:
// if this is fully specified指定, we will try that.
// Otherwise否则 we enumerate枚举 and
// try all the input pin's types first and if that fails we then enumerate
// and try all our preferred media types.
// For each media type we check it against pmt (if non-null and
// partially specified) as well as checking that both pins will accept it.
//首先检查指定媒体类型pmt的有效性,
//若是fully specified的则就用该类型调用内部函数AttemptConnection进行连接。
//若是不,输入pin和输出pin开始真正的类型协商过程,
//for循环2次,输出pin上成员变量m_bTryMyTypesFirst初始值为false即0,因此先pReceivePin->EnumMediaTypes(&pEnumMediaTypes);
//在输入pin上进行类型枚举试连接,如果不成功
//再到输出pin进行类型枚举试连接。
HRESULT CBasePin::AgreeMediaType(
IPin *pReceivePin,
const CMediaType *pmt)
{
ASSERT(pReceivePin);
IEnumMediaTypes *pEnumMediaTypes = NULL;
// if the media type is fully specified then use that.如果是pmt是完全指定的媒体类型,我们就用它做连接。
if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {
// if this media type fails, then we must fail the connection
// since if pmt is nonnull we are only allowed to connect
// using a type that matches it.
return AttemptConnection(pReceivePin, pmt);
}
// Try the other pin's enumerator.尝试另外的pin枚举器
HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
for (int i = 0; i < 2; i++)
{
HRESULT hr;
if (i == (int)m_bTryMyTypesFirst)
{
hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);
}
else
{
hr = EnumMediaTypes(&pEnumMediaTypes);
}
if (SUCCEEDED(hr))
{
ASSERT(pEnumMediaTypes);
hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);
pEnumMediaTypes->Release();
if (SUCCEEDED(hr))
{
return NOERROR;
}
else
{
// try to remember specific error codes if there are any
if ((hr != E_FAIL) &&
(hr != E_INVALIDARG) &&
(hr != VFW_E_TYPE_NOT_ACCEPTED))
{
hrFailure = hr;
}
}
}
}
return hrFailure;
}
// Given an enumerator we cycle循环 through all the media types it proposes and
// firstly suggest them to our derived pin class and if that succeeds try
// them with the pin in a ReceiveConnection call.
// This means that if our pin proposes a media type we still check in here that we can support it.
// This is deliberate so that in simple cases the enumerator can hold all of the
// media types even if some of them are not really currently available
HRESULT CBasePin::TryMediaTypes(
IPin *pReceivePin,
const CMediaType *pmt,
IEnumMediaTypes *pEnum)
{
/* Reset the current enumerator position */
HRESULT hr = pEnum->Reset();
if (FAILED(hr)) {
return hr;
}
CMediaType *pMediaType = NULL;
ULONG ulMediaCount = 0;
// attempt to remember a specific error code if there is one
HRESULT hrFailure = S_OK;
for (;;)
{
// Retrieve恢复 the next media type NOTE each time round the loop the
// numerator interface will allocate another AM_MEDIA_TYPE structure
// If we are successful then we copy it into our output object,
// if not then we must delete the memory allocated before returning
//枚举pin上提供的所有类型,然后全都进行连接
hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);
if (hr != S_OK)
{
if (S_OK == hrFailure)
{
hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
}
return hrFailure;
}
ASSERT(ulMediaCount == 1);
ASSERT(pMediaType);
// check that this matches the partial type (if any)
if ((pmt == NULL) ||
pMediaType->MatchesPartial(pmt))
{
hr = AttemptConnection(pReceivePin, pMediaType);
// attempt to remember a specific error code
if (FAILED(hr) &&
SUCCEEDED(hrFailure) &&
(hr != E_FAIL) &&
(hr != E_INVALIDARG) &&
(hr != VFW_E_TYPE_NOT_ACCEPTED))
{
hrFailure = hr;
}
}
else
{
hr = VFW_E_NO_ACCEPTABLE_TYPES;
}
DeleteMediaType(pMediaType);
if(S_OK == hr)
return hr;
}
}
// given a specific media type, attempt a connection (includes
// checking that the type is acceptable to this pin)
HRESULT
CBasePin::AttemptConnection(
IPin* pReceivePin, // connect to this pin
const CMediaType* pmt // using this type
)
{
// The caller should hold the filter lock becasue this function
// uses m_Connected. The caller should also hold the filter lock
// because this function calls SetMediaType(), IsStopped() and
// CompleteConnect().
//hold the filter lock.获取filter对象上是操作权
ASSERT(CritCheckIn(m_pLock));
// Check that the connection is valid -- need to do this for every
// connect attempt since BreakConnect will undo it.
HRESULT hr = CheckConnect(pReceivePin);
if (FAILED(hr)) {
DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));
// Since the procedure is already returning an error code, there
// is nothing else this function can do to report the error.
EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
return hr;
}
DisplayTypeInfo(pReceivePin, pmt);
//Check we will accept this media type
hr = CheckMediaType(pmt);
if (hr == NOERROR)
{
// Make ourselves look connected otherwise ReceiveConnection
// may not be able to complete the connection
m_Connected = pReceivePin;
m_Connected->AddRef();
hr = SetMediaType(pmt);
//把输入pin对象指针和其媒体类型保存在输出pin对象中。
if (SUCCEEDED(hr))
{
// See if the other pin will accept this type.
//输入pin询问连接对方是否也能接受当前连接类型
hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);
if (SUCCEEDED(hr))
{
// Complete the connection.设置数据传输的内存分配以及管理工作
hr = CompleteConnect(pReceivePin);
if (SUCCEEDED(hr))
{
return hr;
}
else
{
DbgLog((LOG_TRACE,
CONNECT_TRACE_LEVEL,
TEXT("Failed to complete connection")));
pReceivePin->Disconnect();
}
}
}
}
else
{
// we cannot use this media type
// return a specific media type error if there is one
// or map a general failure code to something more helpful
// (in particular S_FALSE gets changed to an error code)
if (SUCCEEDED(hr) ||
(hr == E_FAIL) ||
(hr == E_INVALIDARG)) {
hr = VFW_E_TYPE_NOT_ACCEPTED;
}
}
// BreakConnect and release any connection here in case CheckMediaType
// failed, or if we set anything up during a call back during
// ReceiveConnection.
// Since the procedure is already returning an error code, there
// is nothing else this function can do to report the error.
EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
/* If failed then undo our state */
if (m_Connected) {
m_Connected->Release();
m_Connected = NULL;
}
return hr;
}
HRESULT
CBaseOutputPin::CompleteConnect(IPin *pReceivePin)
{
UNREFERENCED_PARAMETER(pReceivePin);
return DecideAllocator(m_pInputPin, &m_pAllocator);
}
//在DirectShow中,数据传输单元叫Sample(Sample也是一个COM组件,
//管理一块数据内存);而Sample是由分配器(Allocator,也是一个COM)来管理的。
//连接双方必须使用同一个分配器,但是这个分配器到底由哪个pin创建也要协商。
HRESULT
CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, IMemAllocator **ppAlloc)
{
HRESULT hr = NOERROR;
*ppAlloc = NULL;
ALLOCATOR_PROPERTIES prop;
ZeroMemory(&prop, sizeof(prop));
//询问输入pin对分配器的要求
pPin->GetAllocatorRequirements(&prop);
// if he doesn't care about alignment, then set it to 1
if (prop.cbAlign == 0) {
prop.cbAlign = 1;
}
//询问输入pin是否提供一个分配器
hr = pPin->GetAllocator(ppAlloc);
if (SUCCEEDED(hr)) {
//决定Sample使用的内存大小,一起分配器管理Sample的数量
hr = DecideBufferSize(*ppAlloc, &prop);
if (SUCCEEDED(hr)) {
//通知输入pin最终使用的分配器对象
hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
if (SUCCEEDED(hr)) {
return NOERROR;
}
}
}
// If the GetAllocator failed we may not have an interface
if (*ppAlloc) {
(*ppAlloc)->Release();
*ppAlloc = NULL;
}
//Try the output pin's allocator by the same method
//创建一个输出pin上的分配器
hr = InitAllocator(ppAlloc);
if (SUCCEEDED(hr)) {
hr = DecideBufferSize(*ppAlloc, &prop);
if (SUCCEEDED(hr)) {
hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
if (SUCCEEDED(hr)) {
return NOERROR;
}
}
}
//Likewise同样 we may not have an interface to release
if (*ppAlloc) {
(*ppAlloc)->Release();
*ppAlloc = NULL;
}
return hr;
}
//当pin上数据传输内存分配协商成功后,并没有马上分配给Sample内存。一般是在Filter Graph运行后输入pin调用Active时进行的。
HRESULT CBaseOutputPin::Active(void)
{
if(m_pAllocator == TRUE)
{
return VFW_E_NO_ALLOCATOR;
}
return m_pAllocator->Commit();
}