// 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(); }