home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / vmbase.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-19  |  33.5 KB  |  1,212 lines

  1. //==========================================================================;
  2. //
  3. //  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4. //  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5. //  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6. //  PURPOSE.
  7. //
  8. //  Copyright (c) 1992 - 1996  Microsoft Corporation.  All Rights Reserved.
  9. //
  10. //--------------------------------------------------------------------------;
  11.  
  12. // Base class for video mixers
  13.  
  14. #include <streams.h>
  15. #include <vmbase.h>
  16.  
  17.  
  18. // =================================================================
  19. // Implements the CBaseVideoMixer class
  20. // =================================================================
  21.  
  22.  
  23. CBaseVideoMixer::CBaseVideoMixer( TCHAR     *pName,
  24.                                    LPUNKNOWN pUnk,
  25.                                    CLSID     clsid,
  26.                                    HRESULT   *phr,
  27.                                    int iInitialPinCount
  28.                                 )
  29.     : CBaseFilter(pName, pUnk, &m_csFilter, clsid, phr)
  30.     , m_apInput(NULL)
  31.     , m_pOutput(NULL)
  32.     , m_iLeadPin(0)
  33.     , m_iClockPeriod(1000/25)         // 25 frames/second default
  34.     , m_iInputPinCount(iInitialPinCount)
  35.     , m_iInputPinsConnected(0)
  36.     , m_bUsingClock(FALSE)            // uses leading pin by default
  37.     , m_pPosition(NULL)
  38.     , m_apOffsets(NULL)
  39. {
  40.     ASSERT(phr != NULL);
  41.  
  42.     if (*phr == NOERROR)
  43.     *phr = CreatePins();
  44. }
  45.  
  46. WCHAR wszPinName[] = L"Input0";
  47.  
  48. HRESULT CBaseVideoMixer::CreatePins()
  49. {
  50.     HRESULT hr = NOERROR;
  51.     m_pOutput = new CBaseVideoMixerOutputPin(NAME("VideoMixer output pin"),
  52.                          this,          // Owner filter
  53.                          this,          // Route through here
  54.                          &hr,           // Result code
  55.                          L"Output");    // Pin name
  56.  
  57.     if (m_pOutput == NULL)
  58.     hr = E_OUTOFMEMORY;
  59.  
  60.     m_apInput = new CBaseVideoMixerInputPin *[m_iInputPinCount];
  61.  
  62.     if (m_apInput ==  NULL)
  63.     hr = E_OUTOFMEMORY;
  64.     else {
  65.     for (int i=0; i<m_iInputPinCount; i++) {
  66.             wszPinName[6] = L'0' + i;
  67.         m_apInput[i] = new CBaseVideoMixerInputPin(NAME("Videomixer Input pin"),
  68.                                                 this,       // Owner filter
  69.                                                 this,       // Route through here
  70.                                                 &hr,        // Result code
  71.                                                 wszPinName, // Pin Name
  72.                                                 i);         // Pin Number
  73.  
  74.         if (m_apInput[i] == NULL) {
  75.         hr = E_OUTOFMEMORY;
  76.         }
  77.  
  78.         if (FAILED(hr)) {
  79.         break;
  80.         }
  81.         }
  82.     }
  83.  
  84.     return hr;
  85. }
  86.  
  87.  
  88. // destructor
  89.  
  90. CBaseVideoMixer::~CBaseVideoMixer()
  91. {
  92.     /* Delete the pins */
  93.  
  94.     if (m_apInput) {
  95.  
  96.         for (int i = 0; i < m_iInputPinCount; i++)
  97.             if (m_apInput[i] != NULL)
  98.                 delete m_apInput[i];
  99.  
  100.         delete [] m_apInput;
  101.     }
  102.  
  103.     if (m_pOutput)
  104.         delete m_pOutput;
  105.  
  106.     if (m_pPosition != NULL)
  107.         delete m_pPosition;
  108. }
  109.  
  110. STDMETHODIMP CBaseVideoMixer::NonDelegatingQueryInterface(REFIID riid, void **ppv)
  111. {
  112.     CheckPointer(ppv,E_POINTER)
  113.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  114.  
  115.     if (riid == IID_IBaseVideoMixer)
  116.         return GetInterface((IBaseVideoMixer *) this, ppv);
  117.  
  118.     return CBaseFilter::NonDelegatingQueryInterface(riid, ppv);
  119. }
  120.  
  121.  
  122. // return the number of pins we provide
  123.  
  124. int CBaseVideoMixer::GetPinCount()
  125. {
  126.     return m_iInputPinCount + 1;
  127. }
  128.  
  129.  
  130. // return a non-addrefed CBasePin *
  131. CBasePin * CBaseVideoMixer::GetPin(int n)
  132. {
  133.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::GetPin(%d)"), n));
  134.  
  135.     if (n > m_iInputPinCount) {
  136.     DbgBreak("Bad pin requested");
  137.     return NULL;
  138.     } else if (n == m_iInputPinCount) { // our output pin
  139.     return m_pOutput;
  140.     } else {                            // we are dealing with an input pin
  141.         return m_apInput[n];
  142.     }
  143. } // GetPin
  144.  
  145.  
  146. HRESULT CBaseVideoMixer::StartStreaming()
  147. {
  148.     DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::StartStreaming()")));
  149.  
  150.     ASSERT(m_apInput != NULL);
  151.  
  152.     // Reset our frame times. m_rtNextFrame will be set correctly
  153.     // by Receive() if we are not using the clock
  154.  
  155.     if (m_pPosition == NULL)
  156.         m_rtThisFrame = 0;
  157.     else{
  158.         REFTIME rt;
  159.         m_pPosition->get_StartTime(&rt);
  160.  
  161.         m_rtThisFrame = (LONGLONG) rt;
  162.     }
  163.  
  164.     if (m_bUsingClock)
  165.         m_rtNextFrame = m_rtThisFrame + (LONG) m_iClockPeriod;
  166.     else
  167.         // This ensures that we don't throw anything away until after we
  168.         // have received the first frame on our lead pin.
  169.         m_rtNextFrame = m_rtThisFrame;
  170.  
  171.     return NOERROR;
  172. }
  173.  
  174.  
  175. HRESULT CBaseVideoMixer::StopStreaming()
  176. {
  177.     // Free any media samples that we are holding on to
  178.     // we need to have been locked for this operation
  179.     // (done by Stop)
  180.  
  181.     CAutoLock waitUntilStoppedSending(&m_csMixLock);
  182.  
  183.     DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::StopStreaming()")));
  184.  
  185.     ReleaseAllQueuedSamples();
  186.  
  187.     return NOERROR;
  188. }
  189.  
  190. // override this to grab extra interfaces on connection
  191. HRESULT CBaseVideoMixer::CheckConnect(int iPin, IPin *pPin)
  192. {
  193.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::CheckConnect(...)")));
  194.     return NOERROR;
  195. }
  196.  
  197.  
  198. // place holder to allow derived classes to release any extra interfaces
  199. HRESULT CBaseVideoMixer::BreakConnect(int iPin)
  200. {
  201.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::BreakConnect(...)")));
  202.     return NOERROR;
  203. }
  204.  
  205.  
  206. // override this to know when the media type is really set
  207. HRESULT CBaseVideoMixer::SetMediaType(int iPin, const CMediaType *pmt)
  208. {
  209.     DisplayType("SetMediaType", pmt);
  210.  
  211.     // then check with the other connected inputs to see if
  212.     // they like this format as well
  213.     for (int i = 0; i < m_iInputPinCount; i++) {
  214.     if ((i != iPin) && m_apInput[i]->IsConnected()) {
  215.         if (!AreEqualVideoTypes(pmt, &m_apInput[i]->CurrentMediaType())) {
  216.         DbgLog((LOG_TRACE, 2, TEXT("Stream %d doesn't match, reconnecting"), i));
  217.         m_pGraph->Reconnect(m_apInput[i]);
  218.         }
  219.     }
  220.     }
  221.  
  222.     if ((i != m_iInputPinCount) && m_pOutput->IsConnected()) {
  223.     if (!AreEqualVideoTypes(pmt, &m_pOutput->CurrentMediaType())) {
  224.         DbgLog((LOG_TRACE, 2, TEXT("Output doesn't match, reconnecting")));
  225.         m_pGraph->Reconnect(m_pOutput);
  226.     }
  227.     }
  228.  
  229.     return NOERROR;
  230. }
  231.  
  232. HRESULT CBaseVideoMixer::Receive(void)
  233. {
  234.     IMediaSample *pSampleOut;
  235.  
  236.     // We need to block out some changes while we are attempting to mix
  237.     // the samples. I.e. no old samples leaving, no pins being connected
  238.     // or disconnected, no changes to variables such as m_iLeadPin, etc.
  239.     // It is OK to accept new samples while mixing (as the will go to the back of
  240.     // a queue) and it is OK to change our state to paused.
  241.     CAutoLock lock(&m_csMixLock);
  242.  
  243.     while (TRUE) {    
  244.         if (!m_bUsingClock) {
  245.         // We may need to deliver more than one sample for every receive,
  246.         // so we loop while we have a sample on our lead pin.
  247.         if (!m_apInput[m_iLeadPin]->SampleReady())
  248.         break;
  249.     
  250.         // Release all samples which stop before our lead frame stops
  251.             m_apInput[m_iLeadPin]->GetHeadStopTime(&m_rtNextFrame);
  252.  
  253.         DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::Receive resetting next frame time to %s"),
  254.             (LPCTSTR)CDisp(m_rtNextFrame)));
  255.     }
  256.  
  257.         for (int i=0; i < m_iInputPinCount; i++)
  258.             m_apInput[i]->ReleaseAllBefore(m_rtNextFrame);
  259.  
  260.         // See if we can mix the samples
  261.         if (SUCCEEDED(MixAndOutputSamples(&pSampleOut))) {
  262.         DbgLog((LOG_TRACE, 6, TEXT(" -- got NOERROR ")));
  263.  
  264.             // Mix went OK. If we are using the clock then increment our
  265.             // clock time. If we are not using the clock then release the
  266.             // sample on our lead pin
  267.             m_rtThisFrame = m_rtNextFrame;
  268.             if (m_bUsingClock)
  269.                 m_rtNextFrame += (LONG) m_iClockPeriod;
  270.             else
  271.                 m_apInput[m_iLeadPin]->ReleaseHeadSample();
  272.  
  273.             // The video mixer may well hold on to our thread when
  274.             // we deliver the sample. So we remove our lock in order to allow
  275.             // state changes whilst delivering.
  276.             m_csMixLock.Unlock();
  277.  
  278.             HRESULT hr = m_pOutput->Deliver(pSampleOut);
  279.             pSampleOut->Release();
  280.  
  281.             m_csMixLock.Lock();
  282.  
  283.             if (hr != NOERROR) {
  284.         DbgLog((LOG_TRACE, 4, TEXT(" -- got error %x from Deliver"), hr));
  285.                 break;
  286.         }
  287.         } else {
  288.             DbgLog((LOG_TRACE, 6, TEXT(" -- got error from MixAndOutput")));
  289.             // must be missing a sample, so stop
  290.             break;
  291.         }
  292.     
  293.     }
  294.  
  295.     BOOL fAtEOS = TRUE;
  296.     if (!m_bUsingClock) {
  297.     fAtEOS = m_apInput[m_iLeadPin]->m_fEOSReceived;
  298.     } else {
  299.         for (int i=0; i < m_iInputPinCount; i++) {
  300.         if (!m_apInput[i]->m_fEOSReceived) {
  301.         fAtEOS = FALSE;
  302.         break;
  303.         }
  304.     }
  305.     }
  306.  
  307.     if (fAtEOS) {
  308.     DbgLog((LOG_TRACE, 1, TEXT("Sending EOS")));
  309.     m_pOutput->DeliverEndOfStream();
  310.     }
  311.  
  312.     DbgLog((LOG_TRACE, 4, TEXT(" leaving Receive")));
  313.  
  314.     return NOERROR;
  315. }
  316.  
  317. HRESULT CBaseVideoMixer::MixAndOutputSamples(IMediaSample **ppOut)
  318. {
  319.     HRESULT hr;
  320.  
  321.     *ppOut = NULL;
  322.  
  323.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::MixAndOutputSamples(...)")));
  324.  
  325.     // Check that all of the expected samples are in
  326.     for (int i =0; i<m_iInputPinCount; i++) {
  327.         if (m_apInput[i] == NULL ||
  328.         (!m_apInput[i]->SampleReady() &&
  329.          !m_apInput[i]->m_fEOSReceived)) {
  330.             DbgLog((LOG_TRACE, 5, TEXT(" -- pin %d is not ready"), i));
  331.             return E_FAIL;
  332.         }
  333.     }
  334.  
  335.     IMediaSample *pOutSample;
  336.  
  337.     DbgLog((LOG_TRACE, 8, TEXT(" -- getting output buffer ")));
  338.     hr = m_pOutput->GetDeliveryBuffer(&pOutSample, NULL, NULL, 0);
  339.  
  340.     if (FAILED(hr)) {
  341.         DbgLog((LOG_TRACE, 4, TEXT(" -- failed to get output buffer ")));
  342.         return hr;
  343.     }
  344.  
  345.     ASSERT(pOutSample != NULL);
  346.     DbgLog((LOG_TRACE, 8, TEXT(" -- got output buffer ")));
  347.  
  348.     // Set the properties
  349.     pOutSample->SetTime((REFERENCE_TIME*)&m_rtThisFrame, (REFERENCE_TIME*)&m_rtNextFrame);
  350.  
  351.     // we're always outputting uncompressed RGB.
  352.     pOutSample->SetSyncPoint(TRUE);
  353.     pOutSample->SetDiscontinuity(FALSE);
  354.  
  355.     // have the derived class mix the data
  356.     DbgLog((LOG_TRACE, 8, TEXT(" -- mixing...")));
  357.     hr = MixSamples(pOutSample);
  358.  
  359.  
  360.     if (hr != NOERROR)
  361.         pOutSample->Release();
  362.     else
  363.         *ppOut = pOutSample;
  364.  
  365.     return hr;
  366.  
  367. }
  368.  
  369. // ReleaseAllQueuedSamples
  370. // - release all samples which are held on our input pins
  371. HRESULT CBaseVideoMixer::ReleaseAllQueuedSamples(void)
  372. {
  373.     // Calls ReleaseHeadSample (as opposed to m_SampleList.RemoveAll)
  374.     // to ensure that we actually release the sample
  375.     for (int i = 0; i < m_iInputPinCount; i ++)
  376.         while (m_apInput[i]->SampleReady())
  377.             m_apInput[i]->ReleaseHeadSample();
  378.  
  379.     return NOERROR;
  380. }
  381.  
  382. HRESULT CBaseVideoMixer::HandleLeadingPinStopping(void)
  383. {
  384.   {
  385.     // protect change m_iLeadPin;
  386.     CAutoLock lock(&m_csMixLock);
  387.   
  388.     // switch leading pin to next active stream
  389.     for(int iStream = m_iLeadPin; iStream < m_iInputPinCount; iStream++)
  390.     {
  391.       if(!m_apInput[iStream]->m_fEOSReceived)
  392.       {
  393.         m_iLeadPin = iStream;
  394.         break;
  395.       }
  396.     }
  397.   }
  398.  
  399.   return this->Receive();
  400. }
  401.  
  402.  
  403. // enter flush state. Receives already blocked
  404. // must override this if you have queued data or a worker thread
  405. HRESULT CBaseVideoMixer::BeginFlush(void)
  406. {
  407.     // check we are able to receive commands
  408.  
  409.     HRESULT hr = CanChangeState();
  410.     if (FAILED(hr)) {
  411.         return hr;
  412.     }
  413.  
  414.     // block receives -- done by caller (CBaseInputPin::BeginFlush)
  415.  
  416.     // discard queued data
  417.  
  418.     // - release all of our queued input data
  419.     // (we don't queue output data)
  420.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::BeginFlush()")));
  421.  
  422.     ReleaseAllQueuedSamples();
  423.     // free anyone blocked on receive - not possible in this filter
  424.  
  425.     // call downstream
  426.     return m_pOutput->DeliverBeginFlush();
  427. }
  428.  
  429. // leave flush state. must override this if you have queued data
  430. // or a worker thread
  431. HRESULT CBaseVideoMixer::EndFlush(void)
  432. {
  433.     // check we are able to receive commands
  434.  
  435.     HRESULT hr = CanChangeState();
  436.     if (FAILED(hr)) {
  437.         return hr;
  438.     }
  439.  
  440.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::EndFlush()")));
  441.     // sync with pushing thread -- we have no worker thread
  442.  
  443.     // reset m_iLeadPin in case it changed
  444.     m_iLeadPin = 0;
  445.  
  446.  
  447.     // Reset our frame times. m_rtNextFrame will be set correctly
  448.     // by Receive() if we are not using the clock
  449.  
  450.     if (m_pPosition == NULL)
  451.         m_rtThisFrame = 0;
  452.     else{
  453.         REFTIME rt;
  454.         m_pPosition->get_StartTime(&rt);
  455.  
  456.         m_rtThisFrame = (LONGLONG) rt;
  457.     }
  458.  
  459.     if (m_bUsingClock)
  460.         m_rtNextFrame = m_rtThisFrame + (LONG) m_iClockPeriod;
  461.     else
  462.         // This ensures that we don't throw anything away until after we
  463.         // have received the first frame on our lead pin.
  464.         m_rtNextFrame = m_rtThisFrame;
  465.  
  466.     DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixer::EndFlush resetting next frame time to %s"),
  467.         (LPCTSTR)CDisp(m_rtNextFrame)));
  468.     
  469.     // caller (the input pin's method) will unblock Receives
  470.  
  471.     // call EndFlush on downstream pins
  472.     return m_pOutput->DeliverEndFlush();
  473.  
  474. }
  475.  
  476.  
  477.  
  478. // check we are in a position to change state
  479. HRESULT CBaseVideoMixer::CanChangeState()
  480. {
  481.     // check we have a valid input connection(s)
  482.  
  483.     // we don't lock. If the caller requires the state not to change
  484.     // after the check then they must provide the lock
  485.  
  486.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::CanChangeState(...)")));
  487.  
  488.     for (int i = 0; i < m_iInputPinCount; i++)
  489.         if (m_apInput[i] == NULL || !m_apInput[i]->m_mt.IsValid())
  490.             return E_FAIL;
  491.  
  492.     // check we have a valid output connection
  493.     if (!m_pOutput->m_mt.IsValid())
  494.         return E_FAIL;
  495.  
  496.     return NOERROR;
  497. }
  498.  
  499.  
  500. // override these so that the derived filter can catch them
  501.  
  502. STDMETHODIMP CBaseVideoMixer::Stop()
  503. {
  504.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::Stop(...)")));
  505.     CAutoLock l(&m_csFilter);
  506.  
  507.     // Is there any change needed
  508.     if (m_State == State_Stopped) {
  509.         return NOERROR;
  510.     }
  511.  
  512.     // check we can change state
  513.  
  514.     HRESULT hr = CanChangeState();
  515.     if (FAILED(hr)) {
  516.         return hr;
  517.     }
  518.  
  519.     // allow a class derived from CBaseVideoMixer
  520.     // to know about starting and stopping streaming
  521.     hr = StopStreaming();
  522.     if (FAILED(hr)) {
  523.         return hr;
  524.     }
  525.  
  526.     // reset m_iLeadPin in case it changed
  527.     m_iLeadPin = 0;
  528.  
  529.     // do the state transition
  530.     return CBaseFilter::Stop();
  531. }
  532.  
  533.  
  534. STDMETHODIMP CBaseVideoMixer::Pause()
  535. {
  536.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::Pause(...)")));
  537.     CAutoLock l(&m_csFilter);
  538.  
  539.     // Is there any change needed
  540.     if (m_State == State_Paused) {
  541.         return NOERROR;
  542.     }
  543.  
  544.     // check we can change state
  545.  
  546.     HRESULT hr = CanChangeState();
  547.     if (FAILED(hr)) {
  548.         if (m_pOutput) {
  549.             m_pOutput->DeliverEndOfStream();
  550.         }
  551.         return hr;
  552.     }
  553.  
  554.     // allow a class derived from CBaseVideoMixer
  555.     // to know about starting and stopping streaming
  556.  
  557.     if (m_State == State_Stopped) {
  558.         hr = StartStreaming();
  559.         if (FAILED(hr)) {
  560.             return hr;
  561.         }
  562.     }
  563.     return CBaseFilter::Pause();
  564. }
  565.  
  566.  
  567. STDMETHODIMP CBaseVideoMixer::Run(REFERENCE_TIME tStart)
  568. {
  569.     DbgLog((LOG_TRACE, 3, TEXT("CBaseVideoMixer::Run(...)")));
  570.     CAutoLock l(&m_csFilter);
  571.  
  572.     // Is there any change needed
  573.     if (m_State == State_Running) {
  574.         return NOERROR;
  575.     }
  576.  
  577.     HRESULT hr = CanChangeState();
  578.     if (FAILED(hr)) {
  579.         return hr;
  580.     }
  581.  
  582.     // This will call CBaseVideoMixer::Pause if necessary, so we don't
  583.     // need to call StartStreaming here.
  584.  
  585.     return CBaseFilter::Run(tStart);
  586. }
  587.  
  588. // check that we can support a given media type
  589. HRESULT CBaseVideoMixer::CheckMediaType(int iPin, const CMediaType* pmt)
  590. {
  591.     DisplayType("CheckMediaType", pmt);
  592.  
  593.     // ask the derived class
  594.     HRESULT hr = CanMixType(pmt);
  595.     if (FAILED(hr)) {
  596.     DbgLog((LOG_TRACE, 2, TEXT("Mixer doesn't like this format")));
  597.     
  598.     return hr;
  599.     }
  600.  
  601.     // ask the filters connected to our inputs
  602.     for (int i = 0; i < m_iInputPinCount; i++) {
  603.     if ((i != iPin) && m_apInput[i]->IsConnected()) {
  604.         hr = m_apInput[i]->CurrentPeer()->QueryAccept(pmt);
  605.         if (hr != S_OK) {
  606.         DbgLog((LOG_TRACE, 2, TEXT("Input %d doesn't like this format"), i));
  607.         hr = E_FAIL;
  608.  
  609.         return hr;
  610.         }
  611.     }
  612.     }
  613.  
  614.     // ask the filters connected to our outputs
  615.     if ((m_iInputPinCount != iPin) && m_pOutput->IsConnected()) {
  616.     hr = m_pOutput->CurrentPeer()->QueryAccept(pmt);
  617.     if (hr != S_OK) {
  618.         DbgLog((LOG_TRACE, 2, TEXT("Output doesn't like this format")));
  619.         hr = E_FAIL;
  620.  
  621.         return hr;
  622.     }
  623.     }
  624.  
  625.     return hr;
  626. }
  627.  
  628.  
  629.  
  630. // =================================================================
  631. // Implements the CBaseVideoMixerInputPin class
  632. // =================================================================
  633.  
  634.  
  635. // constructor
  636.  
  637. CBaseVideoMixerInputPin::CBaseVideoMixerInputPin(
  638.                             TCHAR *pObjectName,
  639.                             CBaseFilter *pBaseFilter,
  640.                             CBaseVideoMixer *pBaseVideoMixer,
  641.                             HRESULT * phr,
  642.                             LPCWSTR pName,
  643.                             int iPinNo)
  644.     : CBaseInputPin(pObjectName, pBaseFilter, &pBaseVideoMixer->m_csFilter, phr, pName)
  645.     , m_SampleList(NAME("CBaseVideoMixerInpuPin::m_SampleList"))
  646.     , m_iPinNo(iPinNo)
  647.     , m_pBaseVideoMixer(pBaseVideoMixer)
  648. {
  649.     DbgLog((LOG_TRACE,4,TEXT("CBaseVideoMixerInputPin::CBaseVideoMixerInputPin")));
  650. }
  651.  
  652.  
  653. // destructor
  654.  
  655. CBaseVideoMixerInputPin::~CBaseVideoMixerInputPin()
  656. {
  657.     DbgLog((LOG_TRACE,4,TEXT("CBaseVideoMixerInputPin::~CBaseVideoMixerInputPin")));
  658. }
  659.  
  660. IMediaSample *CBaseVideoMixerInputPin::GetHeadSample(void)
  661. {
  662.     ASSERT(m_SampleList.GetCount() != 0);
  663.     return m_SampleList.Get(m_SampleList.GetHeadPosition());
  664. }
  665.  
  666. void CBaseVideoMixerInputPin::ReleaseHeadSample(void)
  667. {
  668.     ASSERT(m_SampleList.GetCount() != 0);
  669.  
  670.     DbgLog((LOG_TRACE, 4, TEXT("CBaseVideoMixerInputPin::ReleaseHeadSample() on pin %d"), m_iPinNo));
  671.  
  672.     m_SampleList.Get(m_SampleList.GetHeadPosition())->Release();
  673.     m_SampleList.RemoveHead();
  674. }
  675.  
  676. void CBaseVideoMixerInputPin::ReleaseAllBefore(CRefTime rtTime)
  677. {
  678.     CRefTime rtStop;
  679.     DbgLog((LOG_TRACE, 5, TEXT("CBaseVideoMixerInputPin::ReleaseAllBefore(%s) on pin %d"),
  680.         (LPCTSTR)CDisp(rtTime), m_iPinNo));
  681.  
  682.     while (SampleReady()) {
  683.         GetHeadStopTime(&rtStop);
  684.  
  685.         if (rtStop < rtTime)
  686.             ReleaseHeadSample();
  687.         else
  688.             return;
  689.     }
  690.  
  691. }
  692.  
  693. void CBaseVideoMixerInputPin::GetHeadStopTime(CRefTime *prt)
  694. {
  695.     ASSERT(m_SampleList.GetCount() != 0);
  696.  
  697.     CRefTime junk;
  698.     m_SampleList.Get(m_SampleList.GetHeadPosition())->GetTime(
  699.          (REFERENCE_TIME*)&junk,
  700.          (REFERENCE_TIME*)prt);
  701.  
  702.     if (m_pBaseVideoMixer->m_apOffsets)
  703.     *prt += m_pBaseVideoMixer->m_apOffsets[m_iPinNo];
  704.  
  705. }
  706.  
  707.  
  708. BOOL CBaseVideoMixerInputPin::SampleReady(void)
  709. {
  710.     return m_SampleList.GetCount() != 0;
  711. }
  712.  
  713.  
  714. // override this just to hold the critsec
  715.  
  716. STDMETHODIMP CBaseVideoMixerInputPin::Disconnect(void)
  717. {
  718.     CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
  719.  
  720.     m_pBaseVideoMixer->m_iInputPinsConnected--;
  721.  
  722.     return CBaseInputPin::Disconnect();
  723. }
  724.  
  725.  
  726. // provides derived filter a chance to grab extra interfaces
  727.  
  728. HRESULT CBaseVideoMixerInputPin::CheckConnect(IPin *pPin)
  729. {
  730.     if (pPin == m_pBaseVideoMixer->m_pOutput) {
  731.     DbgLog((LOG_TRACE, 2, TEXT("Tried to connect input #%d to output"), m_iPinNo));
  732.     return E_FAIL;
  733.     }
  734.     
  735.     HRESULT hr = m_pBaseVideoMixer->CheckConnect(m_iPinNo, pPin);
  736.     if (FAILED(hr)) {
  737.         return hr;
  738.     }
  739.     return CBaseInputPin::CheckConnect(pPin);
  740. }
  741.  
  742.  
  743. // provides derived filter a chance to release it's extra interfaces
  744.  
  745. HRESULT
  746. CBaseVideoMixerInputPin::BreakConnect()
  747. {
  748.     m_pBaseVideoMixer->BreakConnect(m_iPinNo);
  749.     m_mt.SetType(&GUID_NULL);
  750.  
  751.     return CBaseInputPin::BreakConnect();
  752. }
  753.  
  754.  
  755. // check that we can support a given media type
  756. HRESULT CBaseVideoMixerInputPin::CheckMediaType(const CMediaType* pmt)
  757. {
  758.     return m_pBaseVideoMixer->CheckMediaType(m_iPinNo, pmt);
  759. }
  760.  
  761.  
  762. // set the media type for this connection
  763. HRESULT CBaseVideoMixerInputPin::SetMediaType(const CMediaType* mtIn)
  764. {
  765.     CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
  766.  
  767.     // Set the base class media type (should always succeed)
  768.     HRESULT hr = CBasePin::SetMediaType(mtIn);
  769.     ASSERT(SUCCEEDED(hr));
  770.  
  771.     // check the transform can be done (should always succeed)
  772.     ASSERT(SUCCEEDED(m_pBaseVideoMixer->CheckMediaType(m_iPinNo, mtIn)));
  773.  
  774.     m_mt = *mtIn;
  775.     m_pBaseVideoMixer->SetMediaType(m_iPinNo, mtIn);
  776.  
  777.     m_pBaseVideoMixer->m_iInputPinsConnected++;
  778.  
  779.     return NOERROR;
  780. }
  781.  
  782.  
  783. HRESULT CBaseVideoMixerInputPin::Active()
  784. {
  785.     m_fEOSReceived = FALSE;
  786.  
  787.     return NOERROR;
  788. }
  789.  
  790. // =================================================================
  791. // Implements IMemInputPin interface
  792. // =================================================================
  793.  
  794.  
  795. // 'queue' the EOS after all received data
  796. STDMETHODIMP CBaseVideoMixerInputPin::EndOfStream(void)
  797. {
  798.     m_fEOSReceived = TRUE;
  799.  
  800.     int iLeadPin;
  801.     HRESULT hr = m_pBaseVideoMixer->GetLeadPin(&iLeadPin);
  802.     if(FAILED(hr))
  803.       return hr;
  804.             
  805.     if(m_iPinNo == iLeadPin);
  806.     {
  807.       hr = m_pBaseVideoMixer->HandleLeadingPinStopping();
  808.       if(FAILED(hr))
  809.         return hr;
  810.     }
  811.     
  812.     return m_pBaseVideoMixer->Receive();
  813. }
  814.  
  815. // enter flushing state. Call default handler to block Receives, then
  816. // pass to overridable method in filter
  817. STDMETHODIMP CBaseVideoMixerInputPin::BeginFlush(void)
  818. {
  819.     HRESULT hr = CBaseInputPin::BeginFlush();
  820.     if (FAILED(hr)) {
  821.         return hr;
  822.     }
  823.  
  824.     return m_pBaseVideoMixer->BeginFlush();
  825. }
  826.  
  827. // leave flushing state.
  828. // Pass to overridable method in filter, then call base class
  829. // to unblock receives (finally)
  830. STDMETHODIMP CBaseVideoMixerInputPin::EndFlush(void)
  831. {
  832.     HRESULT hr = m_pBaseVideoMixer->EndFlush();
  833.     if (FAILED(hr)) {
  834.         return hr;
  835.     }
  836.  
  837.     m_fEOSReceived = FALSE; // correct???
  838.  
  839.     return CBaseInputPin::EndFlush();
  840. }
  841.  
  842.  
  843. // The next block of data has been received from upstream
  844. HRESULT CBaseVideoMixerInputPin::Receive(IMediaSample * pSample)
  845. {
  846.     ASSERT(pSample);
  847.     HRESULT hr;
  848.  
  849.     DbgLog((LOG_TRACE, 4, TEXT("CBaseVideoMixerInputPin::Receive(..) on pin %d"), m_iPinNo));
  850.  
  851.     if (m_pBaseVideoMixer->m_iInputPinsConnected != m_pBaseVideoMixer->m_iInputPinCount) {
  852.         DbgLog((LOG_TRACE, 2, TEXT("CBaseVideoMixerInputPin::Receive() without all inputs connected!")));
  853.         return S_FALSE;
  854.     }
  855.  
  856.     // check all is well with the base class
  857.     hr = CBaseInputPin::Receive(pSample);
  858.     if (FAILED(hr)) {
  859.         return hr;
  860.     }
  861.  
  862.     if (m_pBaseVideoMixer->m_apLengths) {
  863.     CRefTime tStart, tStop;
  864.     pSample->GetTime((REFERENCE_TIME*)&tStart, (REFERENCE_TIME*)&tStop);
  865.  
  866.     if (tStart > m_pBaseVideoMixer->m_apLengths[m_iPinNo]) {
  867.         DbgLog((LOG_TRACE, 2, TEXT("Past stopping time for pin %d"), m_iPinNo));
  868.         m_fEOSReceived = TRUE;
  869.  
  870.             int iLeadPin;
  871.             hr = m_pBaseVideoMixer->GetLeadPin(&iLeadPin);
  872.             if(FAILED(hr))
  873.               return hr;
  874.             
  875.             if(m_iPinNo == iLeadPin);
  876.             {
  877.               hr = m_pBaseVideoMixer->HandleLeadingPinStopping();
  878.               if(FAILED(hr))
  879.                 return hr;
  880.             }
  881.         return S_FALSE;
  882.     }
  883.     }
  884.  
  885.     // If a graph is stopped and a late sample comes along,
  886.     // then we need to reject the sample. If we don't, we'll end up
  887.     // with a sample with a late time stamp hanging around in our
  888.     // buffers, and that will mess up the algorithm in MixAndOutputSamples
  889.     if (m_pBaseVideoMixer->m_State == State_Stopped) {
  890.     DbgLog((LOG_ERROR, 1, TEXT("Receive while stopped!")));
  891.         return VFW_E_WRONG_STATE;
  892.     }
  893.  
  894.     // Keep this sample and add it to the sample list
  895.     pSample->AddRef();                  // keep new one
  896.     m_SampleList.AddTail(pSample);
  897.  
  898.     return m_pBaseVideoMixer->Receive();
  899. }
  900.  
  901.  
  902. // Pass on the Quality notification q to
  903. // a. Our QualityControl sink (if we have one) or else
  904. // b. to our upstream filter or else
  905. // c. the filter graph
  906. // and if that doesn't work, throw it away with a bad return code
  907. HRESULT CBaseVideoMixerInputPin::PassNotify(Quality q)
  908. {
  909.     // We pass the message on, which means that we find the quality sink
  910.     // for our input pin and send it there
  911.  
  912.     DbgLog((LOG_TRACE,4,TEXT("Passing Quality notification through VideoMixer")));
  913.  
  914.     if (m_pQSink!=NULL) {
  915.         return m_pQSink->Notify(m_pBaseVideoMixer, q);
  916.     } else {
  917.  
  918.         // no sink set, so pass it upstream
  919.         HRESULT hr;
  920.         IQualityControl * pIQC;
  921.  
  922.         hr = E_FAIL;                   // default
  923.         if (m_Connected) {
  924.             m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
  925.  
  926.             if (pIQC!=NULL) {
  927.                 hr = pIQC->Notify(m_pBaseVideoMixer, q);
  928.                 pIQC->Release();
  929.             }
  930.         }
  931.  
  932.         return hr;
  933.     }
  934.  
  935. } // PassNotify
  936.  
  937.  
  938.  
  939. STDMETHODIMP CBaseVideoMixerInputPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
  940. {
  941.     CheckPointer(ppEnum,E_POINTER)
  942.     ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
  943.  
  944.     // find the first connected input pin and use its enumeration.
  945.     // of course, it may enumerate formats which other inputs can't support,
  946.     // but we'll catch that in CheckMediaType.
  947.     for (int i = 0; i < m_pBaseVideoMixer->m_iInputPinCount; i++) {
  948.     if (i != m_iPinNo && m_pBaseVideoMixer->m_apInput[i]->IsConnected()) {
  949.         return m_pBaseVideoMixer->m_apInput[i]->CurrentPeer()->
  950.             EnumMediaTypes(ppEnum);
  951.     }
  952.     }
  953.  
  954.     // no other pins connected?
  955.     return E_UNEXPECTED;
  956. }
  957.  
  958.  
  959.  
  960.  
  961.  
  962. // =================================================================
  963. // Implements the CBaseVideoMixerOutputPin class
  964. // =================================================================
  965.  
  966.  
  967. // constructor
  968.  
  969. CBaseVideoMixerOutputPin::CBaseVideoMixerOutputPin(
  970.     TCHAR *pObjectName,
  971.     CBaseFilter *pBaseFilter,
  972.     CBaseVideoMixer *pBaseVideoMixer,
  973.     HRESULT * phr,
  974.     LPCWSTR pPinName)
  975.     : CBaseOutputPin(pObjectName, pBaseFilter, &pBaseVideoMixer->m_csFilter, phr, pPinName),
  976.     m_iOutputPin(pBaseVideoMixer->m_iInputPinCount)
  977. {
  978.     DbgLog((LOG_TRACE,2,TEXT("CBaseVideoMixerOutputPin::CBaseVideoMixerOutputPin")));
  979.  
  980.     m_pBaseVideoMixer = pBaseVideoMixer;
  981.  
  982. }
  983.  
  984.  
  985. // destructor
  986.  
  987. CBaseVideoMixerOutputPin::~CBaseVideoMixerOutputPin()
  988. {
  989.     DbgLog((LOG_TRACE,2,TEXT("CBaseVideoMixerOutputPin::~CBaseVideoMixerOutputPin")));
  990.  
  991. }
  992.  
  993.  
  994. // overriden to expose IMediaPosition control interface
  995. STDMETHODIMP CBaseVideoMixerOutputPin::NonDelegatingQueryInterface(REFIID riid,
  996.                                                               void **ppv)
  997. {
  998.     CheckPointer(ppv,E_POINTER)
  999.     ValidateReadWritePtr(ppv,sizeof(PVOID));
  1000.     *ppv = NULL;
  1001.  
  1002.     if (riid == IID_IMediaPosition) {
  1003.         if (m_pBaseVideoMixer->m_pPosition == NULL) {
  1004.  
  1005.             HRESULT hr = S_OK;
  1006.             m_pBaseVideoMixer->m_pPosition = new CMultiPinPosPassThru(
  1007.                                     NAME("CBaseVideoMixer::m_pPosition")
  1008.                                   , GetOwner()
  1009.                                   , &hr
  1010.                                );
  1011.  
  1012.             if (m_pBaseVideoMixer->m_pPosition == NULL)
  1013.                 return E_OUTOFMEMORY;
  1014.  
  1015.             if (FAILED(hr)) {
  1016.                 delete m_pBaseVideoMixer->m_pPosition;
  1017.                 m_pBaseVideoMixer->m_pPosition = NULL;
  1018.                 return hr;
  1019.             }
  1020.  
  1021.             m_pBaseVideoMixer->m_pPosition->SetPins(
  1022.                                     (CBasePin **) m_pBaseVideoMixer->m_apInput
  1023.                   , m_pBaseVideoMixer->m_apOffsets
  1024.                                   , m_pBaseVideoMixer->m_iInputPinCount
  1025.                                );
  1026.         }
  1027.         return m_pBaseVideoMixer->m_pPosition->NonDelegatingQueryInterface(riid, ppv);
  1028.  
  1029.     } else
  1030.         return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
  1031.  
  1032. }
  1033.  
  1034.  
  1035. // override this just to hold the critsec
  1036.  
  1037. STDMETHODIMP CBaseVideoMixerOutputPin::Disconnect(void)
  1038. {
  1039.     CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
  1040.     return CBaseOutputPin::Disconnect();
  1041. }
  1042.  
  1043.  
  1044. // provides derived filter a chance to grab extra interfaces
  1045.  
  1046. HRESULT CBaseVideoMixerOutputPin::CheckConnect(IPin *pPin)
  1047. {
  1048.     for (int i = 0; i < m_iOutputPin; i++) {
  1049.     if (pPin == m_pBaseVideoMixer->m_apInput[i]) {
  1050.         DbgLog((LOG_TRACE, 2, TEXT("Tried to connect output to input #%d"), i));
  1051.         return E_FAIL;
  1052.     }
  1053.     }
  1054.     
  1055.     HRESULT hr = m_pBaseVideoMixer->CheckConnect(m_iOutputPin, pPin);
  1056.     if (FAILED(hr))
  1057.         return hr;
  1058.  
  1059.     return CBaseOutputPin::CheckConnect(pPin);
  1060. }
  1061.  
  1062.  
  1063. // provides derived filter a chance to release it's extra interfaces
  1064.  
  1065. HRESULT CBaseVideoMixerOutputPin::BreakConnect()
  1066. {
  1067.     m_pBaseVideoMixer->BreakConnect(m_iOutputPin);
  1068.     m_mt.SetType(&GUID_NULL);
  1069.  
  1070.     return CBaseOutputPin::BreakConnect();
  1071. }
  1072.  
  1073.  
  1074. STDMETHODIMP CBaseVideoMixerOutputPin::EnumMediaTypes(IEnumMediaTypes **ppEnum)
  1075. {
  1076.     CheckPointer(ppEnum,E_POINTER)
  1077.     ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
  1078.  
  1079.     // find the first connected input pin and use its enumeration.
  1080.     // of course, it may enumerate formats which other inputs can't support,
  1081.     // but we'll catch that in CheckMediaType.
  1082.     for (int i = 0; i < m_pBaseVideoMixer->m_iInputPinCount; i++) {
  1083.     if (m_pBaseVideoMixer->m_apInput[i]->IsConnected()) {
  1084.         return m_pBaseVideoMixer->m_apInput[i]->CurrentPeer()->
  1085.             EnumMediaTypes(ppEnum);
  1086.     }
  1087.     }
  1088.  
  1089.     // no pins connected?
  1090.     return E_UNEXPECTED;
  1091. }
  1092.  
  1093.  
  1094. // check a given transform - must have selected input type first
  1095.  
  1096. HRESULT CBaseVideoMixerOutputPin::CheckMediaType(const CMediaType* pmtOut)
  1097. {
  1098.     return m_pBaseVideoMixer->CheckMediaType(m_iOutputPin, pmtOut);
  1099. }
  1100.  
  1101.  
  1102. // called after we have agreed a media type to actually set it in which case
  1103. // we run the CheckTransform function to get the output format type again
  1104.  
  1105. HRESULT CBaseVideoMixerOutputPin::SetMediaType(const CMediaType* pmtOut)
  1106. {
  1107.     HRESULT hr = NOERROR;
  1108.  
  1109.     // Set the base class media type (should always succeed)
  1110.     hr = CBasePin::SetMediaType(pmtOut);
  1111.     ASSERT(SUCCEEDED(hr));
  1112.  
  1113.     // Check this transform can be done (should always succeed)
  1114.     ASSERT(SUCCEEDED(m_pBaseVideoMixer->CheckMediaType(m_iOutputPin, pmtOut)));
  1115.  
  1116.     m_mt = *pmtOut;
  1117.     m_pBaseVideoMixer->SetMediaType(m_iOutputPin, pmtOut);
  1118.  
  1119.     return NOERROR;
  1120. }
  1121.  
  1122.  
  1123. // set the buffer size according to the current format.
  1124.  
  1125. HRESULT
  1126. CBaseVideoMixerOutputPin::DecideBufferSize(
  1127.     IMemAllocator * pAllocator,
  1128.     ALLOCATOR_PROPERTIES * pProp)
  1129. {
  1130.     if (!IsConnected()) {
  1131.         DbgBreak("DecideBufferSize called when !m_pOutput->IsConnected()");
  1132.         return VFW_E_NOT_CONNECTED;
  1133.     } else {
  1134.         // I don't think we actually care about alignment here,
  1135.         // so leave it untouched
  1136.  
  1137.         pProp->cBuffers = 1;
  1138.         pProp->cbBuffer = CurrentMediaType().GetSampleSize();
  1139.         ALLOCATOR_PROPERTIES propActual;
  1140.  
  1141.         HRESULT hr = pAllocator->SetProperties(pProp, &propActual);
  1142.         if (FAILED(hr)) {
  1143.         return hr;
  1144.         }
  1145.  
  1146.         if ((pProp->cBuffers > propActual.cBuffers)
  1147.             || (pProp->cbBuffer > propActual.cbBuffer)
  1148.            ) {
  1149.             return E_FAIL;
  1150.         }
  1151.     }
  1152.  
  1153.     return NOERROR;
  1154. }
  1155.  
  1156.  
  1157. // Override this if you can do something constructive to act on the
  1158. // quality message.
  1159. //
  1160. // Pass the quality messages on upstream.
  1161. STDMETHODIMP CBaseVideoMixerOutputPin::Notify(IFilter * pSender, Quality q)
  1162. {
  1163.     CheckPointer(pSender,E_POINTER)
  1164.     ValidateReadPtr(pSender,sizeof(IFilter));
  1165.     CAutoLock lock(&m_pBaseVideoMixer->m_csFilter);
  1166.  
  1167.     if (m_pBaseVideoMixer->m_apInput==NULL) {
  1168.         // why would this ever be??
  1169.         DbgBreak("m_pInput is NULL");
  1170.         return E_FAIL;
  1171.     }
  1172.  
  1173.     // If we are running late then jump ahead
  1174.     if (q.Late > 0) {
  1175.         m_pBaseVideoMixer->m_rtNextFrame += q.Late;
  1176.         m_pBaseVideoMixer->m_rtThisFrame += q.Late;
  1177.     }
  1178.  
  1179.     // Send the quality message to all of our input pins
  1180.     for (int i=0; i<m_pBaseVideoMixer->m_iInputPinCount; i++)
  1181.         if (m_pBaseVideoMixer->m_apInput[i] != NULL) {
  1182.             HRESULT hr = m_pBaseVideoMixer->m_apInput[i]->PassNotify(q);
  1183.             if (FAILED(hr))
  1184.                 return hr;
  1185.         }
  1186.  
  1187.     return NOERROR;
  1188.  
  1189. } // Notify
  1190.  
  1191.  
  1192. BOOL AreEqualVideoTypes( const CMediaType *pmt1, const CMediaType *pmt2 )
  1193. {
  1194.     // The standard implementation is too strict - it demands an exact match
  1195.     // We just want to know is they are the same format and have the same
  1196.     // width / height
  1197.  
  1198.     ASSERT( IsEqualGUID( *pmt1->Type(), MEDIATYPE_Video ) );
  1199.     ASSERT( IsEqualGUID( *pmt2->Type(), MEDIATYPE_Video ) );
  1200.     ASSERT( *pmt1->FormatType() == FORMAT_VideoInfo );
  1201.     ASSERT( *pmt2->FormatType() == FORMAT_VideoInfo );
  1202.  
  1203.     VIDEOINFO *pvi1 = (VIDEOINFO *) pmt1->Format();
  1204.     VIDEOINFO *pvi2 = (VIDEOINFO *) pmt2->Format();
  1205.  
  1206.     return    IsEqualGUID( *pmt1->Subtype(), *pmt2->Subtype() )
  1207.            && pvi1->bmiHeader.biWidth  == pvi2->bmiHeader.biWidth
  1208.            && pvi2->bmiHeader.biHeight == pvi2->bmiHeader.biHeight;
  1209. }
  1210.  
  1211. 
  1212.