home *** CD-ROM | disk | FTP | other *** search
- //==========================================================================;
- //
- // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
- // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
- // PURPOSE.
- //
- // Copyright (c) 1992 - 1996 Microsoft Corporation. All Rights Reserved.
- //
- //--------------------------------------------------------------------------;
-
- // Base class for video mixers
-
- #include <streams.h>
- #include <vmbase.h>
-
-
- // =================================================================
- // Implements the CBaseVideoMixer class
- // =================================================================
-
-
- CBaseVideoMixer::CBaseVideoMixer( TCHAR *pName,
- LPUNKNOWN pUnk,
- CLSID clsid,
- HRESULT *phr,
- int iInitialPinCount
- )
- : CBaseFilter(pName, pUnk, &m_csFilter, clsid, phr)
- , m_apInput(NULL)
- , m_pOutput(NULL)
- , m_iLeadPin(0)
- , m_iClockPeriod(1000/25) // 25 frames/second default
- , m_iInputPinCount(iInitialPinCount)
- , m_iInputPinsConnected(0)
- , m_bUsingClock(FALSE) // uses leading pin by default
- , m_pPosition(NULL)
- , m_apOffsets(NULL)
- {
- ASSERT(phr != NULL);
-
- if (*phr == NOERROR)
- *phr = CreatePins();
- }
-
- WCHAR wszPinName[] = L"Input0";
-
- HRESULT CBaseVideoMixer::CreatePins()
- {
- HRESULT hr = NOERROR;
- m_pOutput = new CBaseVideoMixerOutputPin(NAME("VideoMixer output pin"),
- this, // Owner filter
- this, // Route through here
- &hr, // Result code
- L"Output"); // Pin name
-
- if (m_pOutput == NULL)
- hr = E_OUTOFMEMORY;
-
- m_apInput = new CBaseVideoMixerInputPin *[m_iInputPinCount];
-
- if (m_apInput == NULL)
- hr = E_OUTOFMEMORY;
- else {
- for (int i=0; i<m_iInputPinCount; i++) {
- wszPinName[6] = L'0' + i;
- m_apInput[i] = new CBaseVideoMixerInputPin(NAME("Videomixer Input pin"),
- this, // Owner filter
- this, // Route through here
- &hr, // Result code
- wszPinName, // Pin Name
- i); // Pin Number
-
- if (m_apInput[i] == NULL) {
- hr = E_OUTOFMEMORY;
- }
-
- if (FAILED(hr)) {
- break;
- }
- }
- }
-
- return hr;
- }
-
-
- // destructor
-
- CBaseVideoMixer::~CBaseVideoMixer()
- {
- /* Delete the pins */
-
- if (m_apInput) {
-
- for (int i = 0; i < m_iInputPinCount; i++)
- if (m_apInput[i] != NULL)
- delete m_apInput[i];
-
- delete [] m_apInput;
- }
-
- if (m_pOutput)
- delete m_pOutput;
-
- if (m_pPosition != NULL)
- delete m_pPosition;
- }
-
- STDMETHODIMP CBaseVideoMixer::NonDelegatingQueryInterface(REFIID riid, void **ppv)
- {
- CheckPointer(ppv,E_POINTER)
- ValidateReadWritePtr(ppv,sizeof(PVOID));
-
- if (riid == IID_IBaseVideoMixer)
- return GetInterface((IBaseVideoMixer *) this, ppv);
-
- return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
- }
-
-
- // return the number of pins we provide
-
- int CBaseVideoMixer::GetPinCount()
- {
- return m_iInputPinCount + 1;
- }
-
-
- // return a non-addrefed CBasePin *
- CBasePin * CBaseVideoMixer::GetPin(int n)
- {
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::GetPin(%d)"), n));
-
- if (n > m_iInputPinCount) {
- DbgBreak("Bad pin requested");
- return NULL;
- } else if (n == m_iInputPinCount) { // our output pin
- return m_pOutput;
- } else { // we are dealing with an input pin
- return m_apInput[n];
- }
- } // GetPin
-
-
- HRESULT CBaseVideoMixer::StartStreaming()
- {
- DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::StartStreaming()")));
-
- ASSERT(m_apInput != NULL);
-
- // Reset our frame times. m_rtNextFrame will be set correctly
- // by Receive() if we are not using the clock
-
- if (m_pPosition == NULL)
- m_rtThisFrame = 0;
- else{
- REFTIME rt;
- m_pPosition->get_StartTime(&rt);
-
- m_rtThisFrame = (LONGLONG) rt;
- }
-
- if (m_bUsingClock)
- m_rtNextFrame = m_rtThisFrame + (LONG) m_iClockPeriod;
- else
- // This ensures that we don't throw anything away until after we
- // have received the first frame on our lead pin.
- m_rtNextFrame = m_rtThisFrame;
-
- return NOERROR;
- }
-
-
- HRESULT CBaseVideoMixer::StopStreaming()
- {
- // Free any media samples that we are holding on to
- // we need to have been locked for this operation
- // (done by Stop)
-
- CAutoLock waitUntilStoppedSending(&m_csMixLock);
-
- DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::StopStreaming()")));
-
- ReleaseAllQueuedSamples();
-
- return NOERROR;
- }
-
- // override this to grab extra interfaces on connection
- HRESULT CBaseVideoMixer::CheckConnect(int iPin, IPin *pPin)
- {
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::CheckConnect(...)")));
- return NOERROR;
- }
-
-
- // place holder to allow derived classes to release any extra interfaces
- HRESULT CBaseVideoMixer::BreakConnect(int iPin)
- {
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::BreakConnect(...)")));
- return NOERROR;
- }
-
-
- // override this to know when the media type is really set
- HRESULT CBaseVideoMixer::SetMediaType(int iPin, const CMediaType *pmt)
- {
- DisplayType("SetMediaType", pmt);
-
- // then check with the other connected inputs to see if
- // they like this format as well
- for (int i = 0; i < m_iInputPinCount; i++) {
- if ((i != iPin) && m_apInput[i]->IsConnected()) {
- if (!AreEqualVideoTypes(pmt, &m_apInput[i]->CurrentMediaType())) {
- DbgLog((LOG_TRACE, 2, TEXT("Stream %d doesn't match, reconnecting"), i));
- m_pGraph->Reconnect(m_apInput[i]);
- }
- }
- }
-
- if ((i != m_iInputPinCount) && m_pOutput->IsConnected()) {
- if (!AreEqualVideoTypes(pmt, &m_pOutput->CurrentMediaType())) {
- DbgLog((LOG_TRACE, 2, TEXT("Output doesn't match, reconnecting")));
- m_pGraph->Reconnect(m_pOutput);
- }
- }
-
- return NOERROR;
- }
-
- HRESULT CBaseVideoMixer::Receive(void)
- {
- IMediaSample *pSampleOut;
-
- // We need to block out some changes while we are attempting to mix
- // the samples. I.e. no old samples leaving, no pins being connected
- // or disconnected, no changes to variables such as m_iLeadPin, etc.
- // It is OK to accept new samples while mixing (as the will go to the back of
- // a queue) and it is OK to change our state to paused.
- CAutoLock lock(&m_csMixLock);
-
- while (TRUE) {
- if (!m_bUsingClock) {
- // We may need to deliver more than one sample for every receive,
- // so we loop while we have a sample on our lead pin.
- if (!m_apInput[m_iLeadPin]->SampleReady())
- break;
-
- // Release all samples which stop before our lead frame stops
- m_apInput[m_iLeadPin]->GetHeadStopTime(&m_rtNextFrame);
-
- DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::Receive resetting next frame time to %s"),
- (LPCTSTR)CDisp(m_rtNextFrame)));
- }
-
- for (int i=0; i < m_iInputPinCount; i++)
- m_apInput[i]->ReleaseAllBefore(m_rtNextFrame);
-
- // See if we can mix the samples
- if (SUCCEEDED(MixAndOutputSamples(&pSampleOut))) {
- DbgLog((LOG_TRACE, 6, TEXT(" -- got NOERROR ")));
-
- // Mix went OK. If we are using the clock then increment our
- // clock time. If we are not using the clock then release the
- // sample on our lead pin
- m_rtThisFrame = m_rtNextFrame;
- if (m_bUsingClock)
- m_rtNextFrame += (LONG) m_iClockPeriod;
- else
- m_apInput[m_iLeadPin]->ReleaseHeadSample();
-
- // The video mixer may well hold on to our thread when
- // we deliver the sample. So we remove our lock in order to allow
- // state changes whilst delivering.
- m_csMixLock.Unlock();
-
- HRESULT hr = m_pOutput->Deliver(pSampleOut);
- pSampleOut->Release();
-
- m_csMixLock.Lock();
-
- if (hr != NOERROR) {
- DbgLog((LOG_TRACE, 4, TEXT(" -- got error %x from Deliver"), hr));
- break;
- }
- } else {
- DbgLog((LOG_TRACE, 6, TEXT(" -- got error from MixAndOutput")));
- // must be missing a sample, so stop
- break;
- }
-
- }
-
- BOOL fAtEOS = TRUE;
- if (!m_bUsingClock) {
- fAtEOS = m_apInput[m_iLeadPin]->m_fEOSReceived;
- } else {
- for (int i=0; i < m_iInputPinCount; i++) {
- if (!m_apInput[i]->m_fEOSReceived) {
- fAtEOS = FALSE;
- break;
- }
- }
- }
-
- if (fAtEOS) {
- DbgLog((LOG_TRACE, 1, TEXT("Sending EOS")));
- m_pOutput->DeliverEndOfStream();
- }
-
- DbgLog((LOG_TRACE, 4, TEXT(" leaving Receive")));
-
- return NOERROR;
- }
-
- HRESULT CBaseVideoMixer::MixAndOutputSamples(IMediaSample **ppOut)
- {
- HRESULT hr;
-
- *ppOut = NULL;
-
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::MixAndOutputSamples(...)")));
-
- // Check that all of the expected samples are in
- for (int i =0; i<m_iInputPinCount; i++) {
- if (m_apInput[i] == NULL ||
- (!m_apInput[i]->SampleReady() &&
- !m_apInput[i]->m_fEOSReceived)) {
- DbgLog((LOG_TRACE, 5, TEXT(" -- pin %d is not ready"), i));
- return E_FAIL;
- }
- }
-
- IMediaSample *pOutSample;
-
- DbgLog((LOG_TRACE, 8, TEXT(" -- getting output buffer ")));
- hr = m_pOutput->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);
-
- if (FAILED(hr)) {
- DbgLog((LOG_TRACE, 4, TEXT(" -- failed to get output buffer ")));
- return hr;
- }
-
- ASSERT(pOutSample != NULL);
- DbgLog((LOG_TRACE, 8, TEXT(" -- got output buffer ")));
-
- // Set the properties
- pOutSample->SetTime((REFERENCE_TIME*)&m_rtThisFrame, (REFERENCE_TIME*)&m_rtNextFrame);
-
- // we're always outputting uncompressed RGB.
- pOutSample->SetSyncPoint(TRUE);
- pOutSample->SetDiscontinuity(FALSE);
-
- // have the derived class mix the data
- DbgLog((LOG_TRACE, 8, TEXT(" -- mixing...")));
- hr = MixSamples(pOutSample);
-
-
- if (hr != NOERROR)
- pOutSample->Release();
- else
- *ppOut = pOutSample;
-
- return hr;
-
- }
-
- // ReleaseAllQueuedSamples
- // - release all samples which are held on our input pins
- HRESULT CBaseVideoMixer::ReleaseAllQueuedSamples(void)
- {
- // Calls ReleaseHeadSample (as opposed to m_SampleList.RemoveAll)
- // to ensure that we actually release the sample
- for (int i = 0; i < m_iInputPinCount; i ++)
- while (m_apInput[i]->SampleReady())
- m_apInput[i]->ReleaseHeadSample();
-
- return NOERROR;
- }
-
- HRESULT CBaseVideoMixer::HandleLeadingPinStopping(void)
- {
- {
- // protect change m_iLeadPin;
- CAutoLock lock(&m_csMixLock);
-
- // switch leading pin to next active stream
- for(int iStream = m_iLeadPin; iStream < m_iInputPinCount; iStream++)
- {
- if(!m_apInput[iStream]->m_fEOSReceived)
- {
- m_iLeadPin = iStream;
- break;
- }
- }
- }
-
- return this->Receive();
- }
-
-
- // enter flush state. Receives already blocked
- // must override this if you have queued data or a worker thread
- HRESULT CBaseVideoMixer::BeginFlush(void)
- {
- // check we are able to receive commands
-
- HRESULT hr = CanChangeState();
- if (FAILED(hr)) {
- return hr;
- }
-
- // block receives -- done by caller (CBaseInputPin::BeginFlush)
-
- // discard queued data
-
- // - release all of our queued input data
- // (we don't queue output data)
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::BeginFlush()")));
-
- ReleaseAllQueuedSamples();
- // free anyone blocked on receive - not possible in this filter
-
- // call downstream
- return m_pOutput->DeliverBeginFlush();
- }
-
- // leave flush state. must override this if you have queued data
- // or a worker thread
- HRESULT CBaseVideoMixer::EndFlush(void)
- {
- // check we are able to receive commands
-
- HRESULT hr = CanChangeState();
- if (FAILED(hr)) {
- return hr;
- }
-
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::EndFlush()")));
- // sync with pushing thread -- we have no worker thread
-
- // reset m_iLeadPin in case it changed
- m_iLeadPin = 0;
-
-
- // Reset our frame times. m_rtNextFrame will be set correctly
- // by Receive() if we are not using the clock
-
- if (m_pPosition == NULL)
- m_rtThisFrame = 0;
- else{
- REFTIME rt;
- m_pPosition->get_StartTime(&rt);
-
- m_rtThisFrame = (LONGLONG) rt;
- }
-
- if (m_bUsingClock)
- m_rtNextFrame = m_rtThisFrame + (LONG) m_iClockPeriod;
- else
- // This ensures that we don't throw anything away until after we
- // have received the first frame on our lead pin.
- m_rtNextFrame = m_rtThisFrame;
-
- DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::EndFlush resetting next frame time to %s"),
- (LPCTSTR)CDisp(m_rtNextFrame)));
-
- // caller (the input pin's method) will unblock Receives
-
- // call EndFlush on downstream pins
- return m_pOutput->DeliverEndFlush();
-
- }
-
-
-
- // check we are in a position to change state
- HRESULT CBaseVideoMixer::CanChangeState()
- {
- // check we have a valid input connection(s)
-
- // we don't lock. If the caller requires the state not to change
- // after the check then they must provide the lock
-
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::CanChangeState(...)")));
-
- for (int i = 0; i < m_iInputPinCount; i++)
- if (m_apInput[i] == NULL || !m_apInput[i]->m_mt.IsValid())
- return E_FAIL;
-
- // check we have a valid output connection
- if (!m_pOutput->m_mt.IsValid())
- return E_FAIL;
-
- return NOERROR;
- }
-
-
- // override these so that the derived filter can catch them
-
- STDMETHODIMP CBaseVideoMixer::Stop()
- {
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::Stop(...)")));
- CAutoLock l(&m_csFilter);
-
- // Is there any change needed
- if (m_State == State_Stopped) {
- return NOERROR;
- }
-
- // check we can change state
-
- HRESULT hr = CanChangeState();
- if (FAILED(hr)) {
- return hr;
- }
-
- // allow a class derived from CBaseVideoMixer
- // to know about starting and stopping streaming
- hr = StopStreaming();
- if (FAILED(hr)) {
- return hr;
- }
-
- // reset m_iLeadPin in case it changed
- m_iLeadPin = 0;
-
- // do the state transition
- return CBaseFilter::Stop();
- }
-
-
- STDMETHODIMP CBaseVideoMixer::Pause()
- {
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::Pause(...)")));
- CAutoLock l(&m_csFilter);
-
- // Is there any change needed
- if (m_State == State_Paused) {
- return NOERROR;
- }
-
- // check we can change state
-
- HRESULT hr = CanChangeState();
- if (FAILED(hr)) {
- if (m_pOutput) {
- m_pOutput->DeliverEndOfStream();
- }
- return hr;
- }
-
- // allow a class derived from CBaseVideoMixer
- // to know about starting and stopping streaming
-
- if (m_State == State_Stopped) {
- hr = StartStreaming();
- if (FAILED(hr)) {
- return hr;
- }
- }
- return CBaseFilter::Pause();
- }
-
-
- STDMETHODIMP CBaseVideoMixer::Run(REFERENCE_TIME tStart)
- {
- DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::Run(...)")));
- CAutoLock l(&m_csFilter);
-
- // Is there any change needed
- if (m_State == State_Running) {
- return NOERROR;
- }
-
- HRESULT hr = CanChangeState();
- if (FAILED(hr)) {
- return hr;
- }
-
- // This will call CBaseVideoMixer::Pause if necessary, so we don't
- // need to call StartStreaming here.
-
- return CBaseFilter::Run(tStart);
- }
-
- // check that we can support a given media type
- HRESULT CBaseVideoMixer::CheckMediaType(int iPin, const CMediaType* pmt)
- {
- DisplayType("CheckMediaType", pmt);
-
- // ask the derived class
- HRESULT hr = CanMixType(pmt);
- if (FAILED(hr)) {
- DbgLog((LOG_TRACE, 2, TEXT("Mixer doesn't like this format")));
-
- return hr;
- }
-
- // ask the filters connected to our inputs
- for (int i = 0; i < m_iInputPinCount; i++) {
- if ((i != iPin) && m_apInput[i]->IsConnected()) {
- hr = m_apInput[i]->CurrentPeer()->QueryAccept(pmt);
- if (hr != S_OK) {
- DbgLog((LOG_TRACE, 2, TEXT("Input %d doesn't like this format"), i));
- hr = E_FAIL;
-
- return hr;
- }
- }
- }
-
- // ask the filters connected to our outputs
- if ((m_iInputPinCount != iPin) && m_pOutput->IsConnected()) {
- hr = m_pOutput->CurrentPeer()->QueryAccept(pmt);
- if (hr != S_OK) {
- DbgLog((LOG_TRACE, 2, TEXT("Output doesn't like this format")));
- hr = E_FAIL;
-
- return hr;
- }
- }
-
- return hr;
- }
-
-
-
- // =================================================================
- // Implements the CBaseVideoMixerInputPin class
- // =================================================================
-
-
- // constructor
-
- CBaseVideoMixerInputPin::CBaseVideoMixerInputPin(
- TCHAR *pObjectName,
- CBaseFilter *pBaseFilter,
- CBaseVideoMixer *pBaseVideoMixer,
- HRESULT * phr,
- LPCWSTR pName,
- int iPinNo)
- : CBaseInputPin(pObjectName, pBaseFilter, &pBaseVideoMixer->m_csFilter, phr, pName)
- , m_SampleList(NAME("CBaseVideoMixerInpuPin::m_SampleList"))
- , m_iPinNo(iPinNo)
- , m_pBaseVideoMixer(pBaseVideoMixer)
- {
- DbgLog((LOG_TRACE,4,TEXT("CBaseVideoMixerInputPin::CBaseVideoMixerInputPin")));
- }
-
-
- // destructor
-
- CBaseVideoMixerInputPin::~CBaseVideoMixerInputPin()
- {
- DbgLog((LOG_TRACE,4,TEXT("CBaseVideoMixerInputPin::~CBaseVideoMixerInputPin")));
- }
-
- IMediaSample *CBaseVideoMixerInputPin::GetHeadSample(void)
- {
- ASSERT(m_SampleList.GetCount() != 0);
- return m_SampleList.Get(m_SampleList.GetHeadPosition());
- }
-
- void CBaseVideoMixerInputPin::ReleaseHeadSample(void)
- {
- ASSERT(m_SampleList.GetCount() != 0);
-
- DbgLog((LOG_TRACE, 4, TEXT("CBaseVideoMixerInputPin::ReleaseHeadSample() on pin %d"), m_iPinNo));
-
- m_SampleList.Get(m_SampleList.GetHeadPosition())->Release();
- m_SampleList.RemoveHead();
- }
-
- void CBaseVideoMixerInputPin::ReleaseAllBefore(CRefTime rtTime)
- {
- CRefTime rtStop;
- DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoMixerInputPin::ReleaseAllBefore(%s) on pin %d"),
- (LPCTSTR)CDisp(rtTime), m_iPinNo));
-
- while (SampleReady()) {
- GetHeadStopTime(&rtStop);
-
- if (rtStop < rtTime)
- ReleaseHeadSample();
- else
- return;
- }
-
- }
-
- void CBaseVideoMixerInputPin::GetHeadStopTime(CRefTime *prt)
- {
- ASSERT(m_SampleList.GetCount() != 0);
-
- CRefTime junk;
- m_SampleList.Get(m_SampleList.GetHeadPosition())->GetTime(
- (REFERENCE_TIME*)&junk,
- (REFERENCE_TIME*)prt);
-
- if (m_pBaseVideoMixer->m_apOffsets)
- *prt += m_pBaseVideoMixer->m_apOffsets[m_iPinNo];
-
- }
-
-
- BOOL CBaseVideoMixerInputPin::SampleReady(void)
- {
- return m_SampleList.GetCount() != 0;
- }
-
-
- // override this just to hold the critsec
-
- STDMETHODIMP CBaseVideoMixerInputPin::Disconnect(void)
- {
- CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
-
- m_pBaseVideoMixer->m_iInputPinsConnected--;
-
- return CBaseInputPin::Disconnect();
- }
-
-
- // provides derived filter a chance to grab extra interfaces
-
- HRESULT CBaseVideoMixerInputPin::CheckConnect(IPin *pPin)
- {
- if (pPin == m_pBaseVideoMixer->m_pOutput) {
- DbgLog((LOG_TRACE, 2, TEXT("Tried to connect input #%d to output"), m_iPinNo));
- return E_FAIL;
- }
-
- HRESULT hr = m_pBaseVideoMixer->CheckConnect(m_iPinNo, pPin);
- if (FAILED(hr)) {
- return hr;
- }
- return CBaseInputPin::CheckConnect(pPin);
- }
-
-
- // provides derived filter a chance to release it's extra interfaces
-
- HRESULT
- CBaseVideoMixerInputPin::BreakConnect()
- {
- m_pBaseVideoMixer->BreakConnect(m_iPinNo);
- m_mt.SetType(&GUID_NULL);
-
- return CBaseInputPin::BreakConnect();
- }
-
-
- // check that we can support a given media type
- HRESULT CBaseVideoMixerInputPin::CheckMediaType(const CMediaType* pmt)
- {
- return m_pBaseVideoMixer->CheckMediaType(m_iPinNo, pmt);
- }
-
-
- // set the media type for this connection
- HRESULT CBaseVideoMixerInputPin::SetMediaType(const CMediaType* mtIn)
- {
- CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
-
- // Set the base class media type (should always succeed)
- HRESULT hr = CBasePin::SetMediaType(mtIn);
- ASSERT(SUCCEEDED(hr));
-
- // check the transform can be done (should always succeed)
- ASSERT(SUCCEEDED(m_pBaseVideoMixer->CheckMediaType(m_iPinNo, mtIn)));
-
- m_mt = *mtIn;
- m_pBaseVideoMixer->SetMediaType(m_iPinNo, mtIn);
-
- m_pBaseVideoMixer->m_iInputPinsConnected++;
-
- return NOERROR;
- }
-
-
- HRESULT CBaseVideoMixerInputPin::Active()
- {
- m_fEOSReceived = FALSE;
-
- return NOERROR;
- }
-
- // =================================================================
- // Implements IMemInputPin interface
- // =================================================================
-
-
- // 'queue' the EOS after all received data
- STDMETHODIMP CBaseVideoMixerInputPin::EndOfStream(void)
- {
- m_fEOSReceived = TRUE;
-
- int iLeadPin;
- HRESULT hr = m_pBaseVideoMixer->GetLeadPin(&iLeadPin);
- if(FAILED(hr))
- return hr;
-
- if(m_iPinNo == iLeadPin);
- {
- hr = m_pBaseVideoMixer->HandleLeadingPinStopping();
- if(FAILED(hr))
- return hr;
- }
-
- return m_pBaseVideoMixer->Receive();
- }
-
- // enter flushing state. Call default handler to block Receives, then
- // pass to overridable method in filter
- STDMETHODIMP CBaseVideoMixerInputPin::BeginFlush(void)
- {
- HRESULT hr = CBaseInputPin::BeginFlush();
- if (FAILED(hr)) {
- return hr;
- }
-
- return m_pBaseVideoMixer->BeginFlush();
- }
-
- // leave flushing state.
- // Pass to overridable method in filter, then call base class
- // to unblock receives (finally)
- STDMETHODIMP CBaseVideoMixerInputPin::EndFlush(void)
- {
- HRESULT hr = m_pBaseVideoMixer->EndFlush();
- if (FAILED(hr)) {
- return hr;
- }
-
- m_fEOSReceived = FALSE; // correct???
-
- return CBaseInputPin::EndFlush();
- }
-
-
- // The next block of data has been received from upstream
- HRESULT CBaseVideoMixerInputPin::Receive(IMediaSample * pSample)
- {
- ASSERT(pSample);
- HRESULT hr;
-
- DbgLog((LOG_TRACE, 4, TEXT("CBaseVideoMixerInputPin::Receive(..) on pin %d"), m_iPinNo));
-
- if (m_pBaseVideoMixer->m_iInputPinsConnected != m_pBaseVideoMixer->m_iInputPinCount) {
- DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixerInputPin::Receive() without all inputs connected!")));
- return S_FALSE;
- }
-
- // check all is well with the base class
- hr = CBaseInputPin::Receive(pSample);
- if (FAILED(hr)) {
- return hr;
- }
-
- if (m_pBaseVideoMixer->m_apLengths) {
- CRefTime tStart, tStop;
- pSample->GetTime((REFERENCE_TIME*)&tStart, (REFERENCE_TIME*)&tStop);
-
- if (tStart > m_pBaseVideoMixer->m_apLengths[m_iPinNo]) {
- DbgLog((LOG_TRACE, 2, TEXT("Past stopping time for pin %d"), m_iPinNo));
- m_fEOSReceived = TRUE;
-
- int iLeadPin;
- hr = m_pBaseVideoMixer->GetLeadPin(&iLeadPin);
- if(FAILED(hr))
- return hr;
-
- if(m_iPinNo == iLeadPin);
- {
- hr = m_pBaseVideoMixer->HandleLeadingPinStopping();
- if(FAILED(hr))
- return hr;
- }
- return S_FALSE;
- }
- }
-
- // If a graph is stopped and a late sample comes along,
- // then we need to reject the sample. If we don't, we'll end up
- // with a sample with a late time stamp hanging around in our
- // buffers, and that will mess up the algorithm in MixAndOutputSamples
- if (m_pBaseVideoMixer->m_State == State_Stopped) {
- DbgLog((LOG_ERROR, 1, TEXT("Receive while stopped!")));
- return VFW_E_WRONG_STATE;
- }
-
- // Keep this sample and add it to the sample list
- pSample->AddRef(); // keep new one
- m_SampleList.AddTail(pSample);
-
- return m_pBaseVideoMixer->Receive();
- }
-
-
- // Pass on the Quality notification q to
- // a. Our QualityControl sink (if we have one) or else
- // b. to our upstream filter or else
- // c. the filter graph
- // and if that doesn't work, throw it away with a bad return code
- HRESULT CBaseVideoMixerInputPin::PassNotify(Quality q)
- {
- // We pass the message on, which means that we find the quality sink
- // for our input pin and send it there
-
- DbgLog((LOG_TRACE,4,TEXT("Passing Quality notification through VideoMixer")));
-
- if (m_pQSink!=NULL) {
- return m_pQSink->Notify(m_pBaseVideoMixer, q);
- } else {
-
- // no sink set, so pass it upstream
- HRESULT hr;
- IQualityControl * pIQC;
-
- hr = E_FAIL; // default
- if (m_Connected) {
- m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
-
- if (pIQC!=NULL) {
- hr = pIQC->Notify(m_pBaseVideoMixer, q);
- pIQC->Release();
- }
- }
-
- return hr;
- }
-
- } // PassNotify
-
-
-
- STDMETHODIMP CBaseVideoMixerInputPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
- {
- CheckPointer(ppEnum,E_POINTER)
- ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
-
- // find the first connected input pin and use its enumeration.
- // of course, it may enumerate formats which other inputs can't support,
- // but we'll catch that in CheckMediaType.
- for (int i = 0; i < m_pBaseVideoMixer->m_iInputPinCount; i++) {
- if (i != m_iPinNo && m_pBaseVideoMixer->m_apInput[i]->IsConnected()) {
- return m_pBaseVideoMixer->m_apInput[i]->CurrentPeer()->
- EnumMediaTypes(ppEnum);
- }
- }
-
- // no other pins connected?
- return E_UNEXPECTED;
- }
-
-
-
-
-
- // =================================================================
- // Implements the CBaseVideoMixerOutputPin class
- // =================================================================
-
-
- // constructor
-
- CBaseVideoMixerOutputPin::CBaseVideoMixerOutputPin(
- TCHAR *pObjectName,
- CBaseFilter *pBaseFilter,
- CBaseVideoMixer *pBaseVideoMixer,
- HRESULT * phr,
- LPCWSTR pPinName)
- : CBaseOutputPin(pObjectName, pBaseFilter, &pBaseVideoMixer->m_csFilter, phr, pPinName),
- m_iOutputPin(pBaseVideoMixer->m_iInputPinCount)
- {
- DbgLog((LOG_TRACE,2,TEXT("CBaseVideoMixerOutputPin::CBaseVideoMixerOutputPin")));
-
- m_pBaseVideoMixer = pBaseVideoMixer;
-
- }
-
-
- // destructor
-
- CBaseVideoMixerOutputPin::~CBaseVideoMixerOutputPin()
- {
- DbgLog((LOG_TRACE,2,TEXT("CBaseVideoMixerOutputPin::~CBaseVideoMixerOutputPin")));
-
- }
-
-
- // overriden to expose IMediaPosition control interface
- STDMETHODIMP CBaseVideoMixerOutputPin::NonDelegatingQueryInterface(REFIID riid,
- void **ppv)
- {
- CheckPointer(ppv,E_POINTER)
- ValidateReadWritePtr(ppv,sizeof(PVOID));
- *ppv = NULL;
-
- if (riid == IID_IMediaPosition) {
- if (m_pBaseVideoMixer->m_pPosition == NULL) {
-
- HRESULT hr = S_OK;
- m_pBaseVideoMixer->m_pPosition = new CMultiPinPosPassThru(
- NAME("CBaseVideoMixer::m_pPosition")
- , GetOwner()
- , &hr
- );
-
- if (m_pBaseVideoMixer->m_pPosition == NULL)
- return E_OUTOFMEMORY;
-
- if (FAILED(hr)) {
- delete m_pBaseVideoMixer->m_pPosition;
- m_pBaseVideoMixer->m_pPosition = NULL;
- return hr;
- }
-
- m_pBaseVideoMixer->m_pPosition->SetPins(
- (CBasePin **) m_pBaseVideoMixer->m_apInput
- , m_pBaseVideoMixer->m_apOffsets
- , m_pBaseVideoMixer->m_iInputPinCount
- );
- }
- return m_pBaseVideoMixer->m_pPosition->NonDelegatingQueryInterface(riid, ppv);
-
- } else
- return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
-
- }
-
-
- // override this just to hold the critsec
-
- STDMETHODIMP CBaseVideoMixerOutputPin::Disconnect(void)
- {
- CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
- return CBaseOutputPin::Disconnect();
- }
-
-
- // provides derived filter a chance to grab extra interfaces
-
- HRESULT CBaseVideoMixerOutputPin::CheckConnect(IPin *pPin)
- {
- for (int i = 0; i < m_iOutputPin; i++) {
- if (pPin == m_pBaseVideoMixer->m_apInput[i]) {
- DbgLog((LOG_TRACE, 2, TEXT("Tried to connect output to input #%d"), i));
- return E_FAIL;
- }
- }
-
- HRESULT hr = m_pBaseVideoMixer->CheckConnect(m_iOutputPin, pPin);
- if (FAILED(hr))
- return hr;
-
- return CBaseOutputPin::CheckConnect(pPin);
- }
-
-
- // provides derived filter a chance to release it's extra interfaces
-
- HRESULT CBaseVideoMixerOutputPin::BreakConnect()
- {
- m_pBaseVideoMixer->BreakConnect(m_iOutputPin);
- m_mt.SetType(&GUID_NULL);
-
- return CBaseOutputPin::BreakConnect();
- }
-
-
- STDMETHODIMP CBaseVideoMixerOutputPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
- {
- CheckPointer(ppEnum,E_POINTER)
- ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
-
- // find the first connected input pin and use its enumeration.
- // of course, it may enumerate formats which other inputs can't support,
- // but we'll catch that in CheckMediaType.
- for (int i = 0; i < m_pBaseVideoMixer->m_iInputPinCount; i++) {
- if (m_pBaseVideoMixer->m_apInput[i]->IsConnected()) {
- return m_pBaseVideoMixer->m_apInput[i]->CurrentPeer()->
- EnumMediaTypes(ppEnum);
- }
- }
-
- // no pins connected?
- return E_UNEXPECTED;
- }
-
-
- // check a given transform - must have selected input type first
-
- HRESULT CBaseVideoMixerOutputPin::CheckMediaType(const CMediaType* pmtOut)
- {
- return m_pBaseVideoMixer->CheckMediaType(m_iOutputPin, pmtOut);
- }
-
-
- // called after we have agreed a media type to actually set it in which case
- // we run the CheckTransform function to get the output format type again
-
- HRESULT CBaseVideoMixerOutputPin::SetMediaType(const CMediaType* pmtOut)
- {
- HRESULT hr = NOERROR;
-
- // Set the base class media type (should always succeed)
- hr = CBasePin::SetMediaType(pmtOut);
- ASSERT(SUCCEEDED(hr));
-
- // Check this transform can be done (should always succeed)
- ASSERT(SUCCEEDED(m_pBaseVideoMixer->CheckMediaType(m_iOutputPin, pmtOut)));
-
- m_mt = *pmtOut;
- m_pBaseVideoMixer->SetMediaType(m_iOutputPin, pmtOut);
-
- return NOERROR;
- }
-
-
- // set the buffer size according to the current format.
-
- HRESULT
- CBaseVideoMixerOutputPin::DecideBufferSize(
- IMemAllocator * pAllocator,
- ALLOCATOR_PROPERTIES * pProp)
- {
- if (!IsConnected()) {
- DbgBreak("DecideBufferSize called when !m_pOutput->IsConnected()");
- return VFW_E_NOT_CONNECTED;
- } else {
- // I don't think we actually care about alignment here,
- // so leave it untouched
-
- pProp->cBuffers = 1;
- pProp->cbBuffer = CurrentMediaType().GetSampleSize();
- ALLOCATOR_PROPERTIES propActual;
-
- HRESULT hr = pAllocator->SetProperties(pProp, &propActual);
- if (FAILED(hr)) {
- return hr;
- }
-
- if ((pProp->cBuffers > propActual.cBuffers)
- || (pProp->cbBuffer > propActual.cbBuffer)
- ) {
- return E_FAIL;
- }
- }
-
- return NOERROR;
- }
-
-
- // Override this if you can do something constructive to act on the
- // quality message.
- //
- // Pass the quality messages on upstream.
- STDMETHODIMP CBaseVideoMixerOutputPin::Notify(IFilter * pSender, Quality q)
- {
- CheckPointer(pSender,E_POINTER)
- ValidateReadPtr(pSender,sizeof(IFilter));
- CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
-
- if (m_pBaseVideoMixer->m_apInput==NULL) {
- // why would this ever be??
- DbgBreak("m_pInput is NULL");
- return E_FAIL;
- }
-
- // If we are running late then jump ahead
- if (q.Late > 0) {
- m_pBaseVideoMixer->m_rtNextFrame += q.Late;
- m_pBaseVideoMixer->m_rtThisFrame += q.Late;
- }
-
- // Send the quality message to all of our input pins
- for (int i=0; i<m_pBaseVideoMixer->m_iInputPinCount; i++)
- if (m_pBaseVideoMixer->m_apInput[i] != NULL) {
- HRESULT hr = m_pBaseVideoMixer->m_apInput[i]->PassNotify(q);
- if (FAILED(hr))
- return hr;
- }
-
- return NOERROR;
-
- } // Notify
-
-
- BOOL AreEqualVideoTypes( const CMediaType *pmt1, const CMediaType *pmt2 )
- {
- // The standard implementation is too strict - it demands an exact match
- // We just want to know is they are the same format and have the same
- // width / height
-
- ASSERT( IsEqualGUID( *pmt1->Type(), MEDIATYPE_Video ) );
- ASSERT( IsEqualGUID( *pmt2->Type(), MEDIATYPE_Video ) );
- ASSERT( *pmt1->FormatType() == FORMAT_VideoInfo );
- ASSERT( *pmt2->FormatType() == FORMAT_VideoInfo );
-
- VIDEOINFO *pvi1 = (VIDEOINFO *) pmt1->Format();
- VIDEOINFO *pvi2 = (VIDEOINFO *) pmt2->Format();
-
- return IsEqualGUID( *pmt1->Subtype(), *pmt2->Subtype() )
- && pvi1->bmiHeader.biWidth == pvi2->bmiHeader.biWidth
- && pvi2->bmiHeader.biHeight == pvi2->bmiHeader.biHeight;
- }
-
-