summaryrefslogtreecommitdiff
path: root/third_party/BaseClasses/source.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/BaseClasses/source.cpp')
-rw-r--r--third_party/BaseClasses/source.cpp522
1 files changed, 522 insertions, 0 deletions
diff --git a/third_party/BaseClasses/source.cpp b/third_party/BaseClasses/source.cpp
new file mode 100644
index 00000000..ef7795c4
--- /dev/null
+++ b/third_party/BaseClasses/source.cpp
@@ -0,0 +1,522 @@
+//------------------------------------------------------------------------------
+// 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;
+}
+