// Completes a frame-step operation. HRESULT EVRCustomPresenter::CompleteFrameStep(IMFSample *pSample) { HRESULT hr = S_OK; MFTIME hnsSampleTime = 0; MFTIME hnsSystemTime = 0; // Update our state. m_FrameStep.state = FRAMESTEP_COMPLETE; m_FrameStep.pSampleNoRef = NULL; // Notify the EVR that the frame-step is complete. NotifyEvent(EC_STEP_COMPLETE, FALSE, 0); // FALSE = completed (not cancelled) // If we are scrubbing (rate == 0), also send the "scrub time" event. if (IsScrubbing()) { // Get the time stamp from the sample. hr = pSample->GetSampleTime(&hnsSampleTime); if (FAILED(hr)) { // No time stamp. Use the current presentation time. if (m_pClock) { hr = m_pClock->GetCorrelatedTime(0, &hnsSampleTime, &hnsSystemTime); } hr = S_OK; // Not an error condition. } NotifyEvent(EC_SCRUB_TIME, LODWORD(hnsSampleTime), HIDWORD(hnsSampleTime)); } return hr; }
void Scrubber::OnActivateOrDeactivateApp(wxActivateEvent &event) { if (event.GetActive()) mScrubHasFocus = IsScrubbing(); else mScrubHasFocus = false; event.Skip(); }
bool Scrubber::ShouldDrawScrubSpeed() { return IsScrubbing() && mScrubHasFocus && ( // Draw for (non-scroll) scrub, sometimes, but never for seek (!PollIsSeeking() && mScrubSpeedDisplayCountdown > 0) #ifdef EXPERIMENTAL_SCRUBBING_SMOOTH_SCROLL // Draw always for scroll-scrub and for scroll-seek || mSmoothScrollingScrub #endif ); }
void Scrubber::StopScrubbing() { mScrubStartPosition = -1; mSmoothScrollingScrub = false; const auto ctb = mProject->GetControlToolBar(); if (IsScrubbing()) { if (gAudioIO->IsBusy()) { ctb->StopPlaying(); } } else { // Didn't really play, but did change button apperance ctb->SetPlay(false, ControlToolBar::PlayAppearance::Straight); } }
// 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; }
bool Scrubber::MaybeStartScrubbing(const wxMouseEvent &event) { if (mScrubStartPosition < 0) return false; if (IsScrubbing()) return false; else { const bool busy = gAudioIO->IsBusy(); if (busy && gAudioIO->GetNumCaptureChannels() > 0) { // Do not stop recording, and don't try to start scrubbing after // recording stops mScrubStartPosition = -1; return false; } wxCoord position = event.m_x; if (abs(mScrubStartPosition - position) >= SCRUBBING_PIXEL_TOLERANCE) { const ViewInfo &viewInfo = mProject->GetViewInfo(); TrackPanel *const trackPanel = mProject->GetTrackPanel(); ControlToolBar * const ctb = mProject->GetControlToolBar(); double maxTime = mProject->GetTracks()->GetEndTime(); const int leftOffset = trackPanel->GetLeftOffset(); double time0 = std::min(maxTime, viewInfo.PositionToTime(mScrubStartPosition, leftOffset) ); double time1 = std::min(maxTime, viewInfo.PositionToTime(position, leftOffset) ); if (time1 != time0) { if (busy) ctb->StopPlaying(); AudioIOStartStreamOptions options(mProject->GetDefaultPlayOptions()); options.timeTrack = NULL; options.scrubDelay = (kTimerInterval / 1000.0); options.scrubStartClockTimeMillis = mScrubStartClockTimeMillis; options.minScrubStutter = 0.2; #if 0 if (!mAlwaysSeeking) { // Take the starting speed limit from the transcription toolbar, // but it may be varied during the scrub. mMaxScrubSpeed = options.maxScrubSpeed = p->GetTranscriptionToolBar()->GetPlaySpeed(); } #else // That idea seems unpopular... just make it one mMaxScrubSpeed = options.maxScrubSpeed = 1.0; #endif options.maxScrubTime = mProject->GetTracks()->GetEndTime(); ControlToolBar::PlayAppearance appearance = ControlToolBar::PlayAppearance::Scrub; const bool cutPreview = false; const bool backwards = time1 < time0; #ifdef EXPERIMENTAL_SCRUBBING_SCROLL_WHEEL static const double maxScrubSpeedBase = pow(2.0, 1.0 / ScrubSpeedStepsPerOctave); mLogMaxScrubSpeed = floor(0.5 + log(mMaxScrubSpeed) / log(maxScrubSpeedBase) ); #endif mScrubSpeedDisplayCountdown = 0; mScrubToken = ctb->PlayPlayRegion(SelectedRegion(time0, time1), options, PlayMode::normalPlay, appearance, backwards); } } else // Wait to test again mScrubStartClockTimeMillis = ::wxGetLocalTimeMillis(); if (IsScrubbing()) mScrubHasFocus = true; // Return true whether we started scrub, or are still waiting to decide. return true; } }