www.pudn.com > VidMix.rar > VidMixFilter.cpp


 
 
#include    
#include           // performance measurement (MSR_) 
#include  
 
 
#if (1100 > _MSC_VER) 
#include  
#else 
#include  
#endif 
#include "vidmixfilter.h" 
#ifndef RELEASE(lpUnk) 
#define RELEASE(lpUnk) if(lpUnk) { lpUnk->Release(); lpUnk = 0;} 
#endif 
 
 
 
 
/****************************************************************************************************************\  
 *											dll	setup															* 
\****************************************************************************************************************/ 
// Setup information 
class CVidMixFilter; 
const AMOVIESETUP_MEDIATYPE sudPinTypes = 
{ 
    &MEDIATYPE_Video,       // Major type 
    &MEDIASUBTYPE_NULL      // Minor type 
}; 
 
const AMOVIESETUP_PIN sudpPins[] = 
{ 
    { L"Input0",             // Pins string name 
      FALSE,                // Is it rendered 
      FALSE,                // Is it an output 
      FALSE,                // Are we allowed none 
      FALSE,                // And allowed many 
      &CLSID_NULL,          // Connects to filter 
      NULL,                 // Connects to pin 
      1,                    // Number of types 
      &sudPinTypes          // Pin information 
    }, 
    { L"Output",            // Pins string name 
      FALSE,                // Is it rendered 
      TRUE,                 // Is it an output 
      FALSE,                // Are we allowed none 
      FALSE,                // And allowed many 
      &CLSID_NULL,          // Connects to filter 
      NULL,                 // Connects to pin 
      1,                    // Number of types 
      &sudPinTypes          // Pin information 
    } 
}; 
 
const AMOVIESETUP_FILTER sudMixer = 
{ 
    &CLSID_VidMixFilter,         // Filter CLSID 
    L"Video Mixing Filter",       // String name 
    MERIT_DO_NOT_USE,       // Filter merit 
    2,                      // Number of pins 
    sudpPins                // Pin information 
}; 
 
 
// List of class IDs and creator functions for the class factory. This 
// provides the link between the OLE entry point in the DLL and an object 
// being created. The class factory will call the static CreateInstance 
 
CFactoryTemplate g_Templates[] = { 
    { L"Video Mixing Filter" 
    , &CLSID_VidMixFilter 
    , CVidMixFilter::CreateInstance 
    , NULL 
    , &sudMixer } 
}; 
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); 
 
STDAPI DllRegisterServer() 
{ 
    return AMovieDllRegisterServer2( TRUE ); 
 
} // DllRegisterServer 
 
 
 
STDAPI DllUnregisterServer() 
{ 
    return AMovieDllRegisterServer2( FALSE ); 
 
} // DllUnregisterServer 
 
 
// 
// DllEntryPoint 
// 
extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); 
 
BOOL APIENTRY DllMain(HANDLE hModule,  
                      DWORD  dwReason,  
                      LPVOID lpReserved) 
{ 
	return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); 
} 
 
 
// 
/****************************************************************************************************************\  
 *											CVidMixFilter													* 
\****************************************************************************************************************/ 
CUnknown *CVidMixFilter::CreateInstance(LPUNKNOWN punk, HRESULT *phr) 
{ 
 
    ASSERT(phr); 
	*phr = S_OK; 
    CVidMixFilter *pNewObject = new CVidMixFilter(NAME("Video Mix Filter"), punk, CLSID_VidMixFilter); 
	if(pNewObject == NULL) 
		*phr  = E_OUTOFMEMORY; 
    return pNewObject; 
 
}  
CVidMixFilter::CVidMixFilter(TCHAR     *pName, LPUNKNOWN pUnk,REFCLSID  clsid) : 
    CBaseFilter(pName,pUnk,&m_csFilter, clsid), 
    m_iInputPinIndex(0), 
    m_pOutput(NULL), 
	m_pInput(NULL), 
    m_bEOSDelivered(FALSE), 
    m_bQualityChanged(FALSE), 
    m_bSampleSkipped(FALSE) 
	//m_pAllocator(NULL) 
{ 
 
	//this->AddRef(); 
 
	ZeroMemory(m_ppInputPins,sizeof(CVidMixInputPin*)*MAX_INPUTPIN_COUNT); 
#ifdef PERF 
    RegisterPerfId(); 
#endif //  PERF 
} 
 
#ifdef UNICODE 
CVidMixFilter::CVidMixFilter(char     *pName, LPUNKNOWN pUnk, REFCLSID  clsid) : 
    CBaseFilter(pName,pUnk,&m_csFilter, clsid), 
	m_iInputPinIndex(0), 
    m_pInput(NULL), 
    m_pOutput(NULL), 
    m_bEOSDelivered(FALSE), 
    m_bQualityChanged(FALSE), 
    m_bSampleSkipped(FALSE) 
	//m_pAllocator(NULL) 
{ 
	//this->AddRef(); 
	ZeroMemory(m_ppInputPins,sizeof(CVidMixInputPin*)*MAX_INPUTPIN_COUNT); 
#ifdef PERF 
    RegisterPerfId(); 
#endif //  PERF 
} 
#endif 
 
BOOL CVidMixFilter::AnyInputPinConnect() 
{ 
	if(m_pInput) 
		if(m_pInput->IsConnected())return TRUE; 
	BOOL bAnyInConnect= 0; 
	 
	for(int i=0;iIsConnected() == TRUE){ 
				bAnyInConnect = TRUE; 
				break; 
			} 
	} 
	return bAnyInConnect; 
} 
 
CVidMixFilter::~CVidMixFilter(void) 
{ 
	if(m_pInput) 
		delete m_pInput; 
	for(int i=0;iIsConnected() ) 
	//		count++; 
	//} 
	//count ++; 
	//if(count>MAX_INPUTPIN_COUNT)count = MAX_INPUTPIN_COUNT; 
	// 
	//return count +1;//inputpins +	outpinps; 
	//if(bNoUnconnect) 
	//{ 
 //      for(int i=0;iAddRef(); 
	//		   break; 
	//	   } 
	//   } 
	//	 
	//} 
	//int count = 0; 
	//for(int i=0;iAddRef(); 
			 
		} 
	 } 
	if(n == 0) 
		return m_pInput; 
	if( n == 1) 
		 return m_ppInputPins[n-1]; 
	  
	if(n == 2) 
		 return m_pOutput; 
 
} 
STDMETHODIMP CVidMixFilter::FindPin(LPCWSTR Id, IPin **ppPin) 
{ 
	CheckPointer(ppPin,E_POINTER); 
    ValidateReadWritePtr(ppPin,sizeof(IPin *)); 
 
	if(m_pInput){ 
		if(0==lstrcmpW(Id,m_pInput->Name()) ){ 
			*ppPin= m_pInput; 
			(*ppPin)->AddRef(); 
			return NOERROR; 
		} 
	} 
	if(m_pOutput){ 
		if(0==lstrcmpW(Id,m_pOutput->Name()) ){ 
			*ppPin= m_pOutput; 
			(*ppPin)->AddRef(); 
			return NOERROR; 
		} 
	} 
	 
	for(int i=0;iName())){ 
				*ppPin= m_ppInputPins[i]; 
				(*ppPin)->AddRef(); 
				return NOERROR; 
			} 
	} 
	return VFW_E_NOT_FOUND; 
    
} 
HRESULT CVidMixFilter::CheckInputType(const CMediaType* mtIn)  
{ 
	if(mtIn==NULL) return E_OUTOFMEMORY; 
	CBasePin * pIn = GetPin(0); 
	if(pIn == 0 )return E_OUTOFMEMORY; 
	 
	if(pIn->IsConnected()){//已连接主输入脚,则只支持与其格式一致的 
		CMediaType mt ; 
		HRESULT hr = GetMediaType(0,&mt); 
		if((mtIn->formattype == FORMAT_VideoInfo) && IsEqualGUID(*mt.Type(),*mtIn->Type()) && IsEqualGUID(*mt.Subtype(),*mtIn->Subtype())) 
			return S_OK; 
		return VFW_E_TYPE_NOT_ACCEPTED; 
		 
	} 
	else{//未连接主输入脚,则支持所有视频 
	 
		if (mtIn->formattype != FORMAT_VideoInfo) return VFW_E_TYPE_NOT_ACCEPTED; 
		if (!IsEqualGUID(mtIn->majortype	,	MEDIATYPE_Video		))	return VFW_E_TYPE_NOT_ACCEPTED; 
 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_YUY2	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_Y41P 	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_UYVY  	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_RGB565	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_RGB555	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_RGB8 	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_RGB32	))	return S_OK; 
		if (IsEqualGUID(mtIn->subtype		,	MEDIASUBTYPE_RGB24 	))	return S_OK; 
		return VFW_E_TYPE_NOT_ACCEPTED; 
	} 
 
 
}   
HRESULT CVidMixFilter::CheckMediaType(const CMediaType* mtIn, const CMediaType* mtOut)  
{ 
	 
	BOOL bin = 0; 
	bin =  SUCCEEDED(CheckInputType(mtIn)); 
	CMediaType mt ; 
 
	HRESULT hr = GetMediaType(0,&mt); 
	if(FAILED(hr)) return hr; 
 
	if(bin && mt == *mtOut)	return S_OK; 
 
	return VFW_E_TYPE_NOT_ACCEPTED; 
		 
} 
//计算画面叠加后的视频类型(分辨率) 
HRESULT CVidMixFilter::GetMediaType(int iPosition, CMediaType *pMediaType) 
{ 
	if(iPosition!=0) return  VFW_S_NO_MORE_ITEMS; 
	CBasePin* pPrimPin = GetPin(0); 
	if(pPrimPin == 0 )return E_OUTOFMEMORY; 
	//主视频类型 
	CMediaType Primmt ; 
	HRESULT hr = pPrimPin->GetMediaType(0,&Primmt); 
	if(FAILED(hr)) return hr; 
	VIDEOINFO *pPrimVi = 0; 
	if (Primmt.formattype == FORMAT_VideoInfo){ 
			pPrimVi = (VIDEOINFO *)Primmt.Format(); 
	} 
	if(pPrimVi == 0 )return  E_UNEXPECTED; 
 
	//计算画面叠加后的视频类型 
	for(int i=0;iIsConnected()) continue; 
		CMediaType inpinmt ; 
		m_ppInputPins[i]->GetMediaType(0,&inpinmt); 
		if (inpinmt.formattype == FORMAT_VideoInfo){ 
	 
			VIDEOINFO *pVi = (VIDEOINFO *)inpinmt.Format(); 
			pPrimVi->bmiHeader.biWidth += pVi->bmiHeader.biWidth; 
			if(pPrimVi->bmiHeader.biHeight< pVi->bmiHeader.biHeight) 
				pPrimVi->bmiHeader.biHeight= pVi->bmiHeader.biHeight; 
			pPrimVi->bmiHeader.biSizeImage += pVi->bmiHeader.biSizeImage; 
			SetRectEmpty(&(pPrimVi->rcSource)); // we want the whole image area rendered. 
			SetRectEmpty(&(pPrimVi->rcTarget)); // no particular destination rectangle 
			//pPrimVi->AvgTimePerFrame += pVi->AvgTimePerFrame; 
			pPrimVi->dwBitRate += pVi->dwBitRate; 
			Primmt.SetSampleSize(Primmt.GetSampleSize()+inpinmt.GetSampleSize()); 
		} 
		/*else if(inpinmt.formattype == FORMAT_VideoInfo2) 
		{ 
			FORMAT_VideoInfo  
		}*/ 
	} 
 
	*pMediaType = Primmt; 
	return S_OK; 
} 
 
 
 
// override these two functions if you want to inform something 
// about entry to or exit from streaming state. 
 
HRESULT	CVidMixFilter::StartStreaming() 
{ 
    return NOERROR; 
} 
HRESULT	CVidMixFilter::StopStreaming() 
{ 
    return NOERROR; 
} 
 
 
// override this to grab extra interfaces on connection 
 
HRESULT	CVidMixFilter::CheckConnect(PIN_DIRECTION dir,IPin *pPin) 
{ 
    UNREFERENCED_PARAMETER(dir); 
    UNREFERENCED_PARAMETER(pPin); 
    return NOERROR; 
} 
 
 
// place holder to allow derived classes to release any extra interfaces 
 
HRESULT	CVidMixFilter::BreakConnect(PIN_DIRECTION dir) 
{ 
    UNREFERENCED_PARAMETER(dir); 
    return NOERROR; 
} 
 
 
// Let derived classes know about connection completion 
 
HRESULT	CVidMixFilter::CompleteConnect(PIN_DIRECTION direction,IPin *pReceivePin) 
{ 
    UNREFERENCED_PARAMETER(direction); 
    UNREFERENCED_PARAMETER(pReceivePin); 
    return NOERROR; 
} 
 
 
// override this to know when the media type is really set 
 
HRESULT	CVidMixFilter::SetMediaType(PIN_DIRECTION direction,const CMediaType *pmt) 
{ 
    UNREFERENCED_PARAMETER(direction); 
    UNREFERENCED_PARAMETER(pmt); 
    return NOERROR; 
} 
 
HRESULT	CVidMixFilter::DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES* pProp) 
{ 
	 
    AM_MEDIA_TYPE mt; 
    HRESULT hr = m_pOutput->ConnectionMediaType(&mt); 
    if (FAILED(hr)) 
    { 
        return hr; 
    } 
	 
    ASSERT(mt.formattype == FORMAT_VideoInfo); 
	ALLOCATOR_PROPERTIES Request,Actual; 
	ZeroMemory(&Request,sizeof(ALLOCATOR_PROPERTIES)); 
	 
    BITMAPINFOHEADER *pbmi = HEADER(mt.pbFormat); 
	Request.cbBuffer = DIBSIZE(*pbmi); 
	Request.cbAlign  = pProp->cbAlign; 
	Request.cbPrefix = pProp->cbPrefix; 
	Request.cBuffers = pProp->cBuffers; 
	if(pProp->cbBuffer > Request.cbBuffer )Request.cbBuffer = pProp->cbBuffer; 
	 
    if (Request.cbAlign == 0) 
    { 
       Request.cbAlign = 1; 
    } 
    if (Request.cBuffers < 3) 
    { 
        Request.cBuffers = 3; 
    } 
    // Release the format block. 
    FreeMediaType(mt); 
 
    // Set allocator properties. 
     
    hr = pAllocator->SetProperties(&Request, &Actual); 
    if (FAILED(hr))  
    { 
        return hr; 
    } 
    // Even when it succeeds, check the actual result. 
    if (Request.cbBuffer > Actual.cbBuffer)  
    { 
        return E_FAIL; 
    } 
    return S_OK; 
 
} 
 
 
//主输入到输出 
// 
HRESULT	CVidMixFilter::ReceiveSamp1(IMediaSample *pSample) 
{ 
    IMediaSample *pOutSample; 
 
    // default - times are the same 
 
    AM_SAMPLE2_PROPERTIES * const pProps = m_pInput->SampleProps(); 
    DWORD dwFlags = m_bSampleSkipped ? AM_GBF_PREVFRAMESKIPPED : 0; 
 
    // This will prevent the image renderer from switching us to DirectDraw 
    // when we can't do it without skipping frames because we're not on a 
    // keyframe.  If it really has to switch us, it still will, but then we 
    // will have to wait for the next keyframe 
    if (!(pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT)) { 
	dwFlags |= AM_GBF_NOTASYNCPOINT; 
    } 
	//0.得到空Sample 
    ASSERT(m_pOutput->m_pAllocator != NULL); 
    HRESULT hr = m_pOutput->m_pAllocator->GetBuffer( 
             &pOutSample 
             , pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID ? 
                   &pProps->tStart : NULL 
             , pProps->dwSampleFlags & AM_SAMPLE_STOPVALID ? 
                   &pProps->tStop : NULL 
             , dwFlags 
         ); 
  
    if (FAILED(hr)) { 
        return hr; 
    } 
	 
    ASSERT(pOutSample); 
    IMediaSample2 *pOutSample2; 
    if (SUCCEEDED(pOutSample->QueryInterface(IID_IMediaSample2, 
                                             (void **)&pOutSample2))) { 
		//1.设置格式 
        AM_SAMPLE2_PROPERTIES OutProps; 
        EXECUTE_ASSERT(SUCCEEDED(pOutSample2->GetProperties( 
            FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, tStart), (PBYTE)&OutProps) 
        )); 
        OutProps.dwTypeSpecificFlags = pProps->dwTypeSpecificFlags; 
        OutProps.dwSampleFlags = 
            (OutProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED) | 
            (pProps->dwSampleFlags & ~AM_SAMPLE_TYPECHANGED); 
        OutProps.tStart = pProps->tStart; 
        OutProps.tStop  = pProps->tStop; 
        OutProps.cbData = FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId); 
 
        hr = pOutSample2->SetProperties( 
            FIELD_OFFSET(AM_SAMPLE2_PROPERTIES, dwStreamId), 
            (PBYTE)&OutProps 
        ); 
        if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) { 
            m_bSampleSkipped = FALSE; 
        } 
        pOutSample2->Release(); 
    } else { 
        if (pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) { 
            pOutSample->SetTime(&pProps->tStart, 
                                &pProps->tStop); 
        } 
        if (pProps->dwSampleFlags & AM_SAMPLE_SPLICEPOINT) { 
            pOutSample->SetSyncPoint(TRUE); 
        } 
        if (pProps->dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) { 
            pOutSample->SetDiscontinuity(TRUE); 
            m_bSampleSkipped = FALSE; 
        } 
		 
        LONGLONG MediaStart, MediaEnd; 
        if (pSample->GetMediaTime(&MediaStart,&MediaEnd) == NOERROR) { 
            pOutSample->SetMediaTime(&MediaStart,&MediaEnd); 
        } 
    } 
	//2.复制数据 
	 
	CMediaType InType; 
	CMediaType OutType; 
	BYTE *pScr = 0; 
	BYTE *pDst = 0; 
	try{ 
		hr = pOutSample->GetPointer(&pDst); 
		if(FAILED(hr)) throw hr; 
		if(!pDst) throw E_OUTOFMEMORY; 
		 
		hr = pSample->GetPointer(&pScr); 
		if(FAILED(hr)) throw hr; 
		if(!pScr) throw E_OUTOFMEMORY; 
 
		hr = m_pInput->GetMediaType(0,&InType); 
		if(FAILED(hr)) throw hr; 
 
		hr = GetMediaType(0,&OutType); 
		if(FAILED(hr)) throw hr; 
		 
		if (InType.formattype != FORMAT_VideoInfo) throw E_FAIL; 
		if (OutType.formattype != FORMAT_VideoInfo) throw E_FAIL; 
 
		VIDEOINFO *pInVi = (VIDEOINFO *)InType.pbFormat; 
		VIDEOINFO *pOutVi = (VIDEOINFO *)OutType.pbFormat; 
		int iwSize =  pInVi->bmiHeader.biWidth * pInVi->bmiHeader.biBitCount/8 ; 
		int owSize =  pOutVi->bmiHeader.biWidth * pOutVi->bmiHeader.biBitCount/8; 
		//逐行复制 
		for(int i=0;ibmiHeader.biHeight;i++){ 
			CopyMemory(	pDst+ i * owSize, 
						pScr+ i * iwSize,iwSize); 
						 
		} 
 
		//3.等待其它正在写入的Pin完成 
		WaitForSingleObject(((CVidMixAllocator::CVidMixMediaSample*)pOutSample)->m_hEventWriting, INFINITE); 
		//向下传 
	 
		pOutSample->SetActualDataLength(DIBSIZE(pOutVi->bmiHeader)); 
		m_pOutput->Deliver(pOutSample); 
 
	} 
	catch(HRESULT hr1) 
	{ 
		hr = hr1; 
	} 
	RELEASE(pOutSample); 
    return hr; 
} 
 
 
 
//辅输入Receiv调用 
HRESULT	CVidMixFilter::ReceiveSamp2(IMediaSample *pSample) 
{ 
	 
	IMediaSample *pOutSample = 0; 
	CMediaType InType ; 
	CMediaType OutType; 
	CMediaType PrimType; 
	BYTE *pScr = 0; 
	BYTE *pDst = 0; 
	HRESULT hr = S_OK; 
	 
	try{ 
		//0.得到空Sample 
		CVidMixAllocator * pAllocator = (CVidMixAllocator *)m_pOutput->m_pAllocator; 
		if(pAllocator == 0 ) throw E_OUTOFMEMORY; 
 
		pAllocator->get_Buffer(&pOutSample,0,0,0); 
		if(pOutSample == 0) throw E_OUTOFMEMORY; 
		//1.写入 
		hr =pOutSample->GetPointer(&pDst); 
		if(FAILED(hr)) throw hr; 
		if(!pDst) throw E_OUTOFMEMORY; 
		 
		hr =pSample->GetPointer(&pScr); 
		if(FAILED(hr)) throw hr; 
		if(!pScr) throw E_OUTOFMEMORY; 
		 
		hr = GetPin(1)->GetMediaType(0,&InType); 
		if(FAILED(hr)) throw hr; 
 
		hr = GetMediaType(0,&OutType); 
		if(FAILED(hr)) throw hr; 
 
		hr = GetPin(0)->GetMediaType(0,&PrimType); 
		if(FAILED(hr)) throw hr; 
		 
		if (InType.formattype != FORMAT_VideoInfo) throw E_FAIL; 
		if (OutType.formattype != FORMAT_VideoInfo) throw E_FAIL; 
 
		VIDEOINFO *pInVi = (VIDEOINFO *)InType.pbFormat; 
		VIDEOINFO *pOutVi = (VIDEOINFO *)OutType.pbFormat; 
		VIDEOINFO *pPrimVi = (VIDEOINFO *)PrimType.pbFormat; 
		int iwSize =  pInVi->bmiHeader.biWidth * pInVi->bmiHeader.biBitCount/8 ; 
		int iwPrimSize =  pPrimVi->bmiHeader.biWidth * pPrimVi->bmiHeader.biBitCount/8 ; 
		 
		//逐行复制 
		for(int i=0;ibmiHeader.biHeight;i++){ 
			CopyMemory(	pDst+ i * pOutVi->bmiHeader.biWidth * pOutVi->bmiHeader.biBitCount/8  + iwPrimSize, 
						pScr+ i * iwSize,iwSize); 
						 
		} 
		//2.清除写入标志 
		pAllocator->release_Buffer(pOutSample); 
		 
 
 
	} 
	catch(HRESULT hr1) 
	{ 
		hr = hr1; 
	} 
	 
	return hr; 
} 
// Return S_FALSE to mean "pass the note on upstream" 
// Return NOERROR (Same as S_OK) 
// to mean "I've done something about it, don't pass it on" 
HRESULT CVidMixFilter::AlterQuality(Quality q) 
{ 
    UNREFERENCED_PARAMETER(q); 
    return S_FALSE; 
} 
 
 
// EndOfStream received. Default behaviour is to deliver straight 
// downstream, since we have no queued data. If you overrode Receive 
// and have queue data, then you need to handle this and deliver EOS after 
// all queued data is sent 
HRESULT	CVidMixFilter::EndOfStream(void) 
{ 
    HRESULT hr = NOERROR; 
    if (m_pOutput != NULL) { 
        hr = m_pOutput->DeliverEndOfStream(); 
    } 
 
    return hr; 
} 
 
 
// enter flush state. Receives already blocked 
// must override this if you have queued data or a worker thread 
HRESULT	CVidMixFilter::BeginFlush(void) 
{ 
    HRESULT hr = NOERROR; 
    if (m_pOutput != NULL) { 
	// block receives -- done by caller (CBaseInputPin::BeginFlush) 
 
	// discard queued data -- we have no queued data 
 
	// free anyone blocked on receive - not possible in this filter 
 
	// call downstream 
	hr = m_pOutput->DeliverBeginFlush(); 
    } 
    return hr; 
} 
 
 
// leave flush state. must override this if you have queued data 
// or a worker thread 
HRESULT	CVidMixFilter::EndFlush(void) 
{ 
    // sync with pushing thread -- we have no worker thread 
 
    // ensure no more data to go downstream -- we have no queued data 
 
    // call EndFlush on downstream pins 
    ASSERT (m_pOutput != NULL); 
    return m_pOutput->DeliverEndFlush(); 
 
    // caller (the input pin's method) will unblock Receives 
} 
 
 
// override these so that the derived filter can catch them 
 
STDMETHODIMP	CVidMixFilter::Stop() 
{ 
    CAutoLock lck1(&m_csFilter); 
    if (m_State == State_Stopped) { 
        return NOERROR; 
    } 
 
	 
	//若无任何输入连接或者输出未连接,则判断为停止状态 
    if (!AnyInputPinConnect() || m_pOutput->IsConnected() == FALSE) { 
                m_State = State_Stopped; 
                m_bEOSDelivered = FALSE; 
                return NOERROR; 
    } 
 
	//停止所有输入pin的线程 
	if(m_pInput)m_pInput->Inactive(); 
 
	for(int i=0;iInactive() ; 
	} 
   
    // synchronize with Receive calls 
 
    CAutoLock lck2(&m_csReceive); 
    m_pOutput->Inactive(); 
 
    // allow a class derived from CVidMixFilter 
    // to know about starting and stopping streaming 
 
    HRESULT hr = StopStreaming(); 
    if (SUCCEEDED(hr)) { 
	// complete the state transition 
	m_State = State_Stopped; 
	m_bEOSDelivered = FALSE; 
    } 
    return hr; 
} 
 
 
STDMETHODIMP	CVidMixFilter::Pause() 
{ 
    CAutoLock lck(&m_csFilter); 
    HRESULT hr = NOERROR; 
 
 
    if (m_State == State_Paused) { 
        // (This space left deliberately blank) 
    } 
 
    // If we have no input pin or it isn't yet connected then when we are 
    // asked to pause we deliver an end of stream to the downstream filter. 
    // This makes sure that it doesn't sit there forever waiting for 
    // samples which we cannot ever deliver without an input connection. 
	 
	else if (!AnyInputPinConnect()) { 
			if (m_pOutput && m_bEOSDelivered == FALSE) { 
				m_pOutput->DeliverEndOfStream(); 
				m_bEOSDelivered = TRUE; 
			} 
			m_State = State_Paused; 
	} 
    // We may have an input connection but no output connection 
    // However, if we have an input pin we do have an output pin 
 
    else if (m_pOutput->IsConnected() == FALSE) { 
        m_State = State_Paused; 
    } 
 
    else { 
	if (m_State == State_Stopped) { 
	    // allow a class derived from CVidMixFilter 
	    // to know about starting and stopping streaming 
            CAutoLock lck2(&m_csReceive); 
	    hr = StartStreaming(); 
	} 
	if (SUCCEEDED(hr)) { 
	    hr = CBaseFilter::Pause(); 
	} 
    } 
 
    m_bSampleSkipped = FALSE; 
    m_bQualityChanged = FALSE; 
    return hr; 
} 
 
HRESULT	CVidMixFilter::NewSegment(  REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) 
{ 
    if (m_pOutput != NULL) { 
        return m_pOutput->DeliverNewSegment(tStart, tStop, dRate); 
    } 
    return S_OK; 
} 
/****************************************************************************************************************\  
 *											CVidMixInputPin														* 
\****************************************************************************************************************/ 
 
HRESULT	CVidMixInputPin::CheckStreaming() 
{ 
    ASSERT(m_pVidMixFilter->m_pOutput != NULL); 
    if (!m_pVidMixFilter->m_pOutput->IsConnected()) { 
        return VFW_E_NOT_CONNECTED; 
    } else { 
        //  Shouldn't be able to get any data if we're not connected! 
        ASSERT(IsConnected()); 
 
        //  we're flushing 
        if (m_bFlushing) { 
            return S_FALSE; 
        } 
        //  Don't process stuff in Stopped state 
        if (IsStopped()) { 
            return VFW_E_WRONG_STATE; 
        } 
        if (m_bRunTimeError) { 
    	    return VFW_E_RUNTIME_ERROR; 
        } 
        return S_OK; 
    } 
} 
 
 
// ================================================================= 
// Implements the CVidMixInputPin class 
// ================================================================= 
 
 
// constructor 
 
CVidMixInputPin::CVidMixInputPin( TCHAR *pObjectName, CVidMixFilter *pVidMixFilter,   HRESULT * phr, LPCWSTR pName) 
    : CBaseInputPin(pObjectName, pVidMixFilter, &pVidMixFilter->m_csFilter, phr, pName) 
{ 
    DbgLog((LOG_TRACE,2,TEXT("CVidMixInputPin::CVidMixInputPin"))); 
    m_pVidMixFilter = pVidMixFilter; 
} 
 
#ifdef UNICODE 
CVidMixInputPin::CVidMixInputPin( CHAR *pObjectName, CVidMixFilter *pVidMixFilter,HRESULT * phr,LPCWSTR pName) 
    : CBaseInputPin(pObjectName, pVidMixFilter, &pVidMixFilter->m_csFilter, phr, pName) 
{ 
    DbgLog((LOG_TRACE,2,TEXT("CVidMixInputPin::CVidMixInputPin"))); 
    m_pVidMixFilter = pVidMixFilter; 
} 
#endif 
 
// provides derived filter a chance to grab extra interfaces 
 
HRESULT	CVidMixInputPin::CheckConnect(IPin *pPin) 
{ 
    HRESULT hr = m_pVidMixFilter->CheckConnect(PINDIR_INPUT,pPin); 
    if (FAILED(hr)) { 
    	return hr; 
    } 
    return CBaseInputPin::CheckConnect(pPin); 
} 
 
 
// provides derived filter a chance to release it's extra interfaces 
 
HRESULT	CVidMixInputPin::BreakConnect() 
{ 
    //  Can't disconnect unless stopped 
    ASSERT(IsStopped()); 
    m_pVidMixFilter->BreakConnect(PINDIR_INPUT); 
    return CBaseInputPin::BreakConnect(); 
} 
 
 
// Let derived class know when the input pin is connected 
 
HRESULT	CVidMixInputPin::CompleteConnect(IPin *pReceivePin) 
{ 
    HRESULT hr = m_pVidMixFilter->CompleteConnect(PINDIR_INPUT,pReceivePin); 
    if (FAILED(hr)) { 
        return hr; 
    } 
    return CBaseInputPin::CompleteConnect(pReceivePin); 
} 
 
 
//检查是否支持该格式 
 
HRESULT	CVidMixInputPin::CheckMediaType(const CMediaType* pmt) 
{ 
    // Check the input type 
 
    HRESULT hr = m_pVidMixFilter->CheckInputType(pmt); 
    if (S_OK != hr) { 
        return hr; 
    } 
 
    // if the output pin is still connected, then we have 
    // to check the transform not just the input format 
 
    if ((m_pVidMixFilter->m_pOutput != NULL) && 
        (m_pVidMixFilter->m_pOutput->IsConnected())) { 
            return m_pVidMixFilter->CheckMediaType( 
                      pmt, 
		      &m_pVidMixFilter->m_pOutput->CurrentMediaType()); 
    } else { 
        return hr; 
    } 
} 
 
 
// set the media type for this connection 
 
HRESULT	CVidMixInputPin::SetMediaType(const CMediaType* mtIn) 
{ 
    // Set the base class media type (should always succeed) 
    HRESULT hr = CBasePin::SetMediaType(mtIn); 
    if (FAILED(hr)) { 
        return hr; 
    } 
	//m_mt = mtIn; 
    // check the transform can be done (should always succeed) 
	//ASSERT(SUCCEEDED(m_pVidMixFilter->CheckInputType(mtIn))); 
 
    return m_pVidMixFilter->SetMediaType(PINDIR_INPUT,mtIn); 
} 
HRESULT CVidMixInputPin::GetMediaType(int iPosition, CMediaType *pMediaType) 
{ 
	 
    UNREFERENCED_PARAMETER(iPosition); 
    UNREFERENCED_PARAMETER(pMediaType); 
	if(iPosition!=0) return E_UNEXPECTED; 
	if(m_pVidMixFilter->m_pInput) 
		if(	/*m_pVidMixFilter->m_pInput->*/IsConnected()	){ 
			IPin *pin = 0; 
			/*m_pVidMixFilter->m_pInput->*/ConnectedTo(&pin); 
			if(pin){ 
				//AM_MEDIA_TYPE *pMt; 
				pin->ConnectionMediaType(pMediaType); 
				pin->Release(); 
				return S_OK; 
			} 
			 
		} 
    return E_UNEXPECTED; 
} 
 
STDMETHODIMP CVidMixInputPin::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) 
{ 
	  return NOERROR; 
} 
// ================================================================= 
// Implements IMemInputPin interface 
// ================================================================= 
 
 
// provide EndOfStream that passes straight downstream 
// (there is no queued data) 
STDMETHODIMP	CVidMixInputPin::EndOfStream(void) 
{ 
    CAutoLock lck(&m_pVidMixFilter->m_csReceive); 
    HRESULT hr = CheckStreaming(); 
    if (S_OK == hr) { 
       hr = m_pVidMixFilter->EndOfStream(); 
    } 
    return hr; 
} 
 
 
// enter flushing state. Call default handler to block Receives, then 
// pass to overridable method in filter 
STDMETHODIMP	CVidMixInputPin::BeginFlush(void) 
{ 
    CAutoLock lck(&m_pVidMixFilter->m_csFilter); 
    //  Are we actually doing anything? 
    ASSERT(m_pVidMixFilter->m_pOutput != NULL); 
    if (!IsConnected() || 
        !m_pVidMixFilter->m_pOutput->IsConnected()) { 
        return VFW_E_NOT_CONNECTED; 
    } 
    HRESULT hr = CBaseInputPin::BeginFlush(); 
    if (FAILED(hr)) { 
    	return hr; 
    } 
 
    return m_pVidMixFilter->BeginFlush(); 
} 
 
 
// leave flushing state. 
// Pass to overridable method in filter, then call base class 
// to unblock receives (finally) 
STDMETHODIMP	CVidMixInputPin::EndFlush(void) 
{ 
    CAutoLock lck(&m_pVidMixFilter->m_csFilter); 
    //  Are we actually doing anything? 
    ASSERT(m_pVidMixFilter->m_pOutput != NULL); 
    if (!IsConnected() || 
        !m_pVidMixFilter->m_pOutput->IsConnected()) { 
        return VFW_E_NOT_CONNECTED; 
    } 
 
    HRESULT hr = m_pVidMixFilter->EndFlush(); 
    if (FAILED(hr)) { 
        return hr; 
    } 
 
    return CBaseInputPin::EndFlush(); 
} 
 
 
//从CVidMixAllocator的m_lFree列表中取出空Sample 
//再设置写入标志,然后放入m_qWait队列中, 
//开始写入数据,完成后清除写入标志 
 
HRESULT	CVidMixInputPin::Receive(IMediaSample * pSample) 
{ 
    HRESULT hr = CBaseInputPin::Receive(pSample); 
	if(hr != NOERROR) return hr; 
	 
	return m_pVidMixFilter->ReceiveSamp2(pSample); 
 
} 
//STDMETHODIMP	CVidMixInputPin::GetAllocator( IMemAllocator **ppAllocator) 
//{ 
//	return m_pVidMixFilter->GetAllocator(ppAllocator); 
//} 
 
 
 
// override to pass downstream 
STDMETHODIMP	CVidMixInputPin::NewSegment(REFERENCE_TIME tStart,REFERENCE_TIME tStop,double dRate) 
{ 
    //  Save the values in the pin 
    CBasePin::NewSegment(tStart, tStop, dRate); 
    return m_pVidMixFilter->NewSegment(tStart, tStop, dRate); 
} 
/****************************************************************************************************************\  
 *											CVidMixPrimInputPin													* 
\****************************************************************************************************************/ 
 
STDMETHODIMP CVidMixPrimInputPin::Receive(IMediaSample *pSample) 
{ 
	HRESULT hr = CBaseInputPin::Receive(pSample); 
	if(hr != NOERROR) return hr; 
	 
	return m_pVidMixFilter->ReceiveSamp1(pSample); 
 
 
} 
/****************************************************************************************************************\  
 *											CVidMixOutputPin													* 
\****************************************************************************************************************/ 
 
// constructor 
CVidMixOutputPin::CVidMixOutputPin(TCHAR *pObjectName, CVidMixFilter *pVidMixFilter, HRESULT * phr,LPCWSTR pPinName) 
    : CBaseOutputPin(pObjectName, pVidMixFilter, &pVidMixFilter->m_csFilter, phr, pPinName), 
      m_pPosition(NULL) 
{ 
    DbgLog((LOG_TRACE,2,TEXT("CVidMixOutputPin::CVidMixOutputPin"))); 
    m_pVidMixFilter = pVidMixFilter; 
 
} 
 
#ifdef UNICODE 
CVidMixOutputPin::CVidMixOutputPin(  CHAR *pObjectName,  CVidMixFilter *pVidMixFilter,    HRESULT * phr, LPCWSTR pPinName) 
    : CBaseOutputPin(pObjectName, pVidMixFilter, &pVidMixFilter->m_csFilter, phr, pPinName), 
      m_pPosition(NULL) 
{ 
    DbgLog((LOG_TRACE,2,TEXT("CVidMixOutputPin::CVidMixOutputPin"))); 
    m_pVidMixFilter = pVidMixFilter; 
 
} 
#endif 
 
// destructor 
 
CVidMixOutputPin::~CVidMixOutputPin() 
{ 
    DbgLog((LOG_TRACE,2,TEXT("CVidMixOutputPin::~CVidMixOutputPin"))); 
 
    if (m_pPosition) m_pPosition->Release(); 
} 
 
 
// overriden to expose IMediaPosition and IMediaSeeking control interfaces 
 
STDMETHODIMP	CVidMixOutputPin::NonDelegatingQueryInterface(REFIID riid, void **ppv) 
{ 
    CheckPointer(ppv,E_POINTER); 
    ValidateReadWritePtr(ppv,sizeof(PVOID)); 
    *ppv = NULL; 
 
    if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) { 
 
        // we should have an input pin by now 
 
        ASSERT(m_pVidMixFilter->m_pInput != NULL); 
 
        if (m_pPosition == NULL) { 
 
            HRESULT hr = CreatePosPassThru( 
                             GetOwner(), 
                             FALSE, 
                             (IPin *)m_pVidMixFilter->m_pInput, 
                             &m_pPosition); 
            if (FAILED(hr)) { 
                return hr; 
            } 
        } 
        return m_pPosition->QueryInterface(riid, ppv); 
    } else { 
        return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv); 
    } 
} 
 
 
// provides derived filter a chance to grab extra interfaces 
 
HRESULT	CVidMixOutputPin::CheckConnect(IPin *pPin) 
{ 
    //至少有一个输入pin被连接 
	 
  
    if (!m_pVidMixFilter->AnyInputPinConnect()) { 
	    return E_UNEXPECTED; 
    } 
 
    HRESULT hr = m_pVidMixFilter->CheckConnect(PINDIR_OUTPUT,pPin); 
    if (FAILED(hr)) { 
	    return hr; 
    } 
    return CBaseOutputPin::CheckConnect(pPin); 
} 
 
 
// provides derived filter a chance to release it's extra interfaces 
 
HRESULT	CVidMixOutputPin::BreakConnect() 
{ 
    //  Can't disconnect unless stopped 
    ASSERT(IsStopped()); 
    m_pVidMixFilter->BreakConnect(PINDIR_OUTPUT); 
    return CBaseOutputPin::BreakConnect(); 
} 
 
 
// Let derived class know when the output pin is connected 
 
HRESULT	CVidMixOutputPin::CompleteConnect(IPin *pReceivePin) 
{ 
    HRESULT hr = m_pVidMixFilter->CompleteConnect(PINDIR_OUTPUT,pReceivePin); 
    if (FAILED(hr)) { 
        return hr; 
    } 
    return CBaseOutputPin::CompleteConnect(pReceivePin); 
} 
 
 
// check a given transform - must have selected input type first 
 
HRESULT	CVidMixOutputPin::CheckMediaType(const CMediaType* pmtOut) 
{ 
    //确保已经有输入pin连接 
	if (!m_pVidMixFilter->m_pInput->IsConnected()) { 
	        return E_INVALIDARG; 
    } 
 
    return m_pVidMixFilter->CheckMediaType( 
			&m_pVidMixFilter->m_pInput->CurrentMediaType(), 
				    pmtOut); 
} 
 
 
// called after we have agreed a media type to actually set it in which case 
// we run the CheckMediaType function to get the output format type again 
 
HRESULT	CVidMixOutputPin::SetMediaType(const CMediaType* pmtOut) 
{ 
	 
    HRESULT hr = NOERROR; 
	ASSERT(m_pVidMixFilter->m_pInput != NULL); 
 
    ASSERT(m_pVidMixFilter->m_pInput->CurrentMediaType().IsValid()); 
 
    // Set the base class media type (should always succeed) 
    hr = CBasePin::SetMediaType(pmtOut); 
    if (FAILED(hr)) { 
        return hr; 
    } 
 
#ifdef DEBUG 
    if (FAILED(m_pVidMixFilter->CheckMediaType(&m_pVidMixFilter-> 
					m_pInput->CurrentMediaType(),pmtOut))) { 
	DbgLog((LOG_ERROR,0,TEXT("*** This filter is accepting an output media type"))); 
	DbgLog((LOG_ERROR,0,TEXT("    that it can't currently transform to.  I hope"))); 
	DbgLog((LOG_ERROR,0,TEXT("    it's smart enough to reconnect its input."))); 
    } 
#endif 
 
    return m_pVidMixFilter->SetMediaType(PINDIR_OUTPUT,pmtOut); 
} 
 
 
// pass the buffer size decision through to the main transform class 
 
HRESULT	CVidMixOutputPin::DecideBufferSize(IMemAllocator * pAllocator, ALLOCATOR_PROPERTIES* pProp) 
{ 
	//return CBaseOutputPin::DecideBufferSize(pAllocator,pProp); 
    return m_pVidMixFilter->DecideBufferSize(pAllocator, pProp); 
} 
 
 
 
// return a specific media type indexed by iPosition 
 
HRESULT	CVidMixOutputPin::GetMediaType(int iPosition, CMediaType *pMediaType) 
{ 
     
	if (m_pVidMixFilter->AnyInputPinConnect()) { 
        return m_pVidMixFilter->GetMediaType(iPosition,pMediaType); 
    } else { 
        return VFW_S_NO_MORE_ITEMS; 
    } 
} 
HRESULT CVidMixOutputPin::DecideAllocator(IMemInputPin *pPin, IMemAllocator **ppAlloc) 
{ 
     HRESULT hr = NOERROR; 
    *ppAlloc = NULL; 
 
    // get downstream prop request 
    // the derived class may modify this in DecideBufferSize, but 
    // we assume that he will consistently modify it the same way, 
    // so we only get it once 
    ALLOCATOR_PROPERTIES prop; 
    ZeroMemory(&prop, sizeof(prop)); 
 
    // whatever he returns, we assume prop is either all zeros 
    // or he has filled it out. 
    pPin->GetAllocatorRequirements(&prop); 
 
    // if he doesn't care about alignment, then set it to 1 
    if (prop.cbAlign == 0) { 
        prop.cbAlign = 1; 
    } 
 
    /* Try the allocator provided by the input pin */ 
 
   /* hr = pPin->GetAllocator(ppAlloc); 
    if (SUCCEEDED(hr)) { 
 
        hr = DecideBufferSize(*ppAlloc, &prop); 
        if (SUCCEEDED(hr)) { 
            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 */ 
 
    hr = InitAllocator(ppAlloc); 
    if (SUCCEEDED(hr)) { 
 
        // note - the properties passed here are in the same 
        // structure as above and may have been modified by 
        // the previous call to DecideBufferSize 
        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; 
} 
HRESULT CVidMixOutputPin::InitAllocator(IMemAllocator **ppAlloc) 
{ 
	CheckPointer(ppAlloc, E_POINTER); 
    if (m_pAllocator)   
    { 
        // We already have an allocator, so return that one. 
        *ppAlloc = m_pAllocator; 
        (*ppAlloc)->AddRef(); 
        return S_OK; 
    } 
    // No allocator yet, so propose our custom allocator. The exact code 
    // here will depend on your custom allocator class definition. 
    HRESULT hr = S_OK; 
    CVidMixAllocator *pAlloc = new CVidMixAllocator("VidMix Allocator",0,&hr); 
    if (!pAlloc) 
    { 
        return E_OUTOFMEMORY; 
    } 
    if (FAILED(hr)) 
    { 
        delete pAlloc; 
        return hr; 
    } 
	m_pAllocator = pAlloc; 
    // Return the IMemAllocator interface to the caller. 
    return pAlloc->QueryInterface(IID_IMemAllocator, (void**)ppAlloc); 
} 
 
 
// Override this if you can do something constructive to act on the 
// quality message.  Consider passing it upstream as well 
 
// Pass the quality mesage on upstream. 
 
STDMETHODIMP	CVidMixOutputPin::Notify(IBaseFilter * pSender, Quality q) 
{ 
    UNREFERENCED_PARAMETER(pSender); 
    ValidateReadPtr(pSender,sizeof(IBaseFilter)); 
 
    // First see if we want to handle this ourselves 
    HRESULT hr = m_pVidMixFilter->AlterQuality(q); 
    if (hr!=S_FALSE) { 
        return hr;        // either S_OK or a failure 
    } 
 
    // S_FALSE means we pass the message on. 
    // Find the quality sink for our input pin and send it there 
 
	ASSERT(m_pVidMixFilter->m_pInput != NULL); 
 
    return m_pVidMixFilter->m_pInput->PassNotify(q); 
 
} // Notify 
 
/****************************************************************************************************************\  
 *											CVidMixAllocator													* 
\****************************************************************************************************************/ 
CVidMixAllocator::CVidMixAllocator( 
    TCHAR *pName, 
    LPUNKNOWN pUnk, 
    HRESULT *phr) 
    : CMemAllocator(pName, pUnk, phr) 
	,m_hWaitForFreeBuff(0) 
{ 
	m_hWaitForFreeBuff = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); 
	if (m_hWaitForFreeBuff == NULL) { 
			*phr = E_OUTOFMEMORY; 
	} 
} 
 
#ifdef UNICODE 
CVidMixAllocator::CVidMixAllocator( 
    CHAR *pName, 
    LPUNKNOWN pUnk, 
    HRESULT *phr) 
    : CMemAllocator(pName, pUnk, phr) 
	,m_hWaitForFreeBuff(0) 
{ 
	m_hWaitForFreeBuff = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL); 
	if (m_hWaitForFreeBuff == NULL) { 
			*phr = E_OUTOFMEMORY; 
	} 
} 
#endif 
CVidMixAllocator::~CVidMixAllocator() 
{ 
	//CMediaSample * pSample = 0; 
	 
	if (m_hWaitForFreeBuff != NULL) { 
		EXECUTE_ASSERT(CloseHandle(m_hWaitForFreeBuff)); 
	} 
    Decommit(); 
    ReallyFree(); 
	 
} 
STDMETHODIMP CVidMixAllocator::Decommit() 
{ 
    BOOL bRelease = FALSE; 
    { 
        /* Check we are not already decommitted */ 
        CAutoLock cObjectLock(this); 
        if (m_bCommitted == FALSE) { 
            if (m_bDecommitInProgress == FALSE) { 
                return NOERROR; 
            } 
        } 
		for (;;) { 
			CVidMixMediaSample * pSample = m_qWait.RemoveHead(); 
				if (pSample != NULL) { 
					m_lFree.Add(pSample); 
				} 
				else break; 
		} 
		 
        /* No more GetBuffer calls will succeed */ 
        m_bCommitted = FALSE; 
 
        // are any buffers outstanding? 
        if (m_lFree.GetCount() < m_lAllocated) { 
            // please complete the decommit when last buffer is freed 
            m_bDecommitInProgress = TRUE; 
        } else { 
            m_bDecommitInProgress = FALSE; 
 
            // need to complete the decommit here as there are no 
            // outstanding buffers 
 
            Free(); 
            bRelease = TRUE; 
        } 
 
        // Tell anyone waiting that they can go now so we can 
        // reject their call 
		 if(m_hWaitForFreeBuff) 
			ReleaseSemaphore(m_hWaitForFreeBuff, m_lFree.GetCount()+1, 0); 
		 NotifySample(); 
    } 
 
    if (bRelease) { 
        Release(); 
    } 
    return NOERROR; 
} 
HRESULT	CVidMixAllocator::Alloc(void) 
{ 
    CAutoLock lck(this); 
 
    /* Check he has called SetProperties */ 
    HRESULT hr = CBaseAllocator::Alloc(); 
    if (FAILED(hr)) { 
        return hr; 
    } 
 
    /* If the requirements haven't changed then don't reallocate */ 
    if (hr == S_FALSE) { 
        ASSERT(m_pBuffer); 
        return NOERROR; 
    } 
    ASSERT(hr == S_OK); // we use this fact in the loop below 
 
    /* Free the old resources */ 
    if (m_pBuffer) { 
        ReallyFree(); 
    } 
 
    /* Compute the aligned size */ 
    LONG lAlignedSize = m_lSize + m_lPrefix; 
    if (m_lAlignment > 1) { 
        LONG lRemainder = lAlignedSize % m_lAlignment; 
        if (lRemainder != 0) { 
            lAlignedSize += (m_lAlignment - lRemainder); 
        } 
    } 
 
    /* Create the contiguous memory block for the samples 
       making sure it's properly aligned (64K should be enough!) 
    */ 
    ASSERT(lAlignedSize % m_lAlignment == 0); 
 
    m_pBuffer = (PBYTE)VirtualAlloc(NULL, 
                    m_lCount * lAlignedSize, 
                    MEM_COMMIT, 
                    PAGE_READWRITE); 
 
    if (m_pBuffer == NULL) { 
        return E_OUTOFMEMORY; 
    } 
 
    LPBYTE pNext = m_pBuffer; 
    CMediaSample *pSample; 
 
    ASSERT(m_lAllocated == 0); 
 
    // Create the new samples - we have allocated m_lSize bytes for each sample 
    // plus m_lPrefix bytes per sample as a prefix. We set the pointer to 
    // the memory after the prefix - so that GetPointer() will return a pointer 
    // to m_lSize bytes. 
    for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) { 
 
 
        pSample = new CVidMixMediaSample( 
                            NAME("Default memory media sample"), 
                this, 
                            &hr, 
                            pNext + m_lPrefix,      // GetPointer() value 
                            m_lSize);               // not including prefix 
 
            ASSERT(SUCCEEDED(hr)); 
        if (pSample == NULL) { 
            return E_OUTOFMEMORY; 
        } 
 
        // This CANNOT fail 
        m_lFree.Add(pSample);// 
    } 
 
    m_bChanged = FALSE; 
    return NOERROR; 
} 
//用于主输入 
//先从m_qWait队列中取缓冲区,再从m_lFree中取 
HRESULT CVidMixAllocator::GetBuffer(IMediaSample **ppBuffer, 
                                  REFERENCE_TIME *pStartTime, 
                                  REFERENCE_TIME *pEndTime, 
                                  DWORD dwFlags 
                                  ) 
{ 
    UNREFERENCED_PARAMETER(pStartTime); 
    UNREFERENCED_PARAMETER(pEndTime); 
    UNREFERENCED_PARAMETER(dwFlags); 
    CMediaSample *pSample = NULL; 
 
    *ppBuffer = NULL; 
    for (;;) 
    { 
        {  // scope for lock 
            CAutoLock cObjectLock(this); 
 
            /* Check we are committed */ 
            if (!m_bCommitted) { 
                return VFW_E_NOT_COMMITTED; 
            } 
			pSample = (CMediaSample *) m_qWait.RemoveHead();// 
            if (pSample == NULL) { 
				pSample = (CMediaSample *) m_lFree.RemoveHead();// 
				if (pSample == NULL)  
					SetWaiting(); 
            } 
        } 
 
        /* If we didn't get a sample then wait for the list to signal */ 
 
        if (pSample) { 
			 
            break; 
        } 
        if (dwFlags & AM_GBF_NOWAIT) { 
            return VFW_E_TIMEOUT; 
        } 
        ASSERT(m_hSem != NULL); 
        WaitForSingleObject(m_hSem, INFINITE); 
    } 
 
    /* Addref the buffer up to one. On release 
       back to zero instead of being deleted, it will requeue itself by 
       calling the ReleaseBuffer member function. NOTE the owner of a 
       media sample must always be derived from CBaseAllocator */ 
 
 
    ASSERT(pSample->m_cRef == 0 ); 
	pSample->m_cRef = 1;//此时增加引用计数 
    *ppBuffer = pSample; 
 
 
    return NOERROR; 
} 
//用于辅输入 
//从m_lFree中取出,放入m_qWait 
//设置写入标志 
HRESULT CVidMixAllocator::get_Buffer(IMediaSample **ppBuffer, 
                                  REFERENCE_TIME *pStartTime, 
                                  REFERENCE_TIME *pEndTime, 
                                  DWORD dwFlags 
                                  ) 
{ 
    UNREFERENCED_PARAMETER(pStartTime); 
    UNREFERENCED_PARAMETER(pEndTime); 
    UNREFERENCED_PARAMETER(dwFlags); 
    CMediaSample *pSample; 
 
    *ppBuffer = NULL; 
    for (;;) 
    { 
        {  // scope for lock 
            CAutoLock cObjectLock(this); 
 
            /* Check we are committed */ 
            if (!m_bCommitted) { 
                return VFW_E_NOT_COMMITTED; 
            } 
			pSample = (CMediaSample *) m_lFree.RemoveHead();// 
            
			if (pSample) { 
				 
				if ( ((CVidMixMediaSample*)pSample)->m_hEventWriting)//设置写入标志 
						ResetEvent(	((CVidMixMediaSample*)pSample)->m_hEventWriting ); 
				m_qWait.Add((CVidMixMediaSample*)pSample); 
				NotifySample();//大家快抢! 
				break; 
			} 
			else 
			{ 
				SetWaiting(); 
			} 
			 
        } 
 
		 
        /* If we didn't get a sample then wait for the list to signal */ 
 
        
        if (dwFlags & AM_GBF_NOWAIT) { 
            return VFW_E_TIMEOUT; 
        } 
        ASSERT(m_hWaitForFreeBuff != NULL); 
		WaitForSingleObject(m_hWaitForFreeBuff, INFINITE); 
    } 
 
  
 
    ASSERT(pSample->m_cRef == 0); 
 
   // pSample->m_cRef = 1;    不增加引用计数 
    *ppBuffer = pSample; 
 
 
    return NOERROR; 
} 
//清除写入标志 
HRESULT	CVidMixAllocator::release_Buffer(IMediaSample * pSample) 
{ 
    CheckPointer(pSample,E_POINTER); 
    ValidateReadPtr(pSample,sizeof(IMediaSample)); 
 
    CAutoLock cal(this); 
	 
	if ( ((CVidMixMediaSample*)pSample)->m_hEventWriting	){ 
		DWORD dwWaitResult  = WaitForSingleObject(  ((CVidMixMediaSample*)pSample)->m_hEventWriting,  0L);  
			if(dwWaitResult == WAIT_TIMEOUT) 
				SetEvent(	((CVidMixMediaSample*)pSample)->m_hEventWriting ); 
	} 
	 
	 
    return NOERROR; 
} 
STDMETHODIMP CVidMixAllocator::ReleaseBuffer(IMediaSample * pSample) 
{ 
	//通知辅助输入先运行 
	 
    if(m_hWaitForFreeBuff) 
		ReleaseSemaphore(m_hWaitForFreeBuff, m_lFree.GetCount()+1, 0); 
     
 
	return CBaseAllocator::ReleaseBuffer(  pSample); 
} 
void CVidMixAllocator::ReallyFree(void) 
{ 
	 
	 
	/* Should never be deleting this unless all buffers are freed */ 
 
    ASSERT(m_lAllocated == m_lFree.GetCount()); 
 
    /* Free up all the CMediaSamples */ 
 
    CMediaSample *pSample; 
    for (;;) { 
        pSample = m_lFree.RemoveHead(); 
        if (pSample != NULL) { 
			 
 
            delete pSample; 
        } else { 
            break; 
        } 
    } 
 
    m_lAllocated = 0; 
 
    // free the block of buffer memory 
    if (m_pBuffer) { 
        EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE)); 
        m_pBuffer = NULL; 
    } 
 
 
	 
} 
// the following removes a very large number of level 4 warnings from the microsoft 
// compiler output, which are not useful at all in this case. 
#pragma warning(disable:4514)