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;i IsConnected() == TRUE){ bAnyInConnect = TRUE; break; } } return bAnyInConnect; } CVidMixFilter::~CVidMixFilter(void) { if(m_pInput) delete m_pInput; for(int i=0;i IsConnected() ) // count++; //} //count ++; //if(count>MAX_INPUTPIN_COUNT)count = MAX_INPUTPIN_COUNT; // //return count +1;//inputpins + outpinps; //if(bNoUnconnect) //{ // for(int i=0;i AddRef(); // break; // } // } // //} //int count = 0; //for(int i=0;i AddRef(); } } 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;i Name())){ *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;i IsConnected()) 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;i bmiHeader.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;i bmiHeader.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;i Inactive() ; } // 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)