Directshow Filter 链接过程

最近要用到一个TransformFilter 做一些转换,就看了些Directshow的东西,一直困惑于Filter之间是怎么进行连接协商的,那些必须要重写的CTransformFilter方法有什么用,又是什么时候被调用的也感到迷惑,现在就记录一下自己的探索过程。


                      


Filter连接的本质就是Filter的Pin与Pin之间的连接,首先上游Filter的OutputPin的CBasePin::Connect( IPin * pReceivePin,__in_opt const AM_MEDIA_TYPE *pmt):

STDMETHODIMP CBasePin::Connect( IPin * pReceivePin,__in_opt const AM_MEDIA_TYPE *pmt) {
	// pReceivePin 表示将要和自己连接的下游Filter的InputPin
        // pmt 商讨的类型
	// ...省略一些其他的判断
	/* 是否已经处于连接状态 */

	if (m_Connected) {
		DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));
		return VFW_E_ALREADY_CONNECTED;
	}
        // ...

	// 转到AgreeMediaType()..

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

	return NOERROR;
}


可以看到,Connect()方法对自身的状态做了一些判断后,就将连接的协商交给AgreeMediaType()来处理了,来看看AgreeMediaType做了什么:

HRESULT CBasePin::AgreeMediaType( IPin *pReceivePin, const CMediaType *pmt)
{
    ASSERT(pReceivePin);
    // 类型的枚举器 
    IEnumMediaTypes *pEnumMediaTypes = NULL;

    // 如果pmt是一个完全类型 ,直接尝试连接 
    if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {

        // pmt 非NULL ,且 pmt是一个完全指定的类型,
	// 那么就用pmt进行连接,连接成功则成功,失败则就是败了,
        
        //  尝试连接
        return AttemptConnection(pReceivePin, pmt);
    }

    /* 尝试其他类型 */

    HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;

    for (int i = 0; i < 2; i++) {
        HRESULT hr;
        // m_bTryMyTypesFirst 默认为 0  
        if (i == (int)m_bTryMyTypesFirst) {
        	// 获取下游Filter 的InputPin的类型枚举 
            hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);
        } else {
        	// 获取本Filter 的OutputPin的类型枚举 
            hr = EnumMediaTypes(&pEnumMediaTypes);
        }
        if (SUCCEEDED(hr)) {
            ASSERT(pEnumMediaTypes);
            //尝试 pEnumMediaTypes   中的类型, 看看双方同不同意连接 
            hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);
            pEnumMediaTypes->Release();
            if (SUCCEEDED(hr)) {
            	// 连接成功 
                return NOERROR;
            } else {
                // 记录错误 ...略
               
            }
        }
    }

    return hrFailure;
}


可以看出,AgreeMediaType() 先判断pmt是否是一万完全指定的类型,若是,就使用AttemptConnection()进行连接,连接失败了,那么两个Filter的连接也就失败了。若pmt并非完全类型,就分别获取下游Filter InputPin上的类型枚举器 和 本Filter OutputPin上的类型枚举器 使用TryMediaTypes()进行尝试连接。


看看 TryMediaTypes():

HRESULT CBasePin::TryMediaTypes(IPin *pReceivePin, __in_opt const CMediaType *pmt,
    IEnumMediaTypes *pEnum)
{
    CMediaType *pMediaType = NULL;
    ULONG ulMediaCount = 0;

    // attempt to remember a specific error code if there is one
    HRESULT hrFailure = S_OK;

    for (;;) {

        /* 一个一个的枚举出pEnum里的类型  */

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

        // 尝试使用pMediaType 进行连接 

        if (pMediaType &&
            ((pmt == NULL) ||
            pMediaType->MatchesPartial(pmt))) {
			
	   // 尝试连接 
            hr = AttemptConnection(pReceivePin, pMediaType);

            // 记录错误.. 略 
            
        } else {
            hr = VFW_E_NO_ACCEPTABLE_TYPES;
        }

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

        if (S_OK == hr) {
        	//连接成功 
            return hr;
        }
    }
}

TryMediaTypes()不断从枚举器里取出类型来尝试连接,直到AttemptConnection() 返回S_OK(连接成功)或者类型被枚举完毕返回,若果此时使用的是下游Filter  InputPin的类型枚举器, 接下来会尝试使用本Filter  OutpinPin的类型枚举器来尝试连接(可以参考上文AgreeMediaType() 里的 for ()  循环 )。

HRESULT  CBasePin::AttemptConnection(
    IPin* pReceivePin,      // 连接到这个Pin 
    const CMediaType* pmt  
)
{
    // 一些例常的状态检查 ... 略
	
	//本Filter的OutputPin 检查此类型是否满足自己的要求 
    hr = CheckMediaType(pmt);
    if (hr == NOERROR) {


        /*  
          如果满足要求 :
		  记录对方的Pin 
        */
        m_Connected = pReceivePin;
        m_Connected->AddRef();
        
        // 记录自己同意的 Type , SetMediaType() 里也可以有一些类型检查
		// 如 CheckTransform(pmt1, pmt2) ...
        hr = SetMediaType(pmt);
        if (SUCCEEDED(hr)) {
           //  本 Filter 已经完全认同这个 pmt 了 , 那么对方的Filter ( 或者说下游Filter 的 InputPin) 
	   //  是否也同意这个 pmt ?  
	   
			
	   //  对方同意吗 ? 
	   //  将此 OutputPin  传给对方问个话。 如果同意连接,对方要保存这个OutputPin 。
	   //  ReceiveConnection() 里会调用:
	   //  CheckMediaType(pmt) 、SetMediaType(pcmt) 对 pmt进行判断, 
	   //  若同意 ,则会调用 :  CompleteConnect() 。 
            hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);
            if (SUCCEEDED(hr)) {
                /* 
				   对方InputPin 也同意了, 那就 CompleteConnect()吧, 
				   完成连接的协商。 
				*/
                hr = CompleteConnect(pReceivePin);
                if (SUCCEEDED(hr)) {
                    return hr;
                } else {
                    DbgLog((LOG_TRACE,
                            CONNECT_TRACE_LEVEL,
                            TEXT("Failed to complete connection")));
                    pReceivePin->Disconnect();
                }
            }
        }
    } else {
        // 记录错误... 略
    }
	// 其他...略 
    return hr;
}


这样,上游Filter 和 下游Filter 就算连接完毕了。但是,Pin上的类型是怎被枚举的 ?继承  CTransformFilter 写自己的TransformFilter 时为什么必须要重写像GetMediaType()  、DecideBufferSize()等函数? 下篇文章再分析。



你可能感兴趣的:(directshow)