SOURCE.CPP
//==========================================================================; 
// 
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR 
//  PURPOSE. 
// 
//  Copyright (c) 1992 - 1997  Microsoft Corporation.  All Rights Reserved. 
// 
//--------------------------------------------------------------------------; 
 
// Implements CSource. A Quartz source filter 'template', March 1995 
 
// Locking Strategy. 
// 
// Hold the filter critical section (m_pFilter->pStateLock()) to serialise 
// access to functions. Note that, in general, this lock may be held 
// by a function when the worker thread may want to hold it. Therefore 
// if you wish to access shared state from the worker thread you will 
// need to add another critical section object. The execption is during 
// the threads processing loop, when it is safe to get the filter critical 
// section from within FillBuffer(). 
 
#include <streams.h> 
 
 
// 
// CSource::Constructor 
// 
// Initialise the pin count for the filter. The user will create the pins in 
// the derived class. 
CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid) 
    : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), 
      m_iPins(0), 
      m_paStreams(NULL) 
{ 
} 
 
CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr) 
    : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), 
      m_iPins(0), 
      m_paStreams(NULL) 
{ 
    UNREFERENCED_PARAMETER(phr); 
} 
 
// 
// CSource::Destructor 
// 
CSource::~CSource() 
{ 
    /*  Free our pins and pin array */ 
    while (m_iPins != 0) { 
// deleting the pins causes them to be removed from the array... 
delete m_paStreams[m_iPins - 1]; 
    } 
 
    ASSERT(m_paStreams == NULL); 
} 
 
 
// 
//  Add a new pin 
// 
HRESULT CSource::AddPin(CSourceStream *pStream) 
{ 
    CAutoLock lock(&m_cStateLock); 
 
    /*  Allocate space for this pin and the old ones */ 
    CSourceStream **paStreams = new CSourceStream *[m_iPins + 1]; 
    if (paStreams == NULL) { 
        return E_OUTOFMEMORY; 
    } 
    if (m_paStreams != NULL) { 
        CopyMemory((PVOID)paStreams, (PVOID)m_paStreams, 
                   m_iPins * sizeof(m_paStreams[0])); 
        paStreams[m_iPins] = pStream; 
        delete [] m_paStreams; 
    } 
    m_paStreams = paStreams; 
    m_paStreams[m_iPins] = pStream; 
    m_iPins++; 
    return S_OK; 
} 
 
// 
//  Remove a pin - pStream is NOT deleted 
// 
HRESULT CSource::RemovePin(CSourceStream *pStream) 
{ 
    int i; 
    for (i = 0; i < m_iPins; i++) { 
        if (m_paStreams[i] == pStream) { 
            if (m_iPins == 1) { 
                delete [] m_paStreams; 
                m_paStreams = NULL; 
            } else { 
                /*  no need to reallocate */ 
while (++i < m_iPins) 
    m_paStreams[i - 1] = m_paStreams[i]; 
            } 
            m_iPins--; 
            return S_OK; 
        } 
    } 
    return S_FALSE; 
} 
 
// 
// FindPin 
// 
// Set *ppPin to the IPin* that has the id Id. 
// or to NULL if the Id cannot be matched. 
STDMETHODIMP CSource::FindPin(LPCWSTR Id, IPin **ppPin) 
{ 
    CheckPointer(ppPin,E_POINTER); 
    ValidateReadWritePtr(ppPin,sizeof(IPin *)); 
    // The -1 undoes the +1 in QueryId and ensures that totally bogus 
    // strings (for which WstrToInt delivers 0) give a deliver a NULL pin. 
    int i = WstrToInt(Id) -1; 
    *ppPin = GetPin(i); 
    if (*ppPin!=NULL){ 
        (*ppPin)->AddRef(); 
        return NOERROR; 
    } else { 
        return VFW_E_NOT_FOUND; 
    } 
} 
 
// 
// FindPinNumber 
// 
// return the number of the pin with this IPin* or -1 if none 
int CSource::FindPinNumber(IPin *iPin) { 
    int i; 
    for (i=0; i<m_iPins; ++i) { 
        if ((IPin *)(m_paStreams[i])==iPin) { 
            return i; 
        } 
    } 
    return -1; 
} 
 
// 
// GetPinCount 
// 
// Returns the number of pins this filter has 
int CSource::GetPinCount(void) { 
 
    CAutoLock lock(&m_cStateLock); 
    return m_iPins; 
} 
 
 
// 
// GetPin 
// 
// Return a non-addref'd pointer to pin n 
// needed by CBaseFilter 
CBasePin *CSource::GetPin(int n) { 
 
    CAutoLock lock(&m_cStateLock); 
 
    // n must be in the range 0..m_iPins-1 
    // if m_iPins>n  && n>=0 it follows that m_iPins>0 
    // which is what used to be checked (i.e. checking that we have a pin) 
    if ((n >= 0) && (n < m_iPins)) { 
 
        ASSERT(m_paStreams[n]); 
return m_paStreams[n]; 
    } 
    return NULL; 
} 
 
 
// 
 
 
// * 
// * --- CSourceStream ---- 
// * 
 
// 
// Set Id to point to a CoTaskMemAlloc'd 
STDMETHODIMP CSourceStream::QueryId(LPWSTR *Id) { 
    CheckPointer(Id,E_POINTER); 
    ValidateReadWritePtr(Id,sizeof(LPWSTR)); 
 
    // We give the pins id's which are 1,2,... 
    // FindPinNumber returns -1 for a bogus pin 
    int i = 1+ m_pFilter->FindPinNumber(this); 
    if (i<1) return VFW_E_NOT_FOUND; 
    *Id = (LPWSTR)CoTaskMemAlloc(8); 
    if (*Id==NULL) { 
       return E_OUTOFMEMORY; 
    } 
    IntToWstr(i, *Id); 
    return NOERROR; 
} 
 
 
 
// 
// CSourceStream::Constructor 
// 
// increments the number of pins present on the filter 
CSourceStream::CSourceStream( 
    TCHAR *pObjectName, 
    HRESULT *phr, 
    CSource *ps, 
    LPCWSTR pPinName) 
    : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName), 
      m_pFilter(ps) { 
 
     *phr = m_pFilter->AddPin(this); 
} 
 
 
// 
// CSourceStream::Destructor 
// 
// Decrements the number of pins on this filter 
CSourceStream::~CSourceStream(void) { 
 
     m_pFilter->RemovePin(this); 
} 
 
 
// 
// CheckMediaType 
// 
// Do we support this type? Provides the default support for 1 type. 
HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) { 
 
    CAutoLock lock(m_pFilter->pStateLock()); 
 
    CMediaType mt; 
    GetMediaType(&mt); 
 
    if (mt == *pMediaType) { 
        return NOERROR; 
    } 
 
    return E_FAIL; 
} 
 
 
// 
// GetMediaType/3 
// 
// By default we support only one type 
// iPosition indexes are 0-n 
HRESULT CSourceStream::GetMediaType(int iPosition, CMediaType *pMediaType) { 
 
    CAutoLock lock(m_pFilter->pStateLock()); 
 
    if (iPosition<0) { 
        return E_INVALIDARG; 
    } 
    if (iPosition>0) { 
        return VFW_S_NO_MORE_ITEMS; 
    } 
    return GetMediaType(pMediaType); 
} 
 
 
// 
// Active 
// 
// The pin is active - start up the worker thread 
HRESULT CSourceStream::Active(void) { 
 
    CAutoLock lock(m_pFilter->pStateLock()); 
 
    HRESULT hr; 
 
    if (m_pFilter->IsActive()) { 
return S_FALSE;// succeeded, but did not allocate resources (they already exist...) 
    } 
 
    // do nothing if not connected - its ok not to connect to 
    // all pins of a source filter 
    if (!IsConnected()) { 
        return NOERROR; 
    } 
 
    hr = CBaseOutputPin::Active(); 
    if (FAILED(hr)) { 
        return hr; 
    } 
 
    ASSERT(!ThreadExists()); 
 
    // start the thread 
    if (!Create()) { 
        return E_FAIL; 
    } 
 
    // Tell thread to initialize. If OnThreadCreate Fails, so does this. 
    hr = Init(); 
    if (FAILED(hr)) 
return hr; 
 
    return Pause(); 
} 
 
 
// 
// Inactive 
// 
// Pin is inactive - shut down the worker thread 
// Waits for the worker to exit before returning. 
HRESULT CSourceStream::Inactive(void) { 
 
    CAutoLock lock(m_pFilter->pStateLock()); 
 
    HRESULT hr; 
 
    // do nothing if not connected - its ok not to connect to 
    // all pins of a source filter 
    if (!IsConnected()) { 
        return NOERROR; 
    } 
 
    // !!! need to do this before trying to stop the thread, because 
    // we may be stuck waiting for our own allocator!!! 
 
    hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator 
    if (FAILED(hr)) { 
return hr; 
    } 
 
    if (ThreadExists()) { 
hr = Stop(); 
 
if (FAILED(hr)) { 
    return hr; 
} 
 
hr = Exit(); 
if (FAILED(hr)) { 
    return hr; 
} 
 
Close();// Wait for the thread to exit, then tidy up. 
    } 
 
    // hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator 
    //if (FAILED(hr)) { 
    //return hr; 
    //} 
 
    return NOERROR; 
} 
 
 
// 
// ThreadProc 
// 
// When this returns the thread exits 
// Return codes > 0 indicate an error occured 
DWORD CSourceStream::ThreadProc(void) { 
 
    HRESULT hr;  // the return code from calls 
    Command com; 
 
    do { 
com = GetRequest(); 
if (com != CMD_INIT) { 
    DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command"))); 
    Reply((DWORD) E_UNEXPECTED); 
} 
    } while (com != CMD_INIT); 
 
    DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing"))); 
 
    hr = OnThreadCreate(); // perform set up tasks 
    if (FAILED(hr)) { 
        DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread."))); 
        OnThreadDestroy(); 
Reply(hr);// send failed return code from OnThreadCreate 
        return 1; 
    } 
 
    // Initialisation suceeded 
    Reply(NOERROR); 
 
    Command cmd; 
    do { 
cmd = GetRequest(); 
 
switch (cmd) { 
 
case CMD_EXIT: 
    Reply(NOERROR); 
    break; 
 
case CMD_RUN: 
    DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???"))); 
    // !!! fall through??? 
 
case CMD_PAUSE: 
    Reply(NOERROR); 
    DoBufferProcessingLoop(); 
    break; 
 
case CMD_STOP: 
    Reply(NOERROR); 
    break; 
 
default: 
    DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd)); 
    Reply((DWORD) E_NOTIMPL); 
    break; 
} 
    } while (cmd != CMD_EXIT); 
 
    hr = OnThreadDestroy();// tidy up. 
    if (FAILED(hr)) { 
        DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread."))); 
        return 1; 
    } 
 
    DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting"))); 
    return 0; 
} 
 
 
// 
// DoBufferProcessingLoop 
// 
// Grabs a buffer and calls the users processing function. 
// Overridable, so that different delivery styles can be catered for. 
HRESULT CSourceStream::DoBufferProcessingLoop(void) { 
 
    Command com; 
 
    OnThreadStartPlay(); 
 
    do { 
while (!CheckRequest(&com)) { 
 
    IMediaSample *pSample; 
 
    HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0); 
    if (FAILED(hr)) { 
                Sleep(1); 
continue;// go round again. Perhaps the error will go away 
    // or the allocator is decommited & we will be asked to 
    // exit soon. 
    } 
 
    // Virtual function user will override. 
    hr = FillBuffer(pSample); 
 
    if (hr == S_OK) { 
hr = Deliver(pSample); 
                pSample->Release(); 
 
                // downstream filter returns S_FALSE if it wants us to 
                // stop or an error if it's reporting an error. 
                if(hr != S_OK) 
                { 
                  DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr)); 
                  return S_OK; 
                } 
 
    } else if (hr == S_FALSE) { 
                // derived class wants us to stop pushing data 
pSample->Release(); 
DeliverEndOfStream(); 
return S_OK; 
    } else { 
                // derived class encountered an error 
                pSample->Release(); 
DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr)); 
                DeliverEndOfStream(); 
                m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0); 
                return hr; 
    } 
 
            // all paths release the sample 
} 
 
        // For all commands sent to us there must be a Reply call! 
 
if (com == CMD_RUN || com == CMD_PAUSE) { 
    Reply(NOERROR); 
} else if (com != CMD_STOP) { 
    Reply((DWORD) E_UNEXPECTED); 
    DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!"))); 
} 
    } while (com != CMD_STOP); 
 
    return S_FALSE; 
}