forked from LeenkxTeam/LNXSDK
Update Files
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user