pin的连接过程

 

 

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

 

posted @ 2013-12-23 19:08  细雨细语  阅读(1478)  评论(0编辑  收藏  举报