589 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			589 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //------------------------------------------------------------------------------
 | |
| // 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;
 | |
| 	}
 | |
|     }
 | |
| }
 |