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; }
// If the presenter is waiting to frame-step, this method starts the frame-step operation. HRESULT EVRCustomPresenter::StartFrameStep() { assert(m_RenderState == RENDER_STATE_STARTED); HRESULT hr = S_OK; IMFSample *pSample = NULL; if (m_FrameStep.state == FRAMESTEP_WAITING_START) { // We have a frame-step request, and are waiting for the clock to start. // Set the state to "pending," which means we are waiting for samples. m_FrameStep.state = FRAMESTEP_PENDING; // If the frame-step queue already has samples, process them now. // We break the loop when the frame-step queue is empty or the frame-step operation is complete while (!m_FrameStep.samples.IsEmpty() && (m_FrameStep.state == FRAMESTEP_PENDING)) { hr = m_FrameStep.samples.RemoveFront(&pSample); if (FAILED(hr)) { SAFE_RELEASE(pSample) CHECK_HR(hr, "EVRCustomPresenter::StartFrameStep VideoSampleList::RemoveFront() failed"); } hr = DeliverFrameStepSample(pSample); if (FAILED(hr)) { SAFE_RELEASE(pSample) CHECK_HR(hr, "EVRCustomPresenter::StartFrameStep EVRCustomPresenter::DeliverFrameStepSample() failed"); } SAFE_RELEASE(pSample); } } else if (m_FrameStep.state == FRAMESTEP_NONE) { // We are not frame stepping. Therefore, if the frame-step queue has samples, we need to process them normally. while (!m_FrameStep.samples.IsEmpty()) { hr = m_FrameStep.samples.RemoveFront(&pSample); if (FAILED(hr)) { SAFE_RELEASE(pSample) CHECK_HR(hr, "EVRCustomPresenter::StartFrameStep VideoSampleList::RemoveFront() failed"); } hr = DeliverSample(pSample, FALSE); if (FAILED(hr)) { SAFE_RELEASE(pSample) CHECK_HR(hr, "EVRCustomPresenter::StartFrameStep EVRCustomPresenter::DeliverSample() failed"); } SAFE_RELEASE(pSample); } } return hr; }
HRESULT WavStream::DeliverQueuedSamples() { HRESULT hr = S_OK; IMFSample *pSample = NULL; EnterCriticalSection(&m_critSec); // If we already reached the end of the stream, send the MEEndStream // event again. if (m_EOS) { hr = QueueEvent(MEEndOfStream, GUID_NULL, S_OK, NULL); } if (SUCCEEDED(hr)) { // Deliver any queued samples. while (!m_sampleQueue.IsEmpty()) { hr = m_sampleQueue.Dequeue(&pSample); if (FAILED(hr)) { break; } hr = DeliverSample(pSample); if (FAILED(hr)) { break; } SafeRelease(&pSample); } } LeaveCriticalSection(&m_critSec); // If we reached the end of the stream, send the end-of-presentation event from // the media source. if (SUCCEEDED(hr)) { if (m_EOS) { hr = m_pSource->QueueEvent(MEEndOfPresentation, GUID_NULL, S_OK, NULL); } } SafeRelease(&pSample); return hr; }
// Process a video sample for frame-stepping. HRESULT EVRCustomPresenter::DeliverFrameStepSample(IMFSample *pSample) { HRESULT hr = S_OK; IUnknown *pUnk = NULL; // For rate 0, discard any sample that ends earlier than the clock time. if (IsScrubbing() && m_pClock && IsSampleTimePassed(m_pClock, pSample)) { // Discard this sample. } else if (m_FrameStep.state >= FRAMESTEP_SCHEDULED) { // A frame was already submitted. Put this sample on the frame-step queue, // in case we are asked to step to the next frame. If frame-stepping is // cancelled, this sample will be processed normally. hr = m_FrameStep.samples.InsertBack(pSample); CHECK_HR(hr, "EVRCustomPresenter::DeliverFrameStepSample VideoSampleList::InsertBack() failed"); } else { // We're ready to frame-step. // Decrement the number of steps. if (m_FrameStep.steps > 0) { m_FrameStep.steps--; } if (m_FrameStep.steps > 0) { // This is not the last step. Discard this sample. } else if (m_FrameStep.state == FRAMESTEP_WAITING_START) { // This is the right frame, but the clock hasn't started yet. Put the // sample on the frame-step queue. When the clock starts, the sample // will be processed. hr = m_FrameStep.samples.InsertBack(pSample); CHECK_HR(hr, "EVRCustomPresenter::DeliverFrameStepSample VideoSampleList::InsertBack() failed"); } else { // This is the right frame *and* the clock has started. Deliver this sample. hr = DeliverSample(pSample, FALSE); CHECK_HR(hr, "EVRCustomPresenter::DeliverFrameStepSample EVRCustomPresenter::DeliverSample() failed"); // QI for IUnknown so that we can identify the sample later. // (Per COM rules, an object alwayss return the same pointer when QI'ed for IUnknown.) hr = pSample->QueryInterface(__uuidof(IUnknown), (void**)&pUnk); if (FAILED(hr)) { SAFE_RELEASE(pUnk); CHECK_HR(hr, "EVRCustomPresenter::DeliverFrameStempSample IMFSample::QueryInterface() failed"); } // Save this value. m_FrameStep.pSampleNoRef = (DWORD_PTR)pUnk; // No add-ref. // NOTE: We do not AddRef the IUnknown pointer, because that would prevent the // sample from invoking the OnSampleFree callback after the sample is presented. // We use this IUnknown pointer purely to identify the sample later; we never // attempt to dereference the pointer. // Update our state. m_FrameStep.state = FRAMESTEP_SCHEDULED; } } SAFE_RELEASE(pUnk); return hr; }
HRESULT WavStream::RequestSample(IUnknown* pToken) { if (m_pSource == NULL) { return E_UNEXPECTED; } HRESULT hr = S_OK; IMFMediaSource *pSource = NULL; IMFSample *pSample = NULL; // Sample to deliver. EnterCriticalSection(&m_critSec); // Check if we are shut down. hr = CheckShutdown(); // Check if we already reached the end of the stream. if (SUCCEEDED(hr)) { if (m_EOS) { hr = MF_E_END_OF_STREAM; } } // Check the source is stopped. // GetState does not hold the source's critical section. Safe to call. if (SUCCEEDED(hr)) { if (m_pSource->GetState() == WavSource::STATE_STOPPED) { hr = MF_E_INVALIDREQUEST; } } if (SUCCEEDED(hr)) { // Create a new audio sample. hr = CreateAudioSample(&pSample); } if (SUCCEEDED(hr)) { // If the caller provided a token, attach it to the sample as // an attribute. // NOTE: If we processed sample requests asynchronously, we would // need to call AddRef on the token and put the token onto a FIFO // queue. See documenation for IMFMediaStream::RequestSample. if (pToken) { hr = pSample->SetUnknown(MFSampleExtension_Token, pToken); } } // If paused, queue the sample for later delivery. Otherwise, deliver the sample now. if (SUCCEEDED(hr)) { if (m_pSource->GetState() == WavSource::STATE_PAUSED) { hr = m_sampleQueue.Queue(pSample); } else { hr = DeliverSample(pSample); } } // Cache a pointer to the source, prior to leaving the critical section. if (SUCCEEDED(hr)) { pSource = m_pSource; pSource->AddRef(); } LeaveCriticalSection(&m_critSec); // We only have one stream, so the end of the stream is also the end of the // presentation. Therefore, when we reach the end of the stream, we need to // queue the end-of-presentation event from the source. Logically we would do // this inside the CheckEndOfStream method. However, we cannot hold the // source's critical section while holding the stream's critical section, at // risk of deadlock. if (SUCCEEDED(hr)) { if (m_EOS) { hr = pSource->QueueEvent(MEEndOfPresentation, GUID_NULL, S_OK, NULL); } } SafeRelease(&pSample); SafeRelease(&pSource); 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(); }