home *** CD-ROM | disk | FTP | other *** search
/ The Net: Ultimate Internet Guide / WWLCD1.ISO / mac / SiteBldr / AMOVIE / SDK / _SETUP / COMMON.Z / outputq.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-30  |  21.1 KB  |  717 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. /*
  13.     outputq.cpp
  14.  
  15.     COutputQueue class
  16.  
  17.     This is a class used by an output pin which may sometimes want
  18.     to queue output samples on a separate thread and sometimes
  19.     call Receive() directly on the input pin
  20. */
  21.  
  22. #include <streams.h>
  23.  
  24.  
  25. //
  26. //  COutputQueue Constructor :
  27. //
  28. //  Determines if a thread is to be created and creates resources
  29. //
  30. //     pInputPin  - the downstream input pin we're queueing samples to
  31. //
  32. //     phr        - changed to a failure code if this function fails
  33. //                  (otherwise unchanges)
  34. //
  35. //     bAuto      - Ask pInputPin if it can block in Receive by calling
  36. //                  its ReceiveCanBlock method and create a thread if
  37. //                  it can block, otherwise not.
  38. //
  39. //     bQueue     - if bAuto == FALSE then we create a thread if and only
  40. //                  if bQueue == TRUE
  41. //
  42. //     lBatchSize - work in batches of lBatchSize
  43. //
  44. //     bBatchEact - Use exact batch sizes so don't send until the
  45. //                  batch is full or SendAnyway() is called
  46. //
  47. //     lListSize  - If we create a thread make the list of samples queued
  48. //                  to the thread have this size cache
  49. //
  50. //     dwPriority - If we create a thread set its priority to this
  51. //
  52. COutputQueue::COutputQueue(
  53.              IPin         *pInputPin,          //  Pin to send stuff to
  54.              HRESULT      *phr,                //  'Return code'
  55.              BOOL          bAuto,              //  Ask pin if queue or not
  56.              BOOL          bQueue,             //  Send through queue
  57.              LONG          lBatchSize,         //  Batch
  58.              BOOL          bBatchExact,        //  Batch exactly to BatchSize
  59.              LONG          lListSize,
  60.              DWORD         dwPriority
  61.             ) : m_lBatchSize(lBatchSize),
  62.                 m_bBatchExact(bBatchExact && (lBatchSize > 1)),
  63.                 m_hThread(NULL),
  64.                 m_hSem(NULL),
  65.                 m_List(NULL),
  66.                 m_pPin(pInputPin),
  67.                 m_ppSamples(NULL),
  68.                 m_lWaiting(0),
  69.                 m_pInputPin(NULL),
  70.                 m_bSendAnyway(FALSE),
  71.                 m_nBatched(0),
  72.                 m_bFlushing(FALSE),
  73.                 m_bFlushed(TRUE),
  74.                 m_bTerminate(FALSE),
  75.                 m_hr(S_OK)
  76. {
  77.     ASSERT(m_lBatchSize > 0);
  78.  
  79.  
  80.     if (FAILED(*phr)) {
  81.         return;
  82.     }
  83.  
  84.     //  Check the input pin is OK and cache its IMemInputPin interface
  85.  
  86.     *phr = pInputPin->QueryInterface(IID_IMemInputPin, (void **)&m_pInputPin);
  87.     if (FAILED(*phr)) {
  88.         return;
  89.     }
  90.  
  91.     // See if we should ask the downstream pin
  92.  
  93.     if (bAuto) {
  94.         HRESULT hr = m_pInputPin->ReceiveCanBlock();
  95.         if (SUCCEEDED(hr)) {
  96.             bQueue = hr == S_OK;
  97.         }
  98.     }
  99.  
  100.     //  Create our sample batch
  101.  
  102.     m_ppSamples = new PMEDIASAMPLE[m_lBatchSize];
  103.     if (m_ppSamples == NULL) {
  104.         *phr = E_OUTOFMEMORY;
  105.         return;
  106.     }
  107.  
  108.     //  If we're queueing allocate resources
  109.  
  110.     if (bQueue) {
  111.         DbgLog((LOG_TRACE, 2, TEXT("Creating thread for output pin")));
  112.         m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
  113.         if (m_hSem == NULL) {
  114.             DWORD dwError = GetLastError();
  115.             *phr = HRESULT_FROM_WIN32(dwError);
  116.             return;
  117.         }
  118.         m_List = new CSampleList(NAME("Sample Queue List"),
  119.                                  lListSize,
  120.                                  FALSE         // No lock
  121.                                 );
  122.         if (m_List == NULL) {
  123.             *phr = E_OUTOFMEMORY;
  124.             return;
  125.         }
  126.  
  127.  
  128.         DWORD dwThreadId;
  129.         m_hThread = CreateThread(NULL,
  130.                                  0,
  131.                                  InitialThreadProc,
  132.                                  (LPVOID)this,
  133.                                  0,
  134.                                  &dwThreadId);
  135.         if (m_hThread == NULL) {
  136.             DWORD dwError = GetLastError();
  137.             *phr = HRESULT_FROM_WIN32(dwError);
  138.             return;
  139.         }
  140.         SetThreadPriority(m_hThread, dwPriority);
  141.     } else {
  142.         DbgLog((LOG_TRACE, 2, TEXT("Calling input pin directly - no thread")));
  143.     }
  144. }
  145.  
  146. //
  147. //  COutputQueuee Destructor :
  148. //
  149. //  Free all resources -
  150. //
  151. //      Thread,
  152. //      Batched samples
  153. //
  154. COutputQueue::~COutputQueue()
  155. {
  156.     DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue")));
  157.     /*  Free our pointer */
  158.     if (m_pInputPin != NULL) {
  159.         m_pInputPin->Release();
  160.     }
  161.     if (m_hThread != NULL) {
  162.         {
  163.             CAutoLock lck(this);
  164.             m_bTerminate = TRUE;
  165.             m_hr = S_FALSE;
  166.             NotifyThread();
  167.         }
  168.         DbgWaitForSingleObject(m_hThread);
  169.         EXECUTE_ASSERT(CloseHandle(m_hThread));
  170.  
  171.         //  The thread frees the samples when asked to terminate
  172.  
  173.         ASSERT(m_List->GetCount() == 0);
  174.         delete m_List;
  175.     } else {
  176.         FreeSamples();
  177.     }
  178.     if (m_hSem != NULL) {
  179.         EXECUTE_ASSERT(CloseHandle(m_hSem));
  180.     }
  181.     delete m_ppSamples;
  182. }
  183.  
  184. //
  185. //  Call the real thread proc as a member function
  186. //
  187. DWORD WINAPI COutputQueue::InitialThreadProc(LPVOID pv)
  188. {
  189.     COutputQueue *pSampleQueue = (COutputQueue *)pv;
  190.     return pSampleQueue->ThreadProc();
  191. }
  192.  
  193. //
  194. //  Thread sending the samples downstream :
  195. //
  196. //  When there is nothing to do the thread sets m_lWaiting (while
  197. //  holding the critical section) and then waits for m_hSem to be
  198. //  set (not holding the critical section)
  199. //
  200. DWORD COutputQueue::ThreadProc()
  201. {
  202.     while (TRUE) {
  203.         BOOL          bWait = FALSE;
  204.         IMediaSample *pSample;
  205.         LONG          lNumberToSend; // Local copy
  206.         NewSegmentPacket* ppacket;
  207.  
  208.         //
  209.         //  Get a batch of samples and send it if possible
  210.         //  In any case exit the loop if there is a control action
  211.         //  requested
  212.         //
  213.         {
  214.             CAutoLock lck(this);
  215.             while (TRUE) {
  216.  
  217.                 if (m_bTerminate) {
  218.                     FreeSamples();
  219.                     return 0;
  220.                 }
  221.                 if (m_bFlushing) {
  222.                     FreeSamples();
  223.                     SetEvent(m_evFlushComplete);
  224.                 }
  225.  
  226.                 //  Get a sample off the list
  227.  
  228.                 pSample = m_List->RemoveHead();
  229.  
  230.                 if (pSample != NULL &&
  231.                     !IsSpecialSample(pSample)) {
  232.  
  233.                     //  If its just a regular sample just add it to the batch
  234.                     //  and exit the loop if the batch is full
  235.  
  236.                     m_ppSamples[m_nBatched++] = pSample;
  237.                     if (m_nBatched == m_lBatchSize) {
  238.                         break;
  239.                     }
  240.                 } else {
  241.  
  242.                     //  If there was nothing in the queue and there's nothing
  243.                     //  to send (either because there's nothing or the batch
  244.                     //  isn't full) then prepare to wait
  245.  
  246.                     if (pSample == NULL &&
  247.                         (m_bBatchExact || m_nBatched == 0)) {
  248.  
  249.                         //  Tell other thread to set the event when there's
  250.                         //  something do to
  251.  
  252.                         ASSERT(m_lWaiting == 0);
  253.                         m_lWaiting++;
  254.                         bWait      = TRUE;
  255.                     } else {
  256.  
  257.                         //  We break out of the loop on SEND_PACKET unless
  258.                         //  there's nothing to send
  259.  
  260.                         if (pSample == SEND_PACKET && m_nBatched == 0) {
  261.                             continue;
  262.                         }
  263.  
  264.                         if (pSample == NEW_SEGMENT) {
  265.                             // now we need the parameters - we are
  266.                             // guaranteed that the next packet contains them
  267.                             ppacket = (NewSegmentPacket *) m_List->RemoveHead();
  268.                             ASSERT(ppacket);
  269.                         }
  270.                         //  EOS_PACKET falls through here and we exit the loop
  271.                         //  In this way it acts like SEND_PACKET
  272.                     }
  273.                     break;
  274.                 }
  275.             }
  276.             if (!bWait) {
  277.                 // We look at m_nBatched from the client side so keep
  278.                 // it up to date inside the critical section
  279.                 lNumberToSend = m_nBatched;  // Local copy
  280.                 m_nBatched = 0;
  281.             }
  282.         }
  283.  
  284.         //  Wait for some more data
  285.  
  286.         if (bWait) {
  287.             DbgWaitForSingleObject(m_hSem);
  288.             continue;
  289.         }
  290.  
  291.  
  292.  
  293.         //  OK - send it if there's anything to send
  294.         //  We DON'T check m_bBatchExact here because either we've got
  295.         //  a full batch or we dropped through because we got
  296.         //  SEND_PACKET or EOS_PACKET - both of which imply we should
  297.         //  flush our batch
  298.  
  299.         if (lNumberToSend != 0) {
  300.             long nProcessed;
  301.             if (m_hr == S_OK) {
  302.                 HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
  303.                                                           lNumberToSend,
  304.                                                           &nProcessed);
  305.                 /*  Don't overwrite a flushing state HRESULT */
  306.                 CAutoLock lck(this);
  307.                 if (m_hr == S_OK) {
  308.                     m_hr = hr;
  309.                 }
  310.             }
  311.             while (lNumberToSend != 0) {
  312.                 m_ppSamples[--lNumberToSend]->Release();
  313.             }
  314.             if (m_hr != S_OK) {
  315.  
  316.                 //  In any case wait for more data - S_OK just
  317.                 //  means there wasn't an error
  318.  
  319.                 DbgLog((LOG_ERROR, 2, TEXT("ReceiveMultiple returned %8.8X"),
  320.                        m_hr));
  321.             }
  322.         }
  323.  
  324.         //  Check for end of stream
  325.  
  326.         if (pSample == EOS_PACKET) {
  327.  
  328.             //  We don't send even end of stream on if we've previously
  329.             //  returned something other than S_OK
  330.             //  This is because in that case the pin which returned
  331.             //  something other than S_OK should have either sent
  332.             //  EndOfStream() or notified the filter graph
  333.  
  334.             if (m_hr == S_OK) {
  335.                 DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
  336.                 HRESULT hr = m_pPin->EndOfStream();
  337.                 if (FAILED(hr)) {
  338.                     DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
  339.                 }
  340.             }
  341.         }
  342.  
  343.         //  Data from a new source
  344.  
  345.         if (pSample == RESET_PACKET) {
  346.             m_hr = S_OK;
  347.             SetEvent(m_evFlushComplete);
  348.         }
  349.  
  350.         if (pSample == NEW_SEGMENT) {
  351.             m_pPin->NewSegment(ppacket->tStart, ppacket->tStop, ppacket->dRate);
  352.             delete ppacket;
  353.         }
  354.     }
  355. }
  356.  
  357. //  Send batched stuff anyway
  358. void COutputQueue::SendAnyway()
  359. {
  360.     if (!IsQueued()) {
  361.  
  362.         //  m_bSendAnyway is a secret hack parameter to ReceiveMultiple
  363.  
  364.         m_bSendAnyway = TRUE;
  365.         LONG nProcessed;
  366.         ReceiveMultiple(NULL, 0, &nProcessed);
  367.         m_bSendAnyway = FALSE;
  368.  
  369.     } else {
  370.         CAutoLock lck(this);
  371.         QueueSample(SEND_PACKET);
  372.         NotifyThread();
  373.     }
  374. }
  375.  
  376. void
  377. COutputQueue::NewSegment(
  378.     REFERENCE_TIME tStart,
  379.     REFERENCE_TIME tStop,
  380.     double dRate)
  381. {
  382.     if (!IsQueued()) {
  383.         if (S_OK == m_hr) {
  384.             if (m_bBatchExact) {
  385.                 SendAnyway();
  386.             }
  387.             m_pPin->NewSegment(tStart, tStop, dRate);
  388.         }
  389.     } else {
  390.         if (m_hr == S_OK) {
  391.             //
  392.             // we need to queue the new segment to appear in order in the
  393.             // data, but we need to pass parameters to it. Rather than
  394.             // take the hit of wrapping every single sample so we can tell
  395.             // special ones apart, we queue special pointers to indicate
  396.             // special packets, and we guarantee (by holding the
  397.             // critical section) that the packet immediately following a
  398.             // NEW_SEGMENT value is a NewSegmentPacket containing the
  399.             // parameters.
  400.             NewSegmentPacket * ppack = new NewSegmentPacket;
  401.             if (ppack == NULL) {
  402.                 return;
  403.             }
  404.             ppack->tStart = tStart;
  405.             ppack->tStop = tStop;
  406.             ppack->dRate = dRate;
  407.  
  408.             CAutoLock lck(this);
  409.             QueueSample(NEW_SEGMENT);
  410.             QueueSample( (IMediaSample*) ppack);
  411.             NotifyThread();
  412.         }
  413.     }
  414. }
  415.  
  416.  
  417. //
  418. //  End of Stream is queued to output device
  419. //
  420. void COutputQueue::EOS()
  421. {
  422.     CAutoLock lck(this);
  423.     if (!IsQueued()) {
  424.         if (m_bBatchExact) {
  425.             SendAnyway();
  426.         }
  427.         if (m_hr == S_OK) {
  428.             DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
  429.             m_bFlushed = FALSE;
  430.             HRESULT hr = m_pPin->EndOfStream();
  431.             if (FAILED(hr)) {
  432.                 DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
  433.             }
  434.         }
  435.     } else {
  436.         if (m_hr == S_OK) {
  437.             m_bFlushed = FALSE;
  438.             QueueSample(EOS_PACKET);
  439.             NotifyThread();
  440.         }
  441.     }
  442. }
  443.  
  444. //
  445. //  Flush all the samples in the queue
  446. //
  447. void COutputQueue::BeginFlush()
  448. {
  449.     {
  450.         CAutoLock lck(this);
  451.  
  452.         // block receives -- we assume this is done by the
  453.         // filter in which we are a component
  454.  
  455.         // discard all queued data
  456.  
  457.         m_bFlushing = TRUE;
  458.  
  459.         //  Make sure we discard all samples from now on
  460.  
  461.         if (m_hr == S_OK) {
  462.             m_hr = S_FALSE;
  463.         }
  464.  
  465.         // Optimize so we don't keep calling downstream all the time
  466.  
  467.         if (m_bFlushed) {
  468.             return;
  469.         }
  470.  
  471.         if (IsQueued()) {
  472.             NotifyThread();
  473.         } else {
  474.             /*  DON'T call FreeSamples() here - we aren't
  475.                 synchronized with the Receive() processing thread
  476.             */
  477.         }
  478.     }
  479.  
  480.     // pass this downstream
  481.  
  482.     m_pPin->BeginFlush();
  483. }
  484.  
  485. //
  486. // leave flush mode - pass this downstream
  487. void COutputQueue::EndFlush()
  488. {
  489.     {
  490.         CAutoLock lck(this);
  491.         ASSERT(m_bFlushing);
  492.         if (m_bFlushed) {
  493.             m_bFlushing = FALSE;
  494.             m_hr = S_OK;
  495.             return;
  496.         }
  497.     }
  498.  
  499.     // sync with pushing thread -- done in BeginFlush
  500.     // ensure no more data to go downstream -- done in BeginFlush
  501.     //
  502.     // Because we are synching here there is no need to hold the critical
  503.     // section (in fact we'd deadlock if we did!)
  504.  
  505.     if (IsQueued()) {
  506.         m_evFlushComplete.Wait();
  507.     } else {
  508.         FreeSamples();
  509.     }
  510.  
  511.     //  Be daring - the caller has guaranteed no samples will arrive
  512.     //  before EndFlush() returns
  513.  
  514.     m_bFlushing = FALSE;
  515.     m_bFlushed  = TRUE;
  516.  
  517.     // call EndFlush on downstream pins
  518.  
  519.     m_pPin->EndFlush();
  520.  
  521.     m_hr = S_OK;
  522. }
  523.  
  524. //  COutputQueue::QueueSample
  525. //
  526. //  private method to Send a sample to the output queue
  527. //  The critical section MUST be held when this is called
  528.  
  529. void COutputQueue::QueueSample(IMediaSample *pSample)
  530. {
  531.     if (NULL == m_List->AddTail(pSample)) {
  532.         if (!IsSpecialSample(pSample)) {
  533.             pSample->Release();
  534.         }
  535.     }
  536. }
  537.  
  538. //
  539. //  COutputQueue::Receive()
  540. //
  541. //  Send a single sample by the multiple sample route
  542. //  (NOTE - this could be optimized if necessary)
  543. //
  544. //  On return the sample will have been Release()'d
  545. //
  546.  
  547. HRESULT COutputQueue::Receive(IMediaSample *pSample)
  548. {
  549.     LONG nProcessed;
  550.     return ReceiveMultiple(&pSample, 1, &nProcessed);
  551. }
  552.  
  553. //
  554. //  COutputQueue::ReceiveMultiple()
  555. //
  556. //  Send a set of samples to the downstream pin
  557. //
  558. //      ppSamples           - array of samples
  559. //      nSamples            - how many
  560. //      nSamplesProcessed   - How many were processed
  561. //
  562. //  On return all samples will have been Release()'d
  563. //
  564.  
  565. HRESULT COutputQueue::ReceiveMultiple (
  566.     IMediaSample **ppSamples,
  567.     long nSamples,
  568.     long *nSamplesProcessed)
  569. {
  570.     CAutoLock lck(this);
  571.     //  Either call directly or queue up the samples
  572.  
  573.     if (!IsQueued()) {
  574.  
  575.         //  If we already had a bad return code then just return
  576.  
  577.         if (S_OK != m_hr) {
  578.  
  579.             //  If we've never received anything since the last Flush()
  580.             //  and the sticky return code is not S_OK we must be
  581.             //  flushing
  582.             //  ((!A || B) is equivalent to A implies B)
  583.             ASSERT(!m_bFlushed || m_bFlushing);
  584.  
  585.             //  We're supposed to Release() them anyway!
  586.             *nSamplesProcessed = 0;
  587.             for (int i = 0; i < nSamples; i++) {
  588.                 DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"),
  589.                         nSamples, m_hr));
  590.                 ppSamples[i]->Release();
  591.             }
  592.  
  593.             return m_hr;
  594.         }
  595.         //
  596.         //  If we're flushing the stick return code should be S_FALSE
  597.         //
  598.         ASSERT(!m_bFlushing);
  599.         m_bFlushed = FALSE;
  600.  
  601.         ASSERT(m_nBatched < m_lBatchSize);
  602.         ASSERT(m_nBatched == 0 || m_bBatchExact);
  603.  
  604.         //  Loop processing the samples in batches
  605.  
  606.         LONG iLost = 0;
  607.         for (long iDone = 0;
  608.              iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway);
  609.             ) {
  610.  
  611. //pragma message (REMIND("Implement threshold scheme"))
  612.             ASSERT(m_nBatched < m_lBatchSize);
  613.             if (iDone < nSamples) {
  614.                 m_ppSamples[m_nBatched++] = ppSamples[iDone++];
  615.             }
  616.             if (m_nBatched == m_lBatchSize ||
  617.                 nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) {
  618.                 LONG nDone;
  619.                 DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"),
  620.                        m_nBatched));
  621.  
  622.                 if (m_hr == S_OK) {
  623.                     m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
  624.                                                         m_nBatched,
  625.                                                         &nDone);
  626.                 } else {
  627.                     nDone = 0;
  628.                 }
  629.                 iLost += m_nBatched - nDone;
  630.                 for (LONG i = 0; i < m_nBatched; i++) {
  631.                     m_ppSamples[i]->Release();
  632.                 }
  633.                 m_nBatched = 0;
  634.             }
  635.         }
  636.         *nSamplesProcessed = iDone - iLost;
  637.         if (*nSamplesProcessed < 0) {
  638.             *nSamplesProcessed = 0;
  639.         }
  640.         return m_hr;
  641.     } else {
  642.         /*  We're sending to our thread */
  643.  
  644.         if (m_hr != S_OK) {
  645.             *nSamplesProcessed = 0;
  646.             DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"),
  647.                     nSamples, m_hr));
  648.             for (int i = 0; i < nSamples; i++) {
  649.                 ppSamples[i]->Release();
  650.             }
  651.             return m_hr;
  652.         }
  653.         m_bFlushed = FALSE;
  654.         for (long i = 0; i < nSamples; i++) {
  655.             QueueSample(ppSamples[i]);
  656.         }
  657.         *nSamplesProcessed = nSamples;
  658.         if (!m_bBatchExact ||
  659.             m_nBatched + m_List->GetCount() >= m_lBatchSize) {
  660.             NotifyThread();
  661.         }
  662.         return S_OK;
  663.     }
  664. }
  665.  
  666. //  Get ready for new data - cancels sticky m_hr
  667. void COutputQueue::Reset()
  668. {
  669.     if (!IsQueued()) {
  670.         m_hr = S_OK;
  671.     } else {
  672.         CAutoLock lck(this);
  673.         QueueSample(RESET_PACKET);
  674.         NotifyThread();
  675.         m_evFlushComplete.Wait();
  676.     }
  677. }
  678.  
  679. //  Remove and Release() all queued and Batched samples
  680. void COutputQueue::FreeSamples()
  681. {
  682.     CAutoLock lck(this);
  683.     if (IsQueued()) {
  684.         while (TRUE) {
  685.             IMediaSample *pSample = m_List->RemoveHead();
  686.             if (pSample == NULL) {
  687.                 break;
  688.             }
  689.             if (!IsSpecialSample(pSample)) {
  690.                 pSample->Release();
  691.             } else {
  692.                 if (pSample == NEW_SEGMENT) {
  693.                     //  Free NEW_SEGMENT packet
  694.                     EXECUTE_ASSERT(NULL != m_List->RemoveHead());
  695.                 }
  696.             }
  697.         }
  698.     }
  699.     for (int i = 0; i < m_nBatched; i++) {
  700.         m_ppSamples[i]->Release();
  701.     }
  702.     m_nBatched = 0;
  703. }
  704.  
  705. //  Notify the thread if there is something to do
  706. //
  707. //  The critical section MUST be held when this is called
  708. void COutputQueue::NotifyThread()
  709. {
  710.     //  Optimize - no need to signal if it's not waiting
  711.     ASSERT(IsQueued());
  712.     if (m_lWaiting) {
  713.         ReleaseSemaphore(m_hSem, m_lWaiting, NULL);
  714.         m_lWaiting = 0;
  715.     }
  716. }
  717.