Media Player Classic - HC 源代码分析 11:Pin之间的具体连接过程1(PIN之间媒体类型的协商)

 上一篇文章讲解了Filter之间的连接最终转为Pin之间的连接,调用了IFilterGraph的ConnectDirect实现两个PIN之间的连接。今天我们来具体了解一下两个PIN是如何连接的。

由于没有ConnectDirect的源代码,所以只能通过MSDN的介绍了解一下两个PIN之间的连接过程。下面是我参考的MSDN的内容链接:

PIN之间连接

PIN之间媒体类型的协商

 PIN之间分配器的协商过程

根据MSDN的介绍我总结一下具体的连接过程大概分为2部分,第一部分为:PIN之间媒体类型的协商 第二部分为:PIN之间分配器的协商 今天我们只讲解第一部分内容,第二部分内容下一篇再讲解。

PIN之间分配器的协商过程大致总结如下:

  1. 调用输出PIN的CBasePin::Connect函数,并且将输入PIN作为参数传入。
  2. CBasePin::Connect函数将调用转给函数CBasePin::AgreeMediaType。
  3. 如果有完整的媒体类型传入,则直接调用CBasePin::AttemptConnection函数尝试连接。如果没有完整的媒体类型传入,则从输入PIN和输出PIN分别取到对应的媒体类型枚举器,然后调用CBasePin::TryMediaTypes函数,在CBasePin::TryMediaTypes函数里面会枚举具体的媒体类型,然后调用CBasePin::AttemptConnection函数尝试连接。
  4. 在输出PIN的AttemptConnection中调用纯虚函数CheckMediaType(具体实现在真正使用的PIN类中)检查媒体类型是否符合要求。如果符合要求,再调用输入PIN的ReceiveConnection函数,并传入输出PIN对象的指针和媒体类型,输入PIN会在此函数中调用纯虚函数CheckMediaType检查媒体类型。
  5. 最后输入和输出PIN都会调用CompleteConnect完成PIN的连接。各个PIN会在这个函数里面实现分配器的协商。

下面我们来看看具体的源码:

首先是CBasePin::Connect函数,源码如下:

/* 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. 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, failing that we retrieve
   our enumerator and see if it will accept any of our preferred types */

STDMETHODIMP
CBasePin::Connect(
    IPin * pReceivePin,
    __in_opt 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 */
    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() ) );

#ifdef DXMPERF
        PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );
#endif // DXMPERF

        return hr;
    }

    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));

#ifdef DXMPERF
    PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );
#endif // DXMPERF

    return NOERROR;
}

由以上源码可以看出,Connect函数主要就是将PIN的连接转给函数AgreeMediaType。

接下来我们贴上AgreeMediaType的源码看看,代码如下:

/* 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.
 */

HRESULT CBasePin::AgreeMediaType(
    IPin *pReceivePin,
    const CMediaType *pmt)
{
    ASSERT(pReceivePin);
    IEnumMediaTypes *pEnumMediaTypes = NULL;

    // if the media type is fully specified then use that
    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 */

    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;
}

从代码可知和步骤3 描述完全一样(如果有完整的媒体类型传入,则直接调用CBasePin::AttemptConnection函数尝试连接。如果没有完整的媒体类型传入,则从输入PIN和输出PIN分别取到对应的媒体类型枚举器,然后调用CBasePin::TryMediaTypes函数,在CBasePin::TryMediaTypes函数里面会枚举具体的媒体类型,然后调用CBasePin::AttemptConnection函数尝试连接。)

我们接下来看看TryMediaTypes函数源码,代码如下:

/* 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,
    __in_opt 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
           enumerator 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 */

        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 (pMediaType &&
            ((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;
        }

        if(pMediaType) {
            DeleteMediaType(pMediaType);
            pMediaType = NULL;
        }

        if (S_OK == hr) {
            return hr;
        }
    }
}

在这个函数里面我们可以看到,媒体类型枚举器会枚举出所有的媒体类型对象,直到有一个媒体类型能调用AttemptConnection连接成功为止。

接下来我们看看AttemptConnection的实现,代码如下:

// 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().
    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);
        if (SUCCEEDED(hr)) {
            /* See if the other pin will accept this type */

            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;
}

这个函数里面就调用了3个重要的函数:

1.CheckMediaType:输出PIN检查媒体类型是否能通过。

2.ReceiveConnection:输入PIN检查媒体类型是否能通过。

3.CompleteConnect:完成输入和输出PIN之间分配器的协商。

最后我们再看看ReceiveConnection函数的实现,代码如下:

/* Called normally by an output pin on an input pin to try and establish a
   connection.
*/

STDMETHODIMP
CBasePin::ReceiveConnection(
    IPin * pConnector,   // this is the pin who we will connect to
    const AM_MEDIA_TYPE *pmt  // this is the media type we will exchange
)
{
    CheckPointer(pConnector,E_POINTER);
    CheckPointer(pmt,E_POINTER);
    ValidateReadPtr(pConnector,sizeof(IPin));
    ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
    CAutoLock cObjectLock(m_pLock);

    /* Are we already connected */
    if (m_Connected) {
        return VFW_E_ALREADY_CONNECTED;
    }

    /* See if the filter is active */
    if (!IsStopped() && !m_bCanReconnectWhenActive) {
        return VFW_E_NOT_STOPPED;
    }

    HRESULT hr = CheckConnect(pConnector);
    if (FAILED(hr)) {
        // 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() ) );

#ifdef DXMPERF
        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
#endif // DXMPERF

        return hr;
    }

    /* Ask derived class if this media type is ok */

    CMediaType * pcmt = (CMediaType*) pmt;
    hr = CheckMediaType(pcmt);
    if (hr != NOERROR) {
        // no -we don't support this media 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 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;
        }

#ifdef DXMPERF
        PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
#endif // DXMPERF

        return hr;
    }

    /* Complete the connection */

    m_Connected = pConnector;
    m_Connected->AddRef();
    hr = SetMediaType(pcmt);
    if (SUCCEEDED(hr)) {
        hr = CompleteConnect(pConnector);
        if (SUCCEEDED(hr)) {

#ifdef DXMPERF
            PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );
#endif // DXMPERF

            return NOERROR;
        }
    }

    DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));
    m_Connected->Release();
    m_Connected = NULL;

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

#ifdef DXMPERF
    PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
#endif // DXMPERF

    return hr;
}

可以看到ReceiveConnection里面也调用了CheckMediaType函数和CompleteConnect函数。

CompleteConnect的实现下一篇讲解。

总结:只要媒体类型协商和分配器协商成功,整个PIN的连接就完成了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值