123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588 |
- //------------------------------------------------------------------------------
- // 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;
- }
- }
- }
|