void SystemClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo) { TimeStamp now = TimeStamp::Now(); aFrom = mIterationStart = IterationEnd(); aTo = mIterationEnd = mGraphImpl->SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + IterationEnd(); mCurrentTimeStamp = now; PR_LOG(gMediaStreamGraphLog, PR_LOG_DEBUG+1, ("Updating current time to %f (real %f, mStateComputedTime %f)", mGraphImpl->MediaTimeToSeconds(aTo), (now - mInitialTimeStamp).ToSeconds(), mGraphImpl->MediaTimeToSeconds(StateComputedTime()))); if (mStateComputedTime < aTo) { STREAM_LOG(PR_LOG_WARNING, ("Media graph global underrun detected")); aTo = mIterationEnd = mStateComputedTime; } if (aFrom >= aTo) { NS_ASSERTION(aFrom == aTo , "Time can't go backwards!"); // This could happen due to low clock resolution, maybe? STREAM_LOG(PR_LOG_DEBUG, ("Time did not advance")); } }
void ThreadedDriver::RunThread() { AutoProfilerUnregisterThread autoUnregister; bool stillProcessing = true; while (stillProcessing) { GraphTime prevCurrentTime, nextCurrentTime; GetIntervalForIteration(prevCurrentTime, nextCurrentTime); mStateComputedTime = mNextStateComputedTime; mNextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock( nextCurrentTime + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS)); STREAM_LOG(PR_LOG_DEBUG, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)mStateComputedTime, (long)mNextStateComputedTime)); stillProcessing = mGraphImpl->OneIteration(prevCurrentTime, nextCurrentTime, StateComputedTime(), mNextStateComputedTime); if (mNextDriver && stillProcessing) { STREAM_LOG(PR_LOG_DEBUG, ("Switching to AudioCallbackDriver")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd, mStateComputedTime, mNextStateComputedTime); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); return; } } }
void ThreadedDriver::RunThread() { AutoProfilerUnregisterThread autoUnregister; bool stillProcessing = true; while (stillProcessing) { mIterationStart = IterationEnd(); mIterationEnd += GetIntervalForIteration(); GraphTime stateComputedTime = StateComputedTime(); if (stateComputedTime < mIterationEnd) { STREAM_LOG(LogLevel::Warning, ("Media graph global underrun detected")); mIterationEnd = stateComputedTime; } if (mIterationStart >= mIterationEnd) { NS_ASSERTION(mIterationStart == mIterationEnd , "Time can't go backwards!"); // This could happen due to low clock resolution, maybe? STREAM_LOG(LogLevel::Debug, ("Time did not advance")); } GraphTime nextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock( mIterationEnd + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS)); if (nextStateComputedTime < stateComputedTime) { // A previous driver may have been processing further ahead of // iterationEnd. STREAM_LOG(LogLevel::Warning, ("Prevent state from going backwards. interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)stateComputedTime, (long)nextStateComputedTime)); nextStateComputedTime = stateComputedTime; } STREAM_LOG(LogLevel::Debug, ("interval[%ld; %ld] state[%ld; %ld]", (long)mIterationStart, (long)mIterationEnd, (long)stateComputedTime, (long)nextStateComputedTime)); stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime); if (mNextDriver && stillProcessing) { STREAM_LOG(LogLevel::Debug, ("Switching to AudioCallbackDriver")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); return; } } }
MediaTime SystemClockDriver::GetIntervalForIteration() { TimeStamp now = TimeStamp::Now(); MediaTime interval = mGraphImpl->SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()); mCurrentTimeStamp = now; MOZ_LOG(gMediaStreamGraphLog, LogLevel::Verbose, ("Updating current time to %f (real %f, StateComputedTime() %f)", mGraphImpl->MediaTimeToSeconds(IterationEnd() + interval), (now - mInitialTimeStamp).ToSeconds(), mGraphImpl->MediaTimeToSeconds(StateComputedTime()))); return interval; }
long AudioCallbackDriver::DataCallback(AudioDataValue* aBuffer, long aFrames) { bool stillProcessing; if (mPauseRequested) { PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount()); return aFrames; } #ifdef XP_MACOSX if (OSXDeviceSwitchingWorkaround()) { PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount()); return aFrames; } #endif #ifdef DEBUG // DebugOnly<> doesn't work here... it forces an initialization that will cause // mInCallback to be set back to false before we exit the statement. Do it by // hand instead. AutoInCallback aic(this); #endif GraphTime stateComputedTime = StateComputedTime(); if (stateComputedTime == 0) { MonitorAutoLock mon(mGraphImpl->GetMonitor()); // Because this function is called during cubeb_stream_init (to prefill the // audio buffers), it can be that we don't have a message here (because this // driver is the first one for this graph), and the graph would exit. Simply // return here until we have messages. if (!mGraphImpl->MessagesQueued()) { PodZero(aBuffer, aFrames * mGraphImpl->AudioChannelCount()); return aFrames; } mGraphImpl->SwapMessageQueues(); } uint32_t durationMS = aFrames * 1000 / mSampleRate; // For now, simply average the duration with the previous // duration so there is some damping against sudden changes. if (!mIterationDurationMS) { mIterationDurationMS = durationMS; } else { mIterationDurationMS = (mIterationDurationMS*3) + durationMS; mIterationDurationMS /= 4; } mBuffer.SetBuffer(aBuffer, aFrames); // fill part or all with leftover data from last iteration (since we // align to Audio blocks) mScratchBuffer.Empty(mBuffer); // if we totally filled the buffer (and mScratchBuffer isn't empty), // we don't need to run an iteration and if we do so we may overflow. if (mBuffer.Available()) { // State computed time is decided by the audio callback's buffer length. We // compute the iteration start and end from there, trying to keep the amount // of buffering in the graph constant. GraphTime nextStateComputedTime = mGraphImpl->RoundUpToNextAudioBlock(stateComputedTime + mBuffer.Available()); mIterationStart = mIterationEnd; // inGraph is the number of audio frames there is between the state time and // the current time, i.e. the maximum theoretical length of the interval we // could use as [mIterationStart; mIterationEnd]. GraphTime inGraph = stateComputedTime - mIterationStart; // We want the interval [mIterationStart; mIterationEnd] to be before the // interval [stateComputedTime; nextStateComputedTime]. We also want // the distance between these intervals to be roughly equivalent each time, to // ensure there is no clock drift between current time and state time. Since // we can't act on the state time because we have to fill the audio buffer, we // reclock the current time against the state time, here. mIterationEnd = mIterationStart + 0.8 * inGraph; STREAM_LOG(LogLevel::Debug, ("interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) (duration ticks: %ld)\n", (long)mIterationStart, (long)mIterationEnd, (long)stateComputedTime, (long)nextStateComputedTime, (long)aFrames, (uint32_t)durationMS, (long)(nextStateComputedTime - stateComputedTime))); mCurrentTimeStamp = TimeStamp::Now(); if (stateComputedTime < mIterationEnd) { STREAM_LOG(LogLevel::Warning, ("Media graph global underrun detected")); mIterationEnd = stateComputedTime; } stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime); } else { NS_WARNING("DataCallback buffer filled entirely from scratch buffer, skipping iteration."); stillProcessing = true; } mBuffer.BufferFilled(); if (mNextDriver && stillProcessing) { { // If the audio stream has not been started by the previous driver or // the graph itself, keep it alive. MonitorAutoLock mon(mGraphImpl->GetMonitor()); if (!IsStarted()) { return aFrames; } } STREAM_LOG(LogLevel::Debug, ("Switching to system driver.")); mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd); mGraphImpl->SetCurrentDriver(mNextDriver); mNextDriver->Start(); // Returning less than aFrames starts the draining and eventually stops the // audio thread. This function will never get called again. return aFrames - 1; } if (!stillProcessing) { LIFECYCLE_LOG("Stopping audio thread for MediaStreamGraph %p", this); return aFrames - 1; } return aFrames; }