forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			802 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			802 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
//------------------------------------------------------------------------------
 | 
						|
// File: OutputQ.cpp
 | 
						|
//
 | 
						|
// Desc: DirectShow base classes - implements COutputQueue class used by an
 | 
						|
//       output pin which may sometimes want to queue output samples on a
 | 
						|
//       separate thread and sometimes call Receive() directly on the input
 | 
						|
//       pin.
 | 
						|
//
 | 
						|
// Copyright (c) 1992-2001 Microsoft Corporation.  All rights reserved.
 | 
						|
//------------------------------------------------------------------------------
 | 
						|
 | 
						|
 | 
						|
#include <streams.h>
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
//  COutputQueue Constructor :
 | 
						|
//
 | 
						|
//  Determines if a thread is to be created and creates resources
 | 
						|
//
 | 
						|
//     pInputPin  - the downstream input pin we're queueing samples to
 | 
						|
//
 | 
						|
//     phr        - changed to a failure code if this function fails
 | 
						|
//                  (otherwise unchanges)
 | 
						|
//
 | 
						|
//     bAuto      - Ask pInputPin if it can block in Receive by calling
 | 
						|
//                  its ReceiveCanBlock method and create a thread if
 | 
						|
//                  it can block, otherwise not.
 | 
						|
//
 | 
						|
//     bQueue     - if bAuto == FALSE then we create a thread if and only
 | 
						|
//                  if bQueue == TRUE
 | 
						|
//
 | 
						|
//     lBatchSize - work in batches of lBatchSize
 | 
						|
//
 | 
						|
//     bBatchEact - Use exact batch sizes so don't send until the
 | 
						|
//                  batch is full or SendAnyway() is called
 | 
						|
//
 | 
						|
//     lListSize  - If we create a thread make the list of samples queued
 | 
						|
//                  to the thread have this size cache
 | 
						|
//
 | 
						|
//     dwPriority - If we create a thread set its priority to this
 | 
						|
//
 | 
						|
COutputQueue::COutputQueue(
 | 
						|
             IPin         *pInputPin,          //  Pin to send stuff to
 | 
						|
             __inout HRESULT      *phr,        //  'Return code'
 | 
						|
             BOOL          bAuto,              //  Ask pin if queue or not
 | 
						|
             BOOL          bQueue,             //  Send through queue
 | 
						|
             LONG          lBatchSize,         //  Batch
 | 
						|
             BOOL          bBatchExact,        //  Batch exactly to BatchSize
 | 
						|
             LONG          lListSize,
 | 
						|
             DWORD         dwPriority,
 | 
						|
             bool          bFlushingOpt        // flushing optimization
 | 
						|
            ) : m_lBatchSize(lBatchSize),
 | 
						|
                m_bBatchExact(bBatchExact && (lBatchSize > 1)),
 | 
						|
                m_hThread(NULL),
 | 
						|
                m_hSem(NULL),
 | 
						|
                m_List(NULL),
 | 
						|
                m_pPin(pInputPin),
 | 
						|
                m_ppSamples(NULL),
 | 
						|
                m_lWaiting(0),
 | 
						|
                m_evFlushComplete(FALSE, phr),
 | 
						|
                m_pInputPin(NULL),
 | 
						|
                m_bSendAnyway(FALSE),
 | 
						|
                m_nBatched(0),
 | 
						|
                m_bFlushing(FALSE),
 | 
						|
                m_bFlushed(TRUE),
 | 
						|
                m_bFlushingOpt(bFlushingOpt),
 | 
						|
                m_bTerminate(FALSE),
 | 
						|
                m_hEventPop(NULL),
 | 
						|
                m_hr(S_OK)
 | 
						|
{
 | 
						|
    ASSERT(m_lBatchSize > 0);
 | 
						|
 | 
						|
 | 
						|
    if (FAILED(*phr)) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    //  Check the input pin is OK and cache its IMemInputPin interface
 | 
						|
 | 
						|
    *phr = pInputPin->QueryInterface(IID_IMemInputPin, (void **)&m_pInputPin);
 | 
						|
    if (FAILED(*phr)) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    // See if we should ask the downstream pin
 | 
						|
 | 
						|
    if (bAuto) {
 | 
						|
        HRESULT hr = m_pInputPin->ReceiveCanBlock();
 | 
						|
        if (SUCCEEDED(hr)) {
 | 
						|
            bQueue = hr == S_OK;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    //  Create our sample batch
 | 
						|
 | 
						|
    m_ppSamples = new PMEDIASAMPLE[m_lBatchSize];
 | 
						|
    if (m_ppSamples == NULL) {
 | 
						|
        *phr = E_OUTOFMEMORY;
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    //  If we're queueing allocate resources
 | 
						|
 | 
						|
    if (bQueue) {
 | 
						|
        DbgLog((LOG_TRACE, 2, TEXT("Creating thread for output pin")));
 | 
						|
        m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
 | 
						|
        if (m_hSem == NULL) {
 | 
						|
            DWORD dwError = GetLastError();
 | 
						|
            *phr = AmHresultFromWin32(dwError);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        m_List = new CSampleList(NAME("Sample Queue List"),
 | 
						|
                                 lListSize,
 | 
						|
                                 FALSE         // No lock
 | 
						|
                                );
 | 
						|
        if (m_List == NULL) {
 | 
						|
            *phr = E_OUTOFMEMORY;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        DWORD dwThreadId;
 | 
						|
        m_hThread = CreateThread(NULL,
 | 
						|
                                 0,
 | 
						|
                                 InitialThreadProc,
 | 
						|
                                 (LPVOID)this,
 | 
						|
                                 0,
 | 
						|
                                 &dwThreadId);
 | 
						|
        if (m_hThread == NULL) {
 | 
						|
            DWORD dwError = GetLastError();
 | 
						|
            *phr = AmHresultFromWin32(dwError);
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        SetThreadPriority(m_hThread, dwPriority);
 | 
						|
    } else {
 | 
						|
        DbgLog((LOG_TRACE, 2, TEXT("Calling input pin directly - no thread")));
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  COutputQueuee Destructor :
 | 
						|
//
 | 
						|
//  Free all resources -
 | 
						|
//
 | 
						|
//      Thread,
 | 
						|
//      Batched samples
 | 
						|
//
 | 
						|
COutputQueue::~COutputQueue()
 | 
						|
{
 | 
						|
    DbgLog((LOG_TRACE, 3, TEXT("COutputQueue::~COutputQueue")));
 | 
						|
    /*  Free our pointer */
 | 
						|
    if (m_pInputPin != NULL) {
 | 
						|
        m_pInputPin->Release();
 | 
						|
    }
 | 
						|
    if (m_hThread != NULL) {
 | 
						|
        {
 | 
						|
            CAutoLock lck(this);
 | 
						|
            m_bTerminate = TRUE;
 | 
						|
            m_hr = S_FALSE;
 | 
						|
            NotifyThread();
 | 
						|
        }
 | 
						|
        DbgWaitForSingleObject(m_hThread);
 | 
						|
        EXECUTE_ASSERT(CloseHandle(m_hThread));
 | 
						|
 | 
						|
        //  The thread frees the samples when asked to terminate
 | 
						|
 | 
						|
        ASSERT(m_List->GetCount() == 0);
 | 
						|
        delete m_List;
 | 
						|
    } else {
 | 
						|
        FreeSamples();
 | 
						|
    }
 | 
						|
    if (m_hSem != NULL) {
 | 
						|
        EXECUTE_ASSERT(CloseHandle(m_hSem));
 | 
						|
    }
 | 
						|
    delete [] m_ppSamples;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  Call the real thread proc as a member function
 | 
						|
//
 | 
						|
DWORD WINAPI COutputQueue::InitialThreadProc(__in LPVOID pv)
 | 
						|
{
 | 
						|
    HRESULT hrCoInit = CAMThread::CoInitializeHelper();
 | 
						|
    
 | 
						|
    COutputQueue *pSampleQueue = (COutputQueue *)pv;
 | 
						|
    DWORD dwReturn = pSampleQueue->ThreadProc();
 | 
						|
 | 
						|
    if(hrCoInit == S_OK) {
 | 
						|
        CoUninitialize();
 | 
						|
    }
 | 
						|
    
 | 
						|
    return dwReturn;
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  Thread sending the samples downstream :
 | 
						|
//
 | 
						|
//  When there is nothing to do the thread sets m_lWaiting (while
 | 
						|
//  holding the critical section) and then waits for m_hSem to be
 | 
						|
//  set (not holding the critical section)
 | 
						|
//
 | 
						|
DWORD COutputQueue::ThreadProc()
 | 
						|
{
 | 
						|
    while (TRUE) {
 | 
						|
        BOOL          bWait = FALSE;
 | 
						|
        IMediaSample *pSample;
 | 
						|
        LONG          lNumberToSend; // Local copy
 | 
						|
        NewSegmentPacket* ppacket;
 | 
						|
 | 
						|
        //
 | 
						|
        //  Get a batch of samples and send it if possible
 | 
						|
        //  In any case exit the loop if there is a control action
 | 
						|
        //  requested
 | 
						|
        //
 | 
						|
        {
 | 
						|
            CAutoLock lck(this);
 | 
						|
            while (TRUE) {
 | 
						|
 | 
						|
                if (m_bTerminate) {
 | 
						|
                    FreeSamples();
 | 
						|
                    return 0;
 | 
						|
                }
 | 
						|
                if (m_bFlushing) {
 | 
						|
                    FreeSamples();
 | 
						|
                    SetEvent(m_evFlushComplete);
 | 
						|
                }
 | 
						|
 | 
						|
                //  Get a sample off the list
 | 
						|
 | 
						|
                pSample = m_List->RemoveHead();
 | 
						|
		// inform derived class we took something off the queue
 | 
						|
		if (m_hEventPop) {
 | 
						|
                    //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));
 | 
						|
		    SetEvent(m_hEventPop);
 | 
						|
		}
 | 
						|
 | 
						|
                if (pSample != NULL &&
 | 
						|
                    !IsSpecialSample(pSample)) {
 | 
						|
 | 
						|
                    //  If its just a regular sample just add it to the batch
 | 
						|
                    //  and exit the loop if the batch is full
 | 
						|
 | 
						|
                    m_ppSamples[m_nBatched++] = pSample;
 | 
						|
                    if (m_nBatched == m_lBatchSize) {
 | 
						|
                        break;
 | 
						|
                    }
 | 
						|
                } else {
 | 
						|
 | 
						|
                    //  If there was nothing in the queue and there's nothing
 | 
						|
                    //  to send (either because there's nothing or the batch
 | 
						|
                    //  isn't full) then prepare to wait
 | 
						|
 | 
						|
                    if (pSample == NULL &&
 | 
						|
                        (m_bBatchExact || m_nBatched == 0)) {
 | 
						|
 | 
						|
                        //  Tell other thread to set the event when there's
 | 
						|
                        //  something do to
 | 
						|
 | 
						|
                        ASSERT(m_lWaiting == 0);
 | 
						|
                        m_lWaiting++;
 | 
						|
                        bWait      = TRUE;
 | 
						|
                    } else {
 | 
						|
 | 
						|
                        //  We break out of the loop on SEND_PACKET unless
 | 
						|
                        //  there's nothing to send
 | 
						|
 | 
						|
                        if (pSample == SEND_PACKET && m_nBatched == 0) {
 | 
						|
                            continue;
 | 
						|
                        }
 | 
						|
 | 
						|
                        if (pSample == NEW_SEGMENT) {
 | 
						|
                            // now we need the parameters - we are
 | 
						|
                            // guaranteed that the next packet contains them
 | 
						|
                            ppacket = (NewSegmentPacket *) m_List->RemoveHead();
 | 
						|
			    // we took something off the queue
 | 
						|
			    if (m_hEventPop) {
 | 
						|
                    	        //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));
 | 
						|
		    	        SetEvent(m_hEventPop);
 | 
						|
			    }
 | 
						|
 | 
						|
                            ASSERT(ppacket);
 | 
						|
                        }
 | 
						|
                        //  EOS_PACKET falls through here and we exit the loop
 | 
						|
                        //  In this way it acts like SEND_PACKET
 | 
						|
                    }
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
            if (!bWait) {
 | 
						|
                // We look at m_nBatched from the client side so keep
 | 
						|
                // it up to date inside the critical section
 | 
						|
                lNumberToSend = m_nBatched;  // Local copy
 | 
						|
                m_nBatched = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //  Wait for some more data
 | 
						|
 | 
						|
        if (bWait) {
 | 
						|
            DbgWaitForSingleObject(m_hSem);
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
 | 
						|
        //  OK - send it if there's anything to send
 | 
						|
        //  We DON'T check m_bBatchExact here because either we've got
 | 
						|
        //  a full batch or we dropped through because we got
 | 
						|
        //  SEND_PACKET or EOS_PACKET - both of which imply we should
 | 
						|
        //  flush our batch
 | 
						|
 | 
						|
        if (lNumberToSend != 0) {
 | 
						|
            long nProcessed;
 | 
						|
            if (m_hr == S_OK) {
 | 
						|
                ASSERT(!m_bFlushed);
 | 
						|
                HRESULT hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
 | 
						|
                                                          lNumberToSend,
 | 
						|
                                                          &nProcessed);
 | 
						|
                /*  Don't overwrite a flushing state HRESULT */
 | 
						|
                CAutoLock lck(this);
 | 
						|
                if (m_hr == S_OK) {
 | 
						|
                    m_hr = hr;
 | 
						|
                }
 | 
						|
                ASSERT(!m_bFlushed);
 | 
						|
            }
 | 
						|
            while (lNumberToSend != 0) {
 | 
						|
                m_ppSamples[--lNumberToSend]->Release();
 | 
						|
            }
 | 
						|
            if (m_hr != S_OK) {
 | 
						|
 | 
						|
                //  In any case wait for more data - S_OK just
 | 
						|
                //  means there wasn't an error
 | 
						|
 | 
						|
                DbgLog((LOG_ERROR, 2, TEXT("ReceiveMultiple returned %8.8X"),
 | 
						|
                       m_hr));
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //  Check for end of stream
 | 
						|
 | 
						|
        if (pSample == EOS_PACKET) {
 | 
						|
 | 
						|
            //  We don't send even end of stream on if we've previously
 | 
						|
            //  returned something other than S_OK
 | 
						|
            //  This is because in that case the pin which returned
 | 
						|
            //  something other than S_OK should have either sent
 | 
						|
            //  EndOfStream() or notified the filter graph
 | 
						|
 | 
						|
            if (m_hr == S_OK) {
 | 
						|
                DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
 | 
						|
                HRESULT hr = m_pPin->EndOfStream();
 | 
						|
                if (FAILED(hr)) {
 | 
						|
                    DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        //  Data from a new source
 | 
						|
 | 
						|
        if (pSample == RESET_PACKET) {
 | 
						|
            m_hr = S_OK;
 | 
						|
            SetEvent(m_evFlushComplete);
 | 
						|
        }
 | 
						|
 | 
						|
        if (pSample == NEW_SEGMENT) {
 | 
						|
            m_pPin->NewSegment(ppacket->tStart, ppacket->tStop, ppacket->dRate);
 | 
						|
            delete ppacket;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//  Send batched stuff anyway
 | 
						|
void COutputQueue::SendAnyway()
 | 
						|
{
 | 
						|
    if (!IsQueued()) {
 | 
						|
 | 
						|
        //  m_bSendAnyway is a private parameter checked in ReceiveMultiple
 | 
						|
 | 
						|
        m_bSendAnyway = TRUE;
 | 
						|
        LONG nProcessed;
 | 
						|
        ReceiveMultiple(NULL, 0, &nProcessed);
 | 
						|
        m_bSendAnyway = FALSE;
 | 
						|
 | 
						|
    } else {
 | 
						|
        CAutoLock lck(this);
 | 
						|
        QueueSample(SEND_PACKET);
 | 
						|
        NotifyThread();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
COutputQueue::NewSegment(
 | 
						|
    REFERENCE_TIME tStart,
 | 
						|
    REFERENCE_TIME tStop,
 | 
						|
    double dRate)
 | 
						|
{
 | 
						|
    if (!IsQueued()) {
 | 
						|
        if (S_OK == m_hr) {
 | 
						|
            if (m_bBatchExact) {
 | 
						|
                SendAnyway();
 | 
						|
            }
 | 
						|
            m_pPin->NewSegment(tStart, tStop, dRate);
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        if (m_hr == S_OK) {
 | 
						|
            //
 | 
						|
            // we need to queue the new segment to appear in order in the
 | 
						|
            // data, but we need to pass parameters to it. Rather than
 | 
						|
            // take the hit of wrapping every single sample so we can tell
 | 
						|
            // special ones apart, we queue special pointers to indicate
 | 
						|
            // special packets, and we guarantee (by holding the
 | 
						|
            // critical section) that the packet immediately following a
 | 
						|
            // NEW_SEGMENT value is a NewSegmentPacket containing the
 | 
						|
            // parameters.
 | 
						|
            NewSegmentPacket * ppack = new NewSegmentPacket;
 | 
						|
            if (ppack == NULL) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            ppack->tStart = tStart;
 | 
						|
            ppack->tStop = tStop;
 | 
						|
            ppack->dRate = dRate;
 | 
						|
 | 
						|
            CAutoLock lck(this);
 | 
						|
            QueueSample(NEW_SEGMENT);
 | 
						|
            QueueSample( (IMediaSample*) ppack);
 | 
						|
            NotifyThread();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//
 | 
						|
//  End of Stream is queued to output device
 | 
						|
//
 | 
						|
void COutputQueue::EOS()
 | 
						|
{
 | 
						|
    CAutoLock lck(this);
 | 
						|
    if (!IsQueued()) {
 | 
						|
        if (m_bBatchExact) {
 | 
						|
            SendAnyway();
 | 
						|
        }
 | 
						|
        if (m_hr == S_OK) {
 | 
						|
            DbgLog((LOG_TRACE, 2, TEXT("COutputQueue sending EndOfStream()")));
 | 
						|
            m_bFlushed = FALSE;
 | 
						|
            HRESULT hr = m_pPin->EndOfStream();
 | 
						|
            if (FAILED(hr)) {
 | 
						|
                DbgLog((LOG_ERROR, 2, TEXT("COutputQueue got code 0x%8.8X from EndOfStream()")));
 | 
						|
            }
 | 
						|
        }
 | 
						|
    } else {
 | 
						|
        if (m_hr == S_OK) {
 | 
						|
            m_bFlushed = FALSE;
 | 
						|
            QueueSample(EOS_PACKET);
 | 
						|
            NotifyThread();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  Flush all the samples in the queue
 | 
						|
//
 | 
						|
void COutputQueue::BeginFlush()
 | 
						|
{
 | 
						|
    if (IsQueued()) {
 | 
						|
        {
 | 
						|
            CAutoLock lck(this);
 | 
						|
 | 
						|
            // block receives -- we assume this is done by the
 | 
						|
            // filter in which we are a component
 | 
						|
 | 
						|
            // discard all queued data
 | 
						|
 | 
						|
            m_bFlushing = TRUE;
 | 
						|
 | 
						|
            //  Make sure we discard all samples from now on
 | 
						|
 | 
						|
            if (m_hr == S_OK) {
 | 
						|
                m_hr = S_FALSE;
 | 
						|
            }
 | 
						|
 | 
						|
            // Optimize so we don't keep calling downstream all the time
 | 
						|
 | 
						|
            if (m_bFlushed && m_bFlushingOpt) {
 | 
						|
                return;
 | 
						|
            }
 | 
						|
 | 
						|
            // Make sure we really wait for the flush to complete
 | 
						|
            m_evFlushComplete.Reset();
 | 
						|
 | 
						|
            NotifyThread();
 | 
						|
        }
 | 
						|
 | 
						|
        // pass this downstream
 | 
						|
 | 
						|
        m_pPin->BeginFlush();
 | 
						|
    } else {
 | 
						|
        // pass downstream first to avoid deadlocks
 | 
						|
        m_pPin->BeginFlush();
 | 
						|
        CAutoLock lck(this);
 | 
						|
        // discard all queued data
 | 
						|
 | 
						|
        m_bFlushing = TRUE;
 | 
						|
 | 
						|
        //  Make sure we discard all samples from now on
 | 
						|
 | 
						|
        if (m_hr == S_OK) {
 | 
						|
            m_hr = S_FALSE;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
// leave flush mode - pass this downstream
 | 
						|
void COutputQueue::EndFlush()
 | 
						|
{
 | 
						|
    {
 | 
						|
        CAutoLock lck(this);
 | 
						|
        ASSERT(m_bFlushing);
 | 
						|
        if (m_bFlushingOpt && m_bFlushed && IsQueued()) {
 | 
						|
            m_bFlushing = FALSE;
 | 
						|
            m_hr = S_OK;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    // sync with pushing thread -- done in BeginFlush
 | 
						|
    // ensure no more data to go downstream -- done in BeginFlush
 | 
						|
    //
 | 
						|
    // Because we are synching here there is no need to hold the critical
 | 
						|
    // section (in fact we'd deadlock if we did!)
 | 
						|
 | 
						|
    if (IsQueued()) {
 | 
						|
        m_evFlushComplete.Wait();
 | 
						|
    } else {
 | 
						|
        FreeSamples();
 | 
						|
    }
 | 
						|
 | 
						|
    //  Be daring - the caller has guaranteed no samples will arrive
 | 
						|
    //  before EndFlush() returns
 | 
						|
 | 
						|
    m_bFlushing = FALSE;
 | 
						|
    m_bFlushed  = TRUE;
 | 
						|
 | 
						|
    // call EndFlush on downstream pins
 | 
						|
 | 
						|
    m_pPin->EndFlush();
 | 
						|
 | 
						|
    m_hr = S_OK;
 | 
						|
}
 | 
						|
 | 
						|
//  COutputQueue::QueueSample
 | 
						|
//
 | 
						|
//  private method to Send a sample to the output queue
 | 
						|
//  The critical section MUST be held when this is called
 | 
						|
 | 
						|
void COutputQueue::QueueSample(IMediaSample *pSample)
 | 
						|
{
 | 
						|
    if (NULL == m_List->AddTail(pSample)) {
 | 
						|
        if (!IsSpecialSample(pSample)) {
 | 
						|
            pSample->Release();
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  COutputQueue::Receive()
 | 
						|
//
 | 
						|
//  Send a single sample by the multiple sample route
 | 
						|
//  (NOTE - this could be optimized if necessary)
 | 
						|
//
 | 
						|
//  On return the sample will have been Release()'d
 | 
						|
//
 | 
						|
 | 
						|
HRESULT COutputQueue::Receive(IMediaSample *pSample)
 | 
						|
{
 | 
						|
    LONG nProcessed;
 | 
						|
    return ReceiveMultiple(&pSample, 1, &nProcessed);
 | 
						|
}
 | 
						|
 | 
						|
//
 | 
						|
//  COutputQueue::ReceiveMultiple()
 | 
						|
//
 | 
						|
//  Send a set of samples to the downstream pin
 | 
						|
//
 | 
						|
//      ppSamples           - array of samples
 | 
						|
//      nSamples            - how many
 | 
						|
//      nSamplesProcessed   - How many were processed
 | 
						|
//
 | 
						|
//  On return all samples will have been Release()'d
 | 
						|
//
 | 
						|
 | 
						|
HRESULT COutputQueue::ReceiveMultiple (
 | 
						|
    __in_ecount(nSamples) IMediaSample **ppSamples,
 | 
						|
    long nSamples,
 | 
						|
    __out long *nSamplesProcessed)
 | 
						|
{
 | 
						|
    if (nSamples < 0) {
 | 
						|
        return E_INVALIDARG;
 | 
						|
    }
 | 
						|
    
 | 
						|
    CAutoLock lck(this);
 | 
						|
    //  Either call directly or queue up the samples
 | 
						|
 | 
						|
    if (!IsQueued()) {
 | 
						|
 | 
						|
        //  If we already had a bad return code then just return
 | 
						|
 | 
						|
        if (S_OK != m_hr) {
 | 
						|
 | 
						|
            //  If we've never received anything since the last Flush()
 | 
						|
            //  and the sticky return code is not S_OK we must be
 | 
						|
            //  flushing
 | 
						|
            //  ((!A || B) is equivalent to A implies B)
 | 
						|
            ASSERT(!m_bFlushed || m_bFlushing);
 | 
						|
 | 
						|
            //  We're supposed to Release() them anyway!
 | 
						|
            *nSamplesProcessed = 0;
 | 
						|
            for (int i = 0; i < nSamples; i++) {
 | 
						|
                DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (direct) : Discarding %d samples code 0x%8.8X"),
 | 
						|
                        nSamples, m_hr));
 | 
						|
                ppSamples[i]->Release();
 | 
						|
            }
 | 
						|
 | 
						|
            return m_hr;
 | 
						|
        }
 | 
						|
        //
 | 
						|
        //  If we're flushing the sticky return code should be S_FALSE
 | 
						|
        //
 | 
						|
        ASSERT(!m_bFlushing);
 | 
						|
        m_bFlushed = FALSE;
 | 
						|
 | 
						|
        ASSERT(m_nBatched < m_lBatchSize);
 | 
						|
        ASSERT(m_nBatched == 0 || m_bBatchExact);
 | 
						|
 | 
						|
        //  Loop processing the samples in batches
 | 
						|
 | 
						|
        LONG iLost = 0;
 | 
						|
        long iDone = 0;
 | 
						|
        for (iDone = 0;
 | 
						|
             iDone < nSamples || (m_nBatched != 0 && m_bSendAnyway);
 | 
						|
            ) {
 | 
						|
 | 
						|
//pragma message (REMIND("Implement threshold scheme"))
 | 
						|
            ASSERT(m_nBatched < m_lBatchSize);
 | 
						|
            if (iDone < nSamples) {
 | 
						|
                m_ppSamples[m_nBatched++] = ppSamples[iDone++];
 | 
						|
            }
 | 
						|
            if (m_nBatched == m_lBatchSize ||
 | 
						|
                nSamples == 0 && (m_bSendAnyway || !m_bBatchExact)) {
 | 
						|
                LONG nDone;
 | 
						|
                DbgLog((LOG_TRACE, 4, TEXT("Batching %d samples"),
 | 
						|
                       m_nBatched));
 | 
						|
 | 
						|
                if (m_hr == S_OK) {
 | 
						|
                    m_hr = m_pInputPin->ReceiveMultiple(m_ppSamples,
 | 
						|
                                                        m_nBatched,
 | 
						|
                                                        &nDone);
 | 
						|
                } else {
 | 
						|
                    nDone = 0;
 | 
						|
                }
 | 
						|
                iLost += m_nBatched - nDone;
 | 
						|
                for (LONG i = 0; i < m_nBatched; i++) {
 | 
						|
                    m_ppSamples[i]->Release();
 | 
						|
                }
 | 
						|
                m_nBatched = 0;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        *nSamplesProcessed = iDone - iLost;
 | 
						|
        if (*nSamplesProcessed < 0) {
 | 
						|
            *nSamplesProcessed = 0;
 | 
						|
        }
 | 
						|
        return m_hr;
 | 
						|
    } else {
 | 
						|
        /*  We're sending to our thread */
 | 
						|
 | 
						|
        if (m_hr != S_OK) {
 | 
						|
            *nSamplesProcessed = 0;
 | 
						|
            DbgLog((LOG_TRACE, 3, TEXT("COutputQueue (queued) : Discarding %d samples code 0x%8.8X"),
 | 
						|
                    nSamples, m_hr));
 | 
						|
            for (int i = 0; i < nSamples; i++) {
 | 
						|
                ppSamples[i]->Release();
 | 
						|
            }
 | 
						|
            return m_hr;
 | 
						|
        }
 | 
						|
        m_bFlushed = FALSE;
 | 
						|
        for (long i = 0; i < nSamples; i++) {
 | 
						|
            QueueSample(ppSamples[i]);
 | 
						|
        }
 | 
						|
        *nSamplesProcessed = nSamples;
 | 
						|
        if (!m_bBatchExact ||
 | 
						|
            m_nBatched + m_List->GetCount() >= m_lBatchSize) {
 | 
						|
            NotifyThread();
 | 
						|
        }
 | 
						|
        return S_OK;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//  Get ready for new data - cancels sticky m_hr
 | 
						|
void COutputQueue::Reset()
 | 
						|
{
 | 
						|
    if (!IsQueued()) {
 | 
						|
        m_hr = S_OK;
 | 
						|
    } else {
 | 
						|
        {
 | 
						|
            CAutoLock lck(this);
 | 
						|
            QueueSample(RESET_PACKET);
 | 
						|
            NotifyThread();
 | 
						|
        }
 | 
						|
        m_evFlushComplete.Wait();
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//  Remove and Release() all queued and Batched samples
 | 
						|
void COutputQueue::FreeSamples()
 | 
						|
{
 | 
						|
    CAutoLock lck(this);
 | 
						|
    if (IsQueued()) {
 | 
						|
        while (TRUE) {
 | 
						|
            IMediaSample *pSample = m_List->RemoveHead();
 | 
						|
	    // inform derived class we took something off the queue
 | 
						|
	    if (m_hEventPop) {
 | 
						|
                //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));
 | 
						|
	        SetEvent(m_hEventPop);
 | 
						|
	    }
 | 
						|
 | 
						|
            if (pSample == NULL) {
 | 
						|
                break;
 | 
						|
            }
 | 
						|
            if (!IsSpecialSample(pSample)) {
 | 
						|
                pSample->Release();
 | 
						|
            } else {
 | 
						|
                if (pSample == NEW_SEGMENT) {
 | 
						|
                    //  Free NEW_SEGMENT packet
 | 
						|
                    NewSegmentPacket *ppacket =
 | 
						|
                        (NewSegmentPacket *) m_List->RemoveHead();
 | 
						|
		    // inform derived class we took something off the queue
 | 
						|
		    if (m_hEventPop) {
 | 
						|
                        //DbgLog((LOG_TRACE,3,TEXT("Queue: Delivered  SET EVENT")));
 | 
						|
		        SetEvent(m_hEventPop);
 | 
						|
		    }
 | 
						|
 | 
						|
                    ASSERT(ppacket != NULL);
 | 
						|
                    delete ppacket;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for (int i = 0; i < m_nBatched; i++) {
 | 
						|
        m_ppSamples[i]->Release();
 | 
						|
    }
 | 
						|
    m_nBatched = 0;
 | 
						|
}
 | 
						|
 | 
						|
//  Notify the thread if there is something to do
 | 
						|
//
 | 
						|
//  The critical section MUST be held when this is called
 | 
						|
void COutputQueue::NotifyThread()
 | 
						|
{
 | 
						|
    //  Optimize - no need to signal if it's not waiting
 | 
						|
    ASSERT(IsQueued());
 | 
						|
    if (m_lWaiting) {
 | 
						|
        ReleaseSemaphore(m_hSem, m_lWaiting, NULL);
 | 
						|
        m_lWaiting = 0;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
//  See if there's any work to do
 | 
						|
//  Returns
 | 
						|
//      TRUE  if there is nothing on the queue and nothing in the batch
 | 
						|
//            and all data has been sent
 | 
						|
//      FALSE otherwise
 | 
						|
//
 | 
						|
BOOL COutputQueue::IsIdle()
 | 
						|
{
 | 
						|
    CAutoLock lck(this);
 | 
						|
 | 
						|
    //  We're idle if
 | 
						|
    //      there is no thread (!IsQueued()) OR
 | 
						|
    //      the thread is waiting for more work  (m_lWaiting != 0)
 | 
						|
    //  AND
 | 
						|
    //      there's nothing in the current batch (m_nBatched == 0)
 | 
						|
 | 
						|
    if (IsQueued() && m_lWaiting == 0 || m_nBatched != 0) {
 | 
						|
        return FALSE;
 | 
						|
    } else {
 | 
						|
 | 
						|
        //  If we're idle it shouldn't be possible for there
 | 
						|
        //  to be anything on the work queue
 | 
						|
 | 
						|
        ASSERT(!IsQueued() || m_List->GetCount() == 0);
 | 
						|
        return TRUE;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void COutputQueue::SetPopEvent(HANDLE hEvent)
 | 
						|
{
 | 
						|
    m_hEventPop = hEvent;
 | 
						|
}
 |