//------------------------------------------------------------------------------ | |
// File: Source.cpp | |
// | |
// Desc: DirectShow base classes - implements CSource, which is a Quartz | |
// source filter 'template.' | |
// | |
// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved. | |
//------------------------------------------------------------------------------ | |
// 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(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid) | |
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid), | |
m_iPins(0), | |
m_paStreams(NULL) | |
{ | |
} | |
CSource::CSource(__in_opt LPCTSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr) | |
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid), | |
m_iPins(0), | |
m_paStreams(NULL) | |
{ | |
UNREFERENCED_PARAMETER(phr); | |
} | |
#ifdef UNICODE | |
CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid) | |
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid), | |
m_iPins(0), | |
m_paStreams(NULL) | |
{ | |
} | |
CSource::CSource(__in_opt LPCSTR pName, __inout_opt LPUNKNOWN lpunk, CLSID clsid, __inout HRESULT *phr) | |
: CBaseFilter(pName, lpunk, &m_cStateLock, clsid), | |
m_iPins(0), | |
m_paStreams(NULL) | |
{ | |
UNREFERENCED_PARAMETER(phr); | |
} | |
#endif | |
// | |
// 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(__in 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(__in 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, __deref_out IPin **ppPin) | |
{ | |
CheckPointer(ppPin,E_POINTER); | |
ValidateReadWritePtr(ppPin,sizeof(IPin *)); | |
// The -1 undoes the +1 in QueryId and ensures that totally invalid | |
// 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(__in 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(__deref_out LPWSTR *Id) { | |
CheckPointer(Id,E_POINTER); | |
ValidateReadWritePtr(Id,sizeof(LPWSTR)); | |
// We give the pins id's which are 1,2,... | |
// FindPinNumber returns -1 for an invalid pin | |
int i = 1+ m_pFilter->FindPinNumber(this); | |
if (i<1) return VFW_E_NOT_FOUND; | |
*Id = (LPWSTR)CoTaskMemAlloc(sizeof(WCHAR) * 12); | |
if (*Id==NULL) { | |
return E_OUTOFMEMORY; | |
} | |
IntToWstr(i, *Id); | |
return NOERROR; | |
} | |
// | |
// CSourceStream::Constructor | |
// | |
// increments the number of pins present on the filter | |
CSourceStream::CSourceStream( | |
__in_opt LPCTSTR pObjectName, | |
__inout HRESULT *phr, | |
__inout CSource *ps, | |
__in_opt LPCWSTR pPinName) | |
: CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName), | |
m_pFilter(ps) { | |
*phr = m_pFilter->AddPin(this); | |
} | |
#ifdef UNICODE | |
CSourceStream::CSourceStream( | |
__in_opt LPCSTR pObjectName, | |
__inout HRESULT *phr, | |
__inout CSource *ps, | |
__in_opt LPCWSTR pPinName) | |
: CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName), | |
m_pFilter(ps) { | |
*phr = m_pFilter->AddPin(this); | |
} | |
#endif | |
// | |
// 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, __inout 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; | |
} | |