|
|
//------------------------------------------------------------------------------
// File: Source.cpp
//
// Desc: DirectShow base classes - implements CSource, which is a Quartz
// source filter 'template.'
//
//@@BEGIN_MSINTERNAL
//
// March 1995
//
//@@END_MSINTERNAL
// 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(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); }
#ifdef UNICODE
CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid) : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), m_iPins(0), m_paStreams(NULL) { }
CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, 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(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 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(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 an invalid 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); }
#ifdef UNICODE
CSourceStream::CSourceStream( char *pObjectName, HRESULT *phr, CSource *ps, 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, 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; }
|