summaryrefslogtreecommitdiff
path: root/third_party/BaseClasses/pullpin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/BaseClasses/pullpin.cpp')
-rw-r--r--third_party/BaseClasses/pullpin.cpp588
1 files changed, 588 insertions, 0 deletions
diff --git a/third_party/BaseClasses/pullpin.cpp b/third_party/BaseClasses/pullpin.cpp
new file mode 100644
index 00000000..a197ba58
--- /dev/null
+++ b/third_party/BaseClasses/pullpin.cpp
@@ -0,0 +1,588 @@
+//------------------------------------------------------------------------------
+// File: PullPin.cpp
+//
+// Desc: DirectShow base classes - implements CPullPin class that pulls data
+// from IAsyncReader.
+//
+// Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------------------------
+
+
+#include <streams.h>
+#include "pullpin.h"
+
+#ifdef DXMPERF
+#include "dxmperf.h"
+#endif // DXMPERF
+
+
+CPullPin::CPullPin()
+ : m_pReader(NULL),
+ m_pAlloc(NULL),
+ m_State(TM_Exit)
+{
+#ifdef DXMPERF
+ PERFLOG_CTOR( L"CPullPin", this );
+#endif // DXMPERF
+
+}
+
+CPullPin::~CPullPin()
+{
+ Disconnect();
+
+#ifdef DXMPERF
+ PERFLOG_DTOR( L"CPullPin", this );
+#endif // DXMPERF
+
+}
+
+// returns S_OK if successfully connected to an IAsyncReader interface
+// from this object
+// Optional allocator should be proposed as a preferred allocator if
+// necessary
+HRESULT
+CPullPin::Connect(IUnknown* pUnk, IMemAllocator* pAlloc, BOOL bSync)
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (m_pReader) {
+ return VFW_E_ALREADY_CONNECTED;
+ }
+
+ HRESULT hr = pUnk->QueryInterface(IID_IAsyncReader, (void**)&m_pReader);
+ if (FAILED(hr)) {
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, hr, pmt );
+ }
+#endif // DXMPERF
+
+ return(hr);
+ }
+
+ hr = DecideAllocator(pAlloc, NULL);
+ if (FAILED(hr)) {
+ Disconnect();
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, hr, pmt );
+ }
+#endif // DXMPERF
+
+ return hr;
+ }
+
+ LONGLONG llTotal, llAvail;
+ hr = m_pReader->Length(&llTotal, &llAvail);
+ if (FAILED(hr)) {
+ Disconnect();
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, hr, pmt );
+ }
+#endif
+
+ return hr;
+ }
+
+ // convert from file position to reference time
+ m_tDuration = llTotal * UNITS;
+ m_tStop = m_tDuration;
+ m_tStart = 0;
+
+ m_bSync = bSync;
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ PERFLOG_CONNECT( this, pUnk, S_OK, pmt );
+ }
+#endif // DXMPERF
+
+
+ return S_OK;
+}
+
+// disconnect any connection made in Connect
+HRESULT
+CPullPin::Disconnect()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ StopThread();
+
+
+#ifdef DXMPERF
+ PERFLOG_DISCONNECT( this, m_pReader, S_OK );
+#endif // DXMPERF
+
+
+ if (m_pReader) {
+ m_pReader->Release();
+ m_pReader = NULL;
+ }
+
+ if (m_pAlloc) {
+ m_pAlloc->Release();
+ m_pAlloc = NULL;
+ }
+
+ return S_OK;
+}
+
+// agree an allocator using RequestAllocator - optional
+// props param specifies your requirements (non-zero fields).
+// returns an error code if fail to match requirements.
+// optional IMemAllocator interface is offered as a preferred allocator
+// but no error occurs if it can't be met.
+HRESULT
+CPullPin::DecideAllocator(
+ IMemAllocator * pAlloc,
+ __inout_opt ALLOCATOR_PROPERTIES * pProps)
+{
+ ALLOCATOR_PROPERTIES *pRequest;
+ ALLOCATOR_PROPERTIES Request;
+ if (pProps == NULL) {
+ Request.cBuffers = 3;
+ Request.cbBuffer = 64*1024;
+ Request.cbAlign = 0;
+ Request.cbPrefix = 0;
+ pRequest = &Request;
+ } else {
+ pRequest = pProps;
+ }
+ HRESULT hr = m_pReader->RequestAllocator(
+ pAlloc,
+ pRequest,
+ &m_pAlloc);
+ return hr;
+}
+
+// start pulling data
+HRESULT
+CPullPin::Active(void)
+{
+ ASSERT(!ThreadExists());
+ return StartThread();
+}
+
+// stop pulling data
+HRESULT
+CPullPin::Inactive(void)
+{
+ StopThread();
+
+ return S_OK;
+}
+
+HRESULT
+CPullPin::Seek(REFERENCE_TIME tStart, REFERENCE_TIME tStop)
+{
+ CAutoLock lock(&m_AccessLock);
+
+ ThreadMsg AtStart = m_State;
+
+ if (AtStart == TM_Start) {
+ BeginFlush();
+ PauseThread();
+ EndFlush();
+ }
+
+ m_tStart = tStart;
+ m_tStop = tStop;
+
+ HRESULT hr = S_OK;
+ if (AtStart == TM_Start) {
+ hr = StartThread();
+ }
+
+ return hr;
+}
+
+HRESULT
+CPullPin::Duration(__out REFERENCE_TIME* ptDuration)
+{
+ *ptDuration = m_tDuration;
+ return S_OK;
+}
+
+
+HRESULT
+CPullPin::StartThread()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (!m_pAlloc || !m_pReader) {
+ return E_UNEXPECTED;
+ }
+
+ HRESULT hr;
+ if (!ThreadExists()) {
+
+ // commit allocator
+ hr = m_pAlloc->Commit();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ // start thread
+ if (!Create()) {
+ return E_FAIL;
+ }
+ }
+
+ m_State = TM_Start;
+ hr = (HRESULT) CallWorker(m_State);
+ return hr;
+}
+
+HRESULT
+CPullPin::PauseThread()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (!ThreadExists()) {
+ return E_UNEXPECTED;
+ }
+
+ // need to flush to ensure the thread is not blocked
+ // in WaitForNext
+ HRESULT hr = m_pReader->BeginFlush();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ m_State = TM_Pause;
+ hr = CallWorker(TM_Pause);
+
+ m_pReader->EndFlush();
+ return hr;
+}
+
+HRESULT
+CPullPin::StopThread()
+{
+ CAutoLock lock(&m_AccessLock);
+
+ if (!ThreadExists()) {
+ return S_FALSE;
+ }
+
+ // need to flush to ensure the thread is not blocked
+ // in WaitForNext
+ HRESULT hr = m_pReader->BeginFlush();
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ m_State = TM_Exit;
+ hr = CallWorker(TM_Exit);
+
+ m_pReader->EndFlush();
+
+ // wait for thread to completely exit
+ Close();
+
+ // decommit allocator
+ if (m_pAlloc) {
+ m_pAlloc->Decommit();
+ }
+
+ return S_OK;
+}
+
+
+DWORD
+CPullPin::ThreadProc(void)
+{
+ while(1) {
+ DWORD cmd = GetRequest();
+ switch(cmd) {
+ case TM_Exit:
+ Reply(S_OK);
+ return 0;
+
+ case TM_Pause:
+ // we are paused already
+ Reply(S_OK);
+ break;
+
+ case TM_Start:
+ Reply(S_OK);
+ Process();
+ break;
+ }
+
+ // at this point, there should be no outstanding requests on the
+ // upstream filter.
+ // We should force begin/endflush to ensure that this is true.
+ // !!!Note that we may currently be inside a BeginFlush/EndFlush pair
+ // on another thread, but the premature EndFlush will do no harm now
+ // that we are idle.
+ m_pReader->BeginFlush();
+ CleanupCancelled();
+ m_pReader->EndFlush();
+ }
+}
+
+HRESULT
+CPullPin::QueueSample(
+ __inout REFERENCE_TIME& tCurrent,
+ REFERENCE_TIME tAlignStop,
+ BOOL bDiscontinuity
+ )
+{
+ IMediaSample* pSample;
+
+ HRESULT hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
+ if (FAILED(hr)) {
+ return hr;
+ }
+
+ LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
+ if (tStopThis > tAlignStop) {
+ tStopThis = tAlignStop;
+ }
+ pSample->SetTime(&tCurrent, &tStopThis);
+ tCurrent = tStopThis;
+
+ pSample->SetDiscontinuity(bDiscontinuity);
+
+ hr = m_pReader->Request(
+ pSample,
+ 0);
+ if (FAILED(hr)) {
+ pSample->Release();
+
+ CleanupCancelled();
+ OnError(hr);
+ }
+ return hr;
+}
+
+HRESULT
+CPullPin::CollectAndDeliver(
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop)
+{
+ IMediaSample* pSample = NULL; // better be sure pSample is set
+ DWORD_PTR dwUnused;
+ HRESULT hr = m_pReader->WaitForNext(
+ INFINITE,
+ &pSample,
+ &dwUnused);
+ if (FAILED(hr)) {
+ if (pSample) {
+ pSample->Release();
+ }
+ } else {
+ hr = DeliverSample(pSample, tStart, tStop);
+ }
+ if (FAILED(hr)) {
+ CleanupCancelled();
+ OnError(hr);
+ }
+ return hr;
+
+}
+
+HRESULT
+CPullPin::DeliverSample(
+ IMediaSample* pSample,
+ REFERENCE_TIME tStart,
+ REFERENCE_TIME tStop
+ )
+{
+ // fix up sample if past actual stop (for sector alignment)
+ REFERENCE_TIME t1, t2;
+ if (S_OK == pSample->GetTime(&t1, &t2)) {
+ if (t2 > tStop) {
+ t2 = tStop;
+ }
+
+ // adjust times to be relative to (aligned) start time
+ t1 -= tStart;
+ t2 -= tStart;
+ HRESULT hr = pSample->SetTime(&t1, &t2);
+ if (FAILED(hr)) {
+ return hr;
+ }
+ }
+
+#ifdef DXMPERF
+ {
+ AM_MEDIA_TYPE * pmt = NULL;
+ pSample->GetMediaType( &pmt );
+ PERFLOG_RECEIVE( L"CPullPin", m_pReader, this, pSample, pmt );
+ }
+#endif
+
+ HRESULT hr = Receive(pSample);
+ pSample->Release();
+ return hr;
+}
+
+void
+CPullPin::Process(void)
+{
+ // is there anything to do?
+ if (m_tStop <= m_tStart) {
+ EndOfStream();
+ return;
+ }
+
+ BOOL bDiscontinuity = TRUE;
+
+ // if there is more than one sample at the allocator,
+ // then try to queue 2 at once in order to overlap.
+ // -- get buffer count and required alignment
+ ALLOCATOR_PROPERTIES Actual;
+ HRESULT hr = m_pAlloc->GetProperties(&Actual);
+
+ // align the start position downwards
+ REFERENCE_TIME tStart = AlignDown(m_tStart / UNITS, Actual.cbAlign) * UNITS;
+ REFERENCE_TIME tCurrent = tStart;
+
+ REFERENCE_TIME tStop = m_tStop;
+ if (tStop > m_tDuration) {
+ tStop = m_tDuration;
+ }
+
+ // align the stop position - may be past stop, but that
+ // doesn't matter
+ REFERENCE_TIME tAlignStop = AlignUp(tStop / UNITS, Actual.cbAlign) * UNITS;
+
+
+ DWORD dwRequest;
+
+ if (!m_bSync) {
+
+ // Break out of the loop either if we get to the end or we're asked
+ // to do something else
+ while (tCurrent < tAlignStop) {
+
+ // Break out without calling EndOfStream if we're asked to
+ // do something different
+ if (CheckRequest(&dwRequest)) {
+ return;
+ }
+
+ // queue a first sample
+ if (Actual.cBuffers > 1) {
+
+ hr = QueueSample(tCurrent, tAlignStop, TRUE);
+ bDiscontinuity = FALSE;
+
+ if (FAILED(hr)) {
+ return;
+ }
+ }
+
+
+
+ // loop queueing second and waiting for first..
+ while (tCurrent < tAlignStop) {
+
+ hr = QueueSample(tCurrent, tAlignStop, bDiscontinuity);
+ bDiscontinuity = FALSE;
+
+ if (FAILED(hr)) {
+ return;
+ }
+
+ hr = CollectAndDeliver(tStart, tStop);
+ if (S_OK != hr) {
+
+ // stop if error, or if downstream filter said
+ // to stop.
+ return;
+ }
+ }
+
+ if (Actual.cBuffers > 1) {
+ hr = CollectAndDeliver(tStart, tStop);
+ if (FAILED(hr)) {
+ return;
+ }
+ }
+ }
+ } else {
+
+ // sync version of above loop
+ while (tCurrent < tAlignStop) {
+
+ // Break out without calling EndOfStream if we're asked to
+ // do something different
+ if (CheckRequest(&dwRequest)) {
+ return;
+ }
+
+ IMediaSample* pSample;
+
+ hr = m_pAlloc->GetBuffer(&pSample, NULL, NULL, 0);
+ if (FAILED(hr)) {
+ OnError(hr);
+ return;
+ }
+
+ LONGLONG tStopThis = tCurrent + (pSample->GetSize() * UNITS);
+ if (tStopThis > tAlignStop) {
+ tStopThis = tAlignStop;
+ }
+ pSample->SetTime(&tCurrent, &tStopThis);
+ tCurrent = tStopThis;
+
+ if (bDiscontinuity) {
+ pSample->SetDiscontinuity(TRUE);
+ bDiscontinuity = FALSE;
+ }
+
+ hr = m_pReader->SyncReadAligned(pSample);
+
+ if (FAILED(hr)) {
+ pSample->Release();
+ OnError(hr);
+ return;
+ }
+
+ hr = DeliverSample(pSample, tStart, tStop);
+ if (hr != S_OK) {
+ if (FAILED(hr)) {
+ OnError(hr);
+ }
+ return;
+ }
+ }
+ }
+
+ EndOfStream();
+}
+
+// after a flush, cancelled i/o will be waiting for collection
+// and release
+void
+CPullPin::CleanupCancelled(void)
+{
+ while (1) {
+ IMediaSample * pSample;
+ DWORD_PTR dwUnused;
+
+ HRESULT hr = m_pReader->WaitForNext(
+ 0, // no wait
+ &pSample,
+ &dwUnused);
+ if(pSample) {
+ pSample->Release();
+ } else {
+ // no more samples
+ return;
+ }
+ }
+}