1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "GraphDriver.h"
9 #include "AudioNodeEngine.h"
10 #include "cubeb/cubeb.h"
11 #include "mozilla/dom/AudioContext.h"
12 #include "mozilla/dom/AudioDeviceInfo.h"
13 #include "mozilla/dom/BaseAudioContextBinding.h"
14 #include "mozilla/SchedulerGroup.h"
15 #include "mozilla/SharedThreadPool.h"
16 #include "mozilla/ClearOnShutdown.h"
17 #include "mozilla/Unused.h"
18 #include "mozilla/MathAlgorithms.h"
19 #include "mozilla/StaticPrefs_media.h"
20 #include "CubebDeviceEnumerator.h"
21 #include "MediaTrackGraphImpl.h"
22 #include "CallbackThreadRegistry.h"
26 # include "webrtc/MediaEngineWebRTC.h"
30 # include <sys/sysctl.h>
33 extern mozilla::LazyLogModule gMediaTrackGraphLog
;
37 #define LOG(type, msg) MOZ_LOG(gMediaTrackGraphLog, type, msg)
41 GraphDriver::GraphDriver(GraphInterface
* aGraphInterface
,
42 GraphDriver
* aPreviousDriver
, uint32_t aSampleRate
)
43 : mGraphInterface(aGraphInterface
),
44 mSampleRate(aSampleRate
),
45 mPreviousDriver(aPreviousDriver
) {}
47 void GraphDriver::SetStreamName(const nsACString
& aStreamName
) {
48 MOZ_ASSERT(InIteration() || (!ThreadRunning() && NS_IsMainThread()));
49 mStreamName
= aStreamName
;
50 LOG(LogLevel::Debug
, ("%p: GraphDriver::SetStreamName driver=%p %s", Graph(),
51 this, mStreamName
.get()));
54 void GraphDriver::SetState(const nsACString
& aStreamName
,
55 GraphTime aIterationEnd
,
56 GraphTime aStateComputedTime
) {
57 MOZ_ASSERT(InIteration() || !ThreadRunning());
59 mStreamName
= aStreamName
;
60 mIterationEnd
= aIterationEnd
;
61 mStateComputedTime
= aStateComputedTime
;
65 bool GraphDriver::InIteration() const {
66 return OnThread() || Graph()->InDriverIteration(this);
70 GraphDriver
* GraphDriver::PreviousDriver() {
71 MOZ_ASSERT(InIteration() || !ThreadRunning());
72 return mPreviousDriver
;
75 void GraphDriver::SetPreviousDriver(GraphDriver
* aPreviousDriver
) {
76 MOZ_ASSERT(InIteration() || !ThreadRunning());
77 mPreviousDriver
= aPreviousDriver
;
80 ThreadedDriver::ThreadedDriver(GraphInterface
* aGraphInterface
,
81 GraphDriver
* aPreviousDriver
,
83 : GraphDriver(aGraphInterface
, aPreviousDriver
, aSampleRate
),
84 mThreadRunning(false) {}
86 class MediaTrackGraphShutdownThreadRunnable
: public Runnable
{
88 explicit MediaTrackGraphShutdownThreadRunnable(
89 already_AddRefed
<nsIThread
> aThread
)
90 : Runnable("MediaTrackGraphShutdownThreadRunnable"), mThread(aThread
) {}
91 NS_IMETHOD
Run() override
{
92 TRACE("MediaTrackGraphShutdownThreadRunnable");
93 MOZ_ASSERT(NS_IsMainThread());
96 mThread
->AsyncShutdown();
102 nsCOMPtr
<nsIThread
> mThread
;
105 ThreadedDriver::~ThreadedDriver() {
107 nsCOMPtr
<nsIRunnable
> event
=
108 new MediaTrackGraphShutdownThreadRunnable(mThread
.forget());
109 SchedulerGroup::Dispatch(event
.forget());
113 class MediaTrackGraphInitThreadRunnable
: public Runnable
{
115 explicit MediaTrackGraphInitThreadRunnable(ThreadedDriver
* aDriver
)
116 : Runnable("MediaTrackGraphInitThreadRunnable"), mDriver(aDriver
) {}
117 NS_IMETHOD
Run() override
{
118 TRACE("MediaTrackGraphInitThreadRunnable");
119 MOZ_ASSERT(!mDriver
->ThreadRunning());
120 LOG(LogLevel::Debug
, ("Starting a new system driver for graph %p",
121 mDriver
->mGraphInterface
.get()));
123 if (GraphDriver
* previousDriver
= mDriver
->PreviousDriver()) {
125 ("%p releasing an AudioCallbackDriver(%p), for graph %p",
126 mDriver
.get(), previousDriver
, mDriver
->Graph()));
127 MOZ_ASSERT(!mDriver
->AsAudioCallbackDriver());
128 AudioCallbackDriver
* audioCallbackDriver
=
129 previousDriver
->AsAudioCallbackDriver();
130 audioCallbackDriver
->mCubebOperationThread
->Dispatch(
131 NS_NewRunnableFunction(
132 "ThreadedDriver previousDriver::Stop()",
133 [audioCallbackDriver
= RefPtr
{audioCallbackDriver
}] {
134 audioCallbackDriver
->Stop();
136 mDriver
->SetPreviousDriver(nullptr);
139 mDriver
->RunThread();
144 RefPtr
<ThreadedDriver
> mDriver
;
147 void ThreadedDriver::Start() {
148 MOZ_ASSERT(!ThreadRunning());
150 ("Starting thread for a SystemClockDriver %p", mGraphInterface
.get()));
151 Unused
<< NS_WARN_IF(mThread
);
152 MOZ_ASSERT(!mThread
); // Ensure we haven't already started it
154 nsCOMPtr
<nsIRunnable
> event
= new MediaTrackGraphInitThreadRunnable(this);
155 // Note: mThread may be null during event->Run() if we pass to NewNamedThread!
157 nsresult rv
= NS_NewNamedThread("MediaTrackGrph", getter_AddRefs(mThread
));
158 if (NS_SUCCEEDED(rv
)) {
159 mThread
->Dispatch(event
.forget(), NS_DISPATCH_NORMAL
);
163 void ThreadedDriver::Shutdown() {
164 NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
165 // mGraph's thread is not running so it's OK to do whatever here
166 LOG(LogLevel::Debug
, ("Stopping threads for MediaTrackGraph %p", this));
170 ("%p: Stopping ThreadedDriver's %p thread", Graph(), this));
171 mThread
->AsyncShutdown();
176 SystemClockDriver::SystemClockDriver(GraphInterface
* aGraphInterface
,
177 GraphDriver
* aPreviousDriver
,
178 uint32_t aSampleRate
)
179 : ThreadedDriver(aGraphInterface
, aPreviousDriver
, aSampleRate
),
180 mInitialTimeStamp(TimeStamp::Now()),
181 mCurrentTimeStamp(TimeStamp::Now()),
182 mLastTimeStamp(TimeStamp::Now()) {}
184 SystemClockDriver::~SystemClockDriver() = default;
186 void ThreadedDriver::RunThread() {
187 mThreadRunning
= true;
189 auto iterationStart
= mIterationEnd
;
190 mIterationEnd
+= GetIntervalForIteration();
192 if (mStateComputedTime
< mIterationEnd
) {
193 LOG(LogLevel::Warning
, ("%p: Global underrun detected", Graph()));
194 mIterationEnd
= mStateComputedTime
;
197 if (iterationStart
>= mIterationEnd
) {
198 NS_ASSERTION(iterationStart
== mIterationEnd
, "Time can't go backwards!");
199 // This could happen due to low clock resolution, maybe?
200 LOG(LogLevel::Debug
, ("%p: Time did not advance", Graph()));
203 GraphTime nextStateComputedTime
=
204 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(
205 mIterationEnd
+ MillisecondsToMediaTime(AUDIO_TARGET_MS
));
206 if (nextStateComputedTime
< mStateComputedTime
) {
207 // A previous driver may have been processing further ahead of
209 LOG(LogLevel::Warning
,
210 ("%p: Prevent state from going backwards. interval[%ld; %ld] "
213 Graph(), (long)iterationStart
, (long)mIterationEnd
,
214 (long)mStateComputedTime
, (long)nextStateComputedTime
));
215 nextStateComputedTime
= mStateComputedTime
;
217 LOG(LogLevel::Verbose
,
218 ("%p: interval[%ld; %ld] state[%ld; %ld]", Graph(),
219 (long)iterationStart
, (long)mIterationEnd
, (long)mStateComputedTime
,
220 (long)nextStateComputedTime
));
222 mStateComputedTime
= nextStateComputedTime
;
223 IterationResult result
=
224 Graph()->OneIteration(mStateComputedTime
, mIterationEnd
, nullptr);
226 if (result
.IsStop()) {
227 // Signal that we're done stopping.
231 WaitForNextIteration();
232 if (GraphDriver
* nextDriver
= result
.NextDriver()) {
233 LOG(LogLevel::Debug
, ("%p: Switching to AudioCallbackDriver", Graph()));
235 nextDriver
->SetState(mStreamName
, mIterationEnd
, mStateComputedTime
);
239 MOZ_ASSERT(result
.IsStillProcessing());
241 mThreadRunning
= false;
244 MediaTime
SystemClockDriver::GetIntervalForIteration() {
245 TimeStamp now
= TimeStamp::Now();
247 SecondsToMediaTime((now
- mCurrentTimeStamp
).ToSeconds());
248 mCurrentTimeStamp
= now
;
250 MOZ_LOG(gMediaTrackGraphLog
, LogLevel::Verbose
,
251 ("%p: Updating current time to %f (real %f, StateComputedTime() %f)",
252 Graph(), MediaTimeToSeconds(mIterationEnd
+ interval
),
253 (now
- mInitialTimeStamp
).ToSeconds(),
254 MediaTimeToSeconds(mStateComputedTime
)));
259 void ThreadedDriver::EnsureNextIteration() {
260 mWaitHelper
.EnsureNextIteration();
263 void ThreadedDriver::WaitForNextIteration() {
265 MOZ_ASSERT(OnThread());
266 mWaitHelper
.WaitForNextIterationAtLeast(WaitInterval());
269 TimeDuration
SystemClockDriver::WaitInterval() {
271 MOZ_ASSERT(OnThread());
272 TimeStamp now
= TimeStamp::Now();
273 int64_t timeoutMS
= MEDIA_GRAPH_TARGET_PERIOD_MS
-
274 int64_t((now
- mCurrentTimeStamp
).ToMilliseconds());
275 // Make sure timeoutMS doesn't overflow 32 bits by waking up at
276 // least once a minute, if we need to wake up at all
277 timeoutMS
= std::max
<int64_t>(0, std::min
<int64_t>(timeoutMS
, 60 * 1000));
278 LOG(LogLevel::Verbose
,
279 ("%p: Waiting for next iteration; at %f, timeout=%f", Graph(),
280 (now
- mInitialTimeStamp
).ToSeconds(), timeoutMS
/ 1000.0));
282 return TimeDuration::FromMilliseconds(timeoutMS
);
285 OfflineClockDriver::OfflineClockDriver(GraphInterface
* aGraphInterface
,
286 uint32_t aSampleRate
, GraphTime aSlice
)
287 : ThreadedDriver(aGraphInterface
, nullptr, aSampleRate
), mSlice(aSlice
) {}
289 OfflineClockDriver::~OfflineClockDriver() = default;
291 void OfflineClockDriver::RunThread() {
292 nsCOMPtr
<nsIThreadInternal
> threadInternal
= do_QueryInterface(mThread
);
293 nsCOMPtr
<nsIThreadObserver
> observer
= do_QueryInterface(Graph());
294 threadInternal
->SetObserver(observer
);
296 ThreadedDriver::RunThread();
299 MediaTime
OfflineClockDriver::GetIntervalForIteration() {
300 return MillisecondsToMediaTime(mSlice
);
303 TrackAndPromiseForOperation::TrackAndPromiseForOperation(
304 MediaTrack
* aTrack
, dom::AudioContextOperation aOperation
,
305 AbstractThread
* aMainThread
,
306 MozPromiseHolder
<MediaTrackGraph::AudioContextOperationPromise
>&& aHolder
)
308 mOperation(aOperation
),
309 mMainThread(aMainThread
),
310 mHolder(std::move(aHolder
)) {}
312 TrackAndPromiseForOperation::TrackAndPromiseForOperation(
313 TrackAndPromiseForOperation
&& aOther
) noexcept
314 : mTrack(std::move(aOther
.mTrack
)),
315 mOperation(aOther
.mOperation
),
316 mMainThread(std::move(aOther
.mMainThread
)),
317 mHolder(std::move(aOther
.mHolder
)) {}
319 /* Helper to proxy the GraphInterface methods used by a running
320 * mFallbackDriver. */
321 class AudioCallbackDriver::FallbackWrapper
: public GraphInterface
{
323 FallbackWrapper(RefPtr
<GraphInterface
> aGraph
,
324 RefPtr
<AudioCallbackDriver
> aOwner
, uint32_t aSampleRate
,
325 const nsACString
& aStreamName
, GraphTime aIterationEnd
,
326 GraphTime aStateComputedTime
)
327 : mGraph(std::move(aGraph
)),
328 mOwner(std::move(aOwner
)),
330 MakeRefPtr
<SystemClockDriver
>(this, nullptr, aSampleRate
)) {
331 mFallbackDriver
->SetState(aStreamName
, aIterationEnd
, aStateComputedTime
);
334 NS_DECL_THREADSAFE_ISUPPORTS
336 /* Proxied SystemClockDriver methods */
337 void Start() { mFallbackDriver
->Start(); }
338 MOZ_CAN_RUN_SCRIPT
void Shutdown() {
339 RefPtr
<SystemClockDriver
> driver
= mFallbackDriver
;
342 void SetStreamName(const nsACString
& aStreamName
) {
343 mFallbackDriver
->SetStreamName(aStreamName
);
345 void EnsureNextIteration() { mFallbackDriver
->EnsureNextIteration(); }
347 bool InIteration() { return mFallbackDriver
->InIteration(); }
349 bool OnThread() { return mFallbackDriver
->OnThread(); }
351 /* GraphInterface methods */
352 void NotifyOutputData(AudioDataValue
* aBuffer
, size_t aFrames
,
353 TrackRate aRate
, uint32_t aChannels
) override
{
354 MOZ_CRASH("Unexpected NotifyOutputData from fallback SystemClockDriver");
356 void NotifyInputStopped() override
{
357 MOZ_CRASH("Unexpected NotifyInputStopped from fallback SystemClockDriver");
359 void NotifyInputData(const AudioDataValue
* aBuffer
, size_t aFrames
,
360 TrackRate aRate
, uint32_t aChannels
,
361 uint32_t aAlreadyBuffered
) override
{
362 MOZ_CRASH("Unexpected NotifyInputData from fallback SystemClockDriver");
364 void DeviceChanged() override
{
365 MOZ_CRASH("Unexpected DeviceChanged from fallback SystemClockDriver");
368 bool InDriverIteration(const GraphDriver
* aDriver
) const override
{
369 return mGraph
->InDriverIteration(mOwner
) && mOwner
->OnFallback();
372 IterationResult
OneIteration(GraphTime aStateComputedEnd
,
373 GraphTime aIterationEnd
,
374 AudioMixer
* aMixer
) override
{
378 AutoInCallback
aic(mOwner
);
381 IterationResult result
=
382 mGraph
->OneIteration(aStateComputedEnd
, aIterationEnd
, aMixer
);
384 AudioStreamState audioState
= mOwner
->mAudioStreamState
;
386 MOZ_ASSERT(audioState
!= AudioStreamState::Stopping
,
387 "The audio driver can only enter stopping if it iterated the "
388 "graph, which it can only do if there's no fallback driver");
389 if (audioState
!= AudioStreamState::Running
&& result
.IsStillProcessing()) {
390 mOwner
->MaybeStartAudioStream();
394 MOZ_ASSERT(result
.IsStillProcessing() || result
.IsStop() ||
395 result
.IsSwitchDriver());
397 // Proxy the release of the fallback driver to a background thread, so it
398 // doesn't perform unexpected suicide.
399 IterationResult stopFallback
=
400 IterationResult::CreateStop(NS_NewRunnableFunction(
401 "AudioCallbackDriver::FallbackDriverStopped",
402 [self
= RefPtr
<FallbackWrapper
>(this), this, aIterationEnd
,
403 aStateComputedEnd
, result
= std::move(result
)]() mutable {
404 FallbackDriverState fallbackState
=
405 result
.IsStillProcessing() ? FallbackDriverState::None
406 : FallbackDriverState::Stopped
;
407 mOwner
->FallbackDriverStopped(aIterationEnd
, aStateComputedEnd
,
410 if (fallbackState
== FallbackDriverState::Stopped
) {
412 // The AudioCallbackDriver may not iterate the graph, but we'll
413 // call into it so we need to be regarded as "in iteration".
414 AutoInCallback
aic(mOwner
);
416 if (GraphDriver
* nextDriver
= result
.NextDriver()) {
418 ("%p: Switching from fallback to other driver.",
421 nextDriver
->SetState(mOwner
->mStreamName
, aIterationEnd
,
424 } else if (result
.IsStop()) {
426 ("%p: Stopping fallback driver.", mOwner
.get()));
431 NS_DispatchBackgroundTask(NS_NewRunnableFunction(
432 "AudioCallbackDriver::FallbackDriverStopped::Release",
433 [fallback
= std::move(self
->mFallbackDriver
)] {}));
440 virtual ~FallbackWrapper() = default;
442 const RefPtr
<GraphInterface
> mGraph
;
443 // Valid until mFallbackDriver has finished its last iteration.
444 RefPtr
<AudioCallbackDriver
> mOwner
;
445 RefPtr
<SystemClockDriver
> mFallbackDriver
;
448 NS_IMPL_ISUPPORTS0(AudioCallbackDriver::FallbackWrapper
)
450 AudioCallbackDriver::AudioCallbackDriver(
451 GraphInterface
* aGraphInterface
, GraphDriver
* aPreviousDriver
,
452 uint32_t aSampleRate
, uint32_t aOutputChannelCount
,
453 uint32_t aInputChannelCount
, CubebUtils::AudioDeviceID aOutputDeviceID
,
454 CubebUtils::AudioDeviceID aInputDeviceID
, AudioInputType aAudioInputType
)
455 : GraphDriver(aGraphInterface
, aPreviousDriver
, aSampleRate
),
456 mOutputChannelCount(aOutputChannelCount
),
457 mInputChannelCount(aInputChannelCount
),
458 mOutputDeviceID(aOutputDeviceID
),
459 mInputDeviceID(aInputDeviceID
),
460 mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS
),
461 mCubebOperationThread(CUBEB_TASK_THREAD
),
462 mAudioThreadId(ProfilerThreadId
{}),
463 mAudioThreadIdInCb(std::thread::id()),
464 mFallback("AudioCallbackDriver::mFallback"),
465 mSandboxed(CubebUtils::SandboxEnabled()) {
466 LOG(LogLevel::Debug
, ("%p: AudioCallbackDriver %p ctor - input: device %p, "
467 "channel %d, output: device %p, channel %d",
468 Graph(), this, mInputDeviceID
, mInputChannelCount
,
469 mOutputDeviceID
, mOutputChannelCount
));
471 NS_WARNING_ASSERTION(mOutputChannelCount
!= 0,
472 "Invalid output channel count");
473 MOZ_ASSERT(mOutputChannelCount
<= 8);
475 const uint32_t kIdleThreadTimeoutMs
= 2000;
476 mCubebOperationThread
->SetIdleThreadTimeout(
477 PR_MillisecondsToInterval(kIdleThreadTimeoutMs
));
479 if (aAudioInputType
== AudioInputType::Voice
) {
480 LOG(LogLevel::Debug
, ("VOICE."));
481 mInputDevicePreference
= CUBEB_DEVICE_PREF_VOICE
;
482 CubebUtils::SetInCommunication(true);
484 mInputDevicePreference
= CUBEB_DEVICE_PREF_ALL
;
487 mMixer
.AddCallback(WrapNotNull(this));
490 AudioCallbackDriver::~AudioCallbackDriver() {
491 if (mInputDevicePreference
== CUBEB_DEVICE_PREF_VOICE
) {
492 CubebUtils::SetInCommunication(false);
496 bool IsMacbookOrMacbookAir() {
499 sysctlbyname("hw.model", NULL
, &len
, NULL
, 0);
501 UniquePtr
<char[]> model(new char[len
]);
502 // This string can be
503 // MacBook%d,%d for a normal MacBook
504 // MacBookAir%d,%d for a Macbook Air
505 sysctlbyname("hw.model", model
.get(), &len
, NULL
, 0);
506 char* substring
= strstr(model
.get(), "MacBook");
508 const size_t offset
= strlen("MacBook");
509 if (!strncmp(model
.get() + offset
, "Air", 3) ||
510 isdigit(model
[offset
+ 1])) {
519 void AudioCallbackDriver::Init(const nsCString
& aStreamName
) {
521 ("%p: AudioCallbackDriver::Init driver=%p", Graph(), this));
522 TRACE("AudioCallbackDriver::Init");
523 MOZ_ASSERT(OnCubebOperationThread());
524 MOZ_ASSERT(mAudioStreamState
== AudioStreamState::Pending
);
525 FallbackDriverState fallbackState
= mFallbackDriverState
;
526 if (fallbackState
== FallbackDriverState::Stopped
) {
527 // The graph has already stopped us.
530 bool fromFallback
= fallbackState
== FallbackDriverState::Running
;
531 cubeb
* cubebContext
= CubebUtils::GetCubebContext();
533 NS_WARNING("Could not get cubeb context.");
534 LOG(LogLevel::Warning
, ("%s: Could not get cubeb context", __func__
));
535 mAudioStreamState
= AudioStreamState::None
;
537 CubebUtils::ReportCubebStreamInitFailure(true);
538 FallbackToSystemClockDriver();
543 cubeb_stream_params output
;
544 cubeb_stream_params input
;
545 bool firstStream
= CubebUtils::GetFirstStream();
547 MOZ_ASSERT(!NS_IsMainThread(),
548 "This is blocking and should never run on the main thread.");
550 output
.rate
= mSampleRate
;
551 output
.format
= CUBEB_SAMPLE_FLOAT32NE
;
553 if (!mOutputChannelCount
) {
554 LOG(LogLevel::Warning
, ("Output number of channels is 0."));
555 mAudioStreamState
= AudioStreamState::None
;
557 CubebUtils::ReportCubebStreamInitFailure(firstStream
);
558 FallbackToSystemClockDriver();
563 CubebUtils::AudioDeviceID forcedOutputDeviceId
= nullptr;
565 char* forcedOutputDeviceName
= CubebUtils::GetForcedOutputDevice();
566 if (forcedOutputDeviceName
) {
567 RefPtr
<CubebDeviceEnumerator
> enumerator
= Enumerator::GetInstance();
568 RefPtr
<AudioDeviceInfo
> device
= enumerator
->DeviceInfoFromName(
569 NS_ConvertUTF8toUTF16(forcedOutputDeviceName
), EnumeratorSide::OUTPUT
);
570 if (device
&& device
->DeviceID()) {
571 forcedOutputDeviceId
= device
->DeviceID();
575 mBuffer
= AudioCallbackBufferWrapper
<AudioDataValue
>(mOutputChannelCount
);
577 SpillBuffer
<AudioDataValue
, WEBAUDIO_BLOCK_SIZE
* 2>(mOutputChannelCount
);
579 output
.channels
= mOutputChannelCount
;
580 AudioConfig::ChannelLayout::ChannelMap channelMap
=
581 AudioConfig::ChannelLayout(mOutputChannelCount
).Map();
583 output
.layout
= static_cast<uint32_t>(channelMap
);
584 output
.prefs
= CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_OUTPUT
);
585 if (mInputDevicePreference
== CUBEB_DEVICE_PREF_VOICE
&&
586 CubebUtils::RouteOutputAsVoice()) {
587 output
.prefs
|= static_cast<cubeb_stream_prefs
>(CUBEB_STREAM_PREF_VOICE
);
590 uint32_t latencyFrames
= CubebUtils::GetCubebMTGLatencyInFrames(&output
);
592 // Macbook and MacBook air don't have enough CPU to run very low latency
593 // MediaTrackGraphs, cap the minimal latency to 512 frames int this case.
594 if (IsMacbookOrMacbookAir()) {
595 latencyFrames
= std::max((uint32_t)512, latencyFrames
);
598 // On OSX, having a latency that is lower than 10ms is very common. It's
599 // not very useful when doing voice, because all the WebRTC code deal in 10ms
600 // chunks of audio. Take the first power of two above 10ms at the current
601 // rate in this case. It's probably 512, for common rates.
602 #if defined(XP_MACOSX)
603 if (mInputDevicePreference
== CUBEB_DEVICE_PREF_VOICE
) {
604 if (latencyFrames
< mSampleRate
/ 100) {
605 latencyFrames
= mozilla::RoundUpPow2(mSampleRate
/ 100);
609 LOG(LogLevel::Debug
, ("Effective latency in frames: %d", latencyFrames
));
612 input
.channels
= mInputChannelCount
;
613 input
.layout
= CUBEB_LAYOUT_UNDEFINED
;
614 input
.prefs
= CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_INPUT
);
615 if (mInputDevicePreference
== CUBEB_DEVICE_PREF_VOICE
) {
616 input
.prefs
|= static_cast<cubeb_stream_prefs
>(CUBEB_STREAM_PREF_VOICE
);
619 cubeb_stream
* stream
= nullptr;
620 const char* streamName
=
621 aStreamName
.IsEmpty() ? "AudioCallbackDriver" : aStreamName
.get();
622 bool inputWanted
= mInputChannelCount
> 0;
623 CubebUtils::AudioDeviceID outputId
= mOutputDeviceID
;
624 CubebUtils::AudioDeviceID inputId
= mInputDeviceID
;
626 if (CubebUtils::CubebStreamInit(
627 cubebContext
, &stream
, streamName
, inputId
,
628 inputWanted
? &input
: nullptr,
629 forcedOutputDeviceId
? forcedOutputDeviceId
: outputId
, &output
,
630 latencyFrames
, DataCallback_s
, StateCallback_s
, this) == CUBEB_OK
) {
631 mAudioStream
.own(stream
);
633 cubeb_stream_set_volume(mAudioStream
, CubebUtils::GetVolumeScale());
634 NS_WARNING_ASSERTION(
636 "Could not set the audio stream volume in GraphDriver.cpp");
637 CubebUtils::ReportCubebBackendUsed();
640 "Could not create a cubeb stream for MediaTrackGraph, falling "
641 "back to a SystemClockDriver");
642 mAudioStreamState
= AudioStreamState::None
;
643 // Only report failures when we're not coming from a driver that was
644 // created itself as a fallback driver because of a previous audio driver
647 CubebUtils::ReportCubebStreamInitFailure(firstStream
);
648 FallbackToSystemClockDriver();
654 PanOutputIfNeeded(inputWanted
);
657 cubeb_stream_register_device_changed_callback(
658 mAudioStream
, AudioCallbackDriver::DeviceChangedCallback_s
);
660 // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable. This
661 // is intended for diagnosing issues, and only works if the content sandbox is
663 mInputStreamFile
.Open("GraphDriverInput", input
.channels
, input
.rate
);
664 mOutputStreamFile
.Open("GraphDriverOutput", output
.channels
, output
.rate
);
666 if (NS_WARN_IF(!StartStream())) {
667 LOG(LogLevel::Warning
,
668 ("%p: AudioCallbackDriver couldn't start a cubeb stream.", Graph()));
672 LOG(LogLevel::Debug
, ("%p: AudioCallbackDriver started.", Graph()));
675 void AudioCallbackDriver::SetCubebStreamName(const nsCString
& aStreamName
) {
676 MOZ_ASSERT(OnCubebOperationThread());
677 MOZ_ASSERT(mAudioStream
);
678 cubeb_stream_set_name(mAudioStream
, aStreamName
.get());
681 void AudioCallbackDriver::Start() {
682 MOZ_ASSERT(!IsStarted());
683 MOZ_ASSERT(mAudioStreamState
== AudioStreamState::None
);
684 MOZ_ASSERT_IF(PreviousDriver(), PreviousDriver()->InIteration());
685 mAudioStreamState
= AudioStreamState::Pending
;
687 if (mFallbackDriverState
== FallbackDriverState::None
) {
688 // Starting an audio driver could take a while. We start a system driver in
689 // the meantime so that the graph is kept running.
690 FallbackToSystemClockDriver();
693 if (mPreviousDriver
) {
694 if (AudioCallbackDriver
* previousAudioCallback
=
695 mPreviousDriver
->AsAudioCallbackDriver()) {
696 LOG(LogLevel::Debug
, ("Releasing audio driver off main thread."));
697 mCubebOperationThread
->Dispatch(NS_NewRunnableFunction(
698 "AudioCallbackDriver previousDriver::Stop()",
699 [previousDriver
= RefPtr
{previousAudioCallback
}] {
700 previousDriver
->Stop();
704 ("Dropping driver reference for SystemClockDriver."));
705 MOZ_ASSERT(mPreviousDriver
->AsSystemClockDriver());
707 mPreviousDriver
= nullptr;
710 LOG(LogLevel::Debug
, ("Starting new audio driver off main thread, "
711 "to ensure it runs after previous shutdown."));
712 mCubebOperationThread
->Dispatch(
713 NS_NewRunnableFunction("AudioCallbackDriver Init()",
714 [self
= RefPtr
{this}, streamName
= mStreamName
] {
715 self
->Init(streamName
);
719 bool AudioCallbackDriver::StartStream() {
720 TRACE("AudioCallbackDriver::StartStream");
721 MOZ_ASSERT(!IsStarted() && OnCubebOperationThread());
722 // Set STARTING before cubeb_stream_start, since starting the cubeb stream
723 // can result in a callback (that may read mAudioStreamState) before
724 // mAudioStreamState would otherwise be set.
725 mAudioStreamState
= AudioStreamState::Starting
;
726 if (cubeb_stream_start(mAudioStream
) != CUBEB_OK
) {
727 NS_WARNING("Could not start cubeb stream for MTG.");
734 void AudioCallbackDriver::Stop() {
736 ("%p: AudioCallbackDriver::Stop driver=%p", Graph(), this));
737 TRACE("AudioCallbackDriver::Stop");
738 MOZ_ASSERT(OnCubebOperationThread());
739 cubeb_stream_register_device_changed_callback(mAudioStream
, nullptr);
740 if (cubeb_stream_stop(mAudioStream
) != CUBEB_OK
) {
741 NS_WARNING("Could not stop cubeb stream for MTG.");
743 mAudioStreamState
= AudioStreamState::None
;
747 void AudioCallbackDriver::Shutdown() {
748 MOZ_ASSERT(NS_IsMainThread());
749 RefPtr
<FallbackWrapper
> fallback
;
751 auto fallbackLock
= mFallback
.Lock();
752 fallback
= fallbackLock
.ref();
753 fallbackLock
.ref() = nullptr;
757 ("%p: Releasing fallback driver %p.", Graph(), fallback
.get()));
758 fallback
->Shutdown();
762 ("%p: Releasing audio driver off main thread (GraphDriver::Shutdown).",
765 nsLiteralCString
reason("AudioCallbackDriver::Shutdown");
766 NS_DispatchAndSpinEventLoopUntilComplete(
767 reason
, mCubebOperationThread
,
768 NS_NewRunnableFunction(reason
.get(),
769 [self
= RefPtr
{this}] { self
->Stop(); }));
772 void AudioCallbackDriver::SetStreamName(const nsACString
& aStreamName
) {
773 MOZ_ASSERT(InIteration() || !ThreadRunning());
774 if (aStreamName
== mStreamName
) {
777 // Record the stream name, which will be passed onto the next driver, if
778 // any, either from this driver or the fallback driver.
779 GraphDriver::SetStreamName(aStreamName
);
781 auto fallbackLock
= mFallback
.Lock();
782 FallbackWrapper
* fallback
= fallbackLock
.ref().get();
784 MOZ_ASSERT(fallback
->InIteration());
785 fallback
->SetStreamName(aStreamName
);
788 AudioStreamState streamState
= mAudioStreamState
;
789 if (streamState
!= AudioStreamState::None
&&
790 streamState
!= AudioStreamState::Stopping
) {
791 mCubebOperationThread
->Dispatch(
792 NS_NewRunnableFunction("AudioCallbackDriver SetStreamName()",
793 [self
= RefPtr
{this}, streamName
= mStreamName
] {
794 self
->SetCubebStreamName(streamName
);
800 long AudioCallbackDriver::DataCallback_s(cubeb_stream
* aStream
, void* aUser
,
801 const void* aInputBuffer
,
802 void* aOutputBuffer
, long aFrames
) {
803 AudioCallbackDriver
* driver
= reinterpret_cast<AudioCallbackDriver
*>(aUser
);
804 return driver
->DataCallback(static_cast<const AudioDataValue
*>(aInputBuffer
),
805 static_cast<AudioDataValue
*>(aOutputBuffer
),
810 void AudioCallbackDriver::StateCallback_s(cubeb_stream
* aStream
, void* aUser
,
811 cubeb_state aState
) {
812 AudioCallbackDriver
* driver
= reinterpret_cast<AudioCallbackDriver
*>(aUser
);
813 driver
->StateCallback(aState
);
817 void AudioCallbackDriver::DeviceChangedCallback_s(void* aUser
) {
818 AudioCallbackDriver
* driver
= reinterpret_cast<AudioCallbackDriver
*>(aUser
);
819 driver
->DeviceChangedCallback();
822 AudioCallbackDriver::AutoInCallback::AutoInCallback(
823 AudioCallbackDriver
* aDriver
)
825 MOZ_ASSERT(mDriver
->mAudioThreadIdInCb
== std::thread::id());
826 mDriver
->mAudioThreadIdInCb
= std::this_thread::get_id();
829 AudioCallbackDriver::AutoInCallback::~AutoInCallback() {
830 MOZ_ASSERT(mDriver
->mAudioThreadIdInCb
== std::this_thread::get_id());
831 mDriver
->mAudioThreadIdInCb
= std::thread::id();
834 bool AudioCallbackDriver::CheckThreadIdChanged() {
835 ProfilerThreadId id
= profiler_current_thread_id();
836 if (id
!= mAudioThreadId
) {
843 long AudioCallbackDriver::DataCallback(const AudioDataValue
* aInputBuffer
,
844 AudioDataValue
* aOutputBuffer
,
846 if (!mSandboxed
&& CheckThreadIdChanged()) {
847 CallbackThreadRegistry::Get()->Register(mAudioThreadId
,
848 "NativeAudioCallback");
851 if (mAudioStreamState
.compareExchange(AudioStreamState::Starting
,
852 AudioStreamState::Running
)) {
853 LOG(LogLevel::Verbose
, ("%p: AudioCallbackDriver %p First audio callback "
854 "close the Fallback driver",
858 FallbackDriverState fallbackState
= mFallbackDriverState
;
859 if (MOZ_UNLIKELY(fallbackState
== FallbackDriverState::Running
)) {
860 // Wait for the fallback driver to stop. Wake it up so it can stop if it's
862 LOG(LogLevel::Verbose
,
863 ("%p: AudioCallbackDriver %p Waiting for the Fallback driver to stop",
865 EnsureNextIteration();
866 PodZero(aOutputBuffer
, aFrames
* mOutputChannelCount
);
870 if (MOZ_UNLIKELY(fallbackState
== FallbackDriverState::Stopped
)) {
871 // We're supposed to stop.
872 PodZero(aOutputBuffer
, aFrames
* mOutputChannelCount
);
874 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId
);
879 MOZ_ASSERT(mAudioStreamState
== AudioStreamState::Running
);
880 TRACE_AUDIO_CALLBACK_BUDGET("AudioCallbackDriver real-time budget", aFrames
,
882 TRACE("AudioCallbackDriver::DataCallback");
885 AutoInCallback
aic(this);
888 uint32_t durationMS
= aFrames
* 1000 / mSampleRate
;
890 // For now, simply average the duration with the previous
891 // duration so there is some damping against sudden changes.
892 if (!mIterationDurationMS
) {
893 mIterationDurationMS
= durationMS
;
895 mIterationDurationMS
= (mIterationDurationMS
* 3) + durationMS
;
896 mIterationDurationMS
/= 4;
899 mBuffer
.SetBuffer(aOutputBuffer
, aFrames
);
900 // fill part or all with leftover data from last iteration (since we
901 // align to Audio blocks)
902 uint32_t alreadyBuffered
= mScratchBuffer
.Empty(mBuffer
);
904 // State computed time is decided by the audio callback's buffer length. We
905 // compute the iteration start and end from there, trying to keep the amount
906 // of buffering in the graph constant.
907 GraphTime nextStateComputedTime
=
908 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(mStateComputedTime
+
909 mBuffer
.Available());
911 auto iterationStart
= mIterationEnd
;
912 // inGraph is the number of audio frames there is between the state time and
913 // the current time, i.e. the maximum theoretical length of the interval we
914 // could use as [iterationStart; mIterationEnd].
915 GraphTime inGraph
= mStateComputedTime
- iterationStart
;
916 // We want the interval [iterationStart; mIterationEnd] to be before the
917 // interval [mStateComputedTime; nextStateComputedTime]. We also want
918 // the distance between these intervals to be roughly equivalent each time, to
919 // ensure there is no clock drift between current time and state time. Since
920 // we can't act on the state time because we have to fill the audio buffer, we
921 // reclock the current time against the state time, here.
922 mIterationEnd
= iterationStart
+ 0.8 * inGraph
;
924 LOG(LogLevel::Verbose
,
925 ("%p: interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) "
926 "(duration ticks: %ld)",
927 Graph(), (long)iterationStart
, (long)mIterationEnd
,
928 (long)mStateComputedTime
, (long)nextStateComputedTime
, (long)aFrames
,
929 (uint32_t)durationMS
,
930 (long)(nextStateComputedTime
- mStateComputedTime
)));
932 if (mStateComputedTime
< mIterationEnd
) {
933 LOG(LogLevel::Error
, ("%p: Media graph global underrun detected", Graph()));
934 MOZ_ASSERT_UNREACHABLE("We should not underrun in full duplex");
935 mIterationEnd
= mStateComputedTime
;
938 // Process mic data if any/needed
939 if (aInputBuffer
&& mInputChannelCount
> 0) {
940 Graph()->NotifyInputData(aInputBuffer
, static_cast<size_t>(aFrames
),
941 mSampleRate
, mInputChannelCount
, alreadyBuffered
);
944 IterationResult result
=
945 Graph()->OneIteration(nextStateComputedTime
, mIterationEnd
, &mMixer
);
947 mStateComputedTime
= nextStateComputedTime
;
949 MOZ_ASSERT(mBuffer
.Available() == 0,
950 "The graph should have filled the buffer");
952 mBuffer
.BufferFilled();
954 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
955 // Prevent returning NaN to the OS mixer, and propagating NaN into the reverse
956 // stream of the AEC.
957 NaNToZeroInPlace(aOutputBuffer
, aFrames
* mOutputChannelCount
);
960 // Callback any observers for the AEC speaker data. Note that one
961 // (maybe) of these will be full-duplex, the others will get their input
962 // data off separate cubeb callbacks. Take care with how stuff is
963 // removed/added to this list and TSAN issues, but input and output will
964 // use separate callback methods.
965 Graph()->NotifyOutputData(aOutputBuffer
, static_cast<size_t>(aFrames
),
966 mSampleRate
, mOutputChannelCount
);
969 // This only happens when the output is on a macbookpro's external speaker,
970 // that are stereo, but let's just be safe.
971 if (mNeedsPanning
&& mOutputChannelCount
== 2) {
972 // hard pan to the right
973 for (uint32_t i
= 0; i
< aFrames
* 2; i
+= 2) {
974 aOutputBuffer
[i
+ 1] += aOutputBuffer
[i
];
975 aOutputBuffer
[i
] = 0.0;
980 // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable
982 mInputStreamFile
.Write(static_cast<const AudioDataValue
*>(aInputBuffer
),
983 aFrames
* mInputChannelCount
);
985 mOutputStreamFile
.Write(static_cast<const AudioDataValue
*>(aOutputBuffer
),
986 aFrames
* mOutputChannelCount
);
988 if (result
.IsStop()) {
989 if (mInputDeviceID
) {
990 mGraphInterface
->NotifyInputStopped();
992 // Signal that we have stopped.
994 // Update the flag before handing over the graph and going to drain.
995 mAudioStreamState
= AudioStreamState::Stopping
;
997 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId
);
1002 if (GraphDriver
* nextDriver
= result
.NextDriver()) {
1003 LOG(LogLevel::Debug
,
1004 ("%p: Switching to %s driver.", Graph(),
1005 nextDriver
->AsAudioCallbackDriver() ? "audio" : "system"));
1006 if (mInputDeviceID
) {
1007 mGraphInterface
->NotifyInputStopped();
1010 mAudioStreamState
= AudioStreamState::Stopping
;
1011 nextDriver
->SetState(mStreamName
, mIterationEnd
, mStateComputedTime
);
1012 nextDriver
->Start();
1014 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId
);
1016 // Returning less than aFrames starts the draining and eventually stops the
1017 // audio thread. This function will never get called again.
1021 MOZ_ASSERT(result
.IsStillProcessing());
1025 static const char* StateToString(cubeb_state aState
) {
1027 case CUBEB_STATE_STARTED
:
1029 case CUBEB_STATE_STOPPED
:
1031 case CUBEB_STATE_DRAINED
:
1033 case CUBEB_STATE_ERROR
:
1036 MOZ_CRASH("Unexpected state!");
1040 void AudioCallbackDriver::StateCallback(cubeb_state aState
) {
1041 MOZ_ASSERT(!InIteration());
1042 LOG(LogLevel::Debug
,
1043 ("AudioCallbackDriver(%p) State: %s", this, StateToString(aState
)));
1045 if (aState
== CUBEB_STATE_STARTED
|| aState
== CUBEB_STATE_STOPPED
) {
1046 // Nothing to do for STARTED.
1048 // For STOPPED, don't reset mAudioStreamState until after
1049 // cubeb_stream_stop() returns, as wasapi_stream_stop() dispatches
1050 // CUBEB_STATE_STOPPED before ensuring that data callbacks have finished.
1051 // https://searchfox.org/mozilla-central/rev/f9beb753a84aa297713d1565dcd0c5e3c66e4174/media/libcubeb/src/cubeb_wasapi.cpp#3009,3012
1055 AudioStreamState streamState
= mAudioStreamState
;
1056 if (streamState
< AudioStreamState::Starting
) {
1057 // mAudioStream has already entered STOPPED, DRAINED, or ERROR.
1058 // Don't reset a Pending state indicating that a task to destroy
1059 // mAudioStream and init a new cubeb_stream has already been triggered.
1063 // Reset for DRAINED or ERROR.
1064 streamState
= mAudioStreamState
.exchange(AudioStreamState::None
);
1066 if (aState
== CUBEB_STATE_ERROR
) {
1067 // About to hand over control of the graph. Do not start a new driver if
1068 // StateCallback() receives an error for this stream while the main thread
1069 // or another driver has control of the graph.
1070 if (streamState
== AudioStreamState::Running
) {
1071 if (mFallbackDriverState
== FallbackDriverState::None
) {
1072 // Only switch to fallback if it's not already running. It could be
1073 // running with the callback driver having started but not seen a single
1074 // callback yet. I.e., handover from fallback to callback is not done.
1075 if (mInputDeviceID
) {
1077 // No audio callback after an error. We're calling into the graph here
1078 // so we need to be regarded as "in iteration".
1079 AutoInCallback
aic(this);
1081 mGraphInterface
->NotifyInputStopped();
1083 FallbackToSystemClockDriver();
1089 void AudioCallbackDriver::MixerCallback(AudioDataValue
* aMixedBuffer
,
1090 AudioSampleFormat aFormat
,
1091 uint32_t aChannels
, uint32_t aFrames
,
1092 uint32_t aSampleRate
) {
1093 MOZ_ASSERT(InIteration());
1094 uint32_t toWrite
= mBuffer
.Available();
1096 if (!mBuffer
.Available() && aFrames
> 0) {
1097 NS_WARNING("DataCallback buffer full, expect frame drops.");
1100 MOZ_ASSERT(mBuffer
.Available() <= aFrames
);
1102 mBuffer
.WriteFrames(aMixedBuffer
, mBuffer
.Available());
1103 MOZ_ASSERT(mBuffer
.Available() == 0,
1104 "Missing frames to fill audio callback's buffer.");
1106 DebugOnly
<uint32_t> written
= mScratchBuffer
.Fill(
1107 aMixedBuffer
+ toWrite
* aChannels
, aFrames
- toWrite
);
1108 NS_WARNING_ASSERTION(written
== aFrames
- toWrite
, "Dropping frames.");
1111 void AudioCallbackDriver::PanOutputIfNeeded(bool aMicrophoneActive
) {
1113 TRACE("AudioCallbackDriver::PanOutputIfNeeded");
1114 cubeb_device
* out
= nullptr;
1117 size_t length
= sizeof(name
);
1119 rv
= sysctlbyname("hw.model", name
, &length
, NULL
, 0);
1125 for (uint32_t i
= 0; i
< length
; i
++) {
1126 // skip the model name
1127 if (isalpha(name
[i
])) {
1130 sscanf(name
+ i
, "%d,%d", &major
, &minor
);
1134 enum MacbookModel
{ MacBook
, MacBookPro
, MacBookAir
, NotAMacbook
};
1138 if (!strncmp(name
, "MacBookPro", length
)) {
1140 } else if (strncmp(name
, "MacBookAir", length
)) {
1142 } else if (strncmp(name
, "MacBook", length
)) {
1145 model
= NotAMacbook
;
1147 // For macbook pro before 2016 model (change of chassis), hard pan the audio
1148 // to the right if the speakers are in use to avoid feedback.
1149 if (model
== MacBookPro
&& major
<= 12) {
1150 if (cubeb_stream_get_current_device(mAudioStream
, &out
) == CUBEB_OK
) {
1152 // Check if we are currently outputing sound on external speakers.
1153 if (out
->output_name
&& !strcmp(out
->output_name
, "ispk")) {
1154 // Pan everything to the right speaker.
1155 LOG(LogLevel::Debug
, ("Using the built-in speakers, with%s audio input",
1156 aMicrophoneActive
? "" : "out"));
1157 mNeedsPanning
= aMicrophoneActive
;
1159 LOG(LogLevel::Debug
, ("Using an external output device"));
1160 mNeedsPanning
= false;
1162 cubeb_stream_device_destroy(mAudioStream
, out
);
1168 void AudioCallbackDriver::DeviceChangedCallback() {
1169 MOZ_ASSERT(!InIteration());
1170 // Tell the audio engine the device has changed, it might want to reset some
1172 Graph()->DeviceChanged();
1174 RefPtr
<AudioCallbackDriver
> self(this);
1175 bool hasInput
= mInputChannelCount
;
1176 NS_DispatchBackgroundTask(NS_NewRunnableFunction(
1177 "PanOutputIfNeeded", [self
{std::move(self
)}, hasInput
]() {
1178 self
->PanOutputIfNeeded(hasInput
);
1183 uint32_t AudioCallbackDriver::IterationDuration() {
1184 MOZ_ASSERT(InIteration());
1185 // The real fix would be to have an API in cubeb to give us the number. Short
1186 // of that, we approximate it here. bug 1019507
1187 return mIterationDurationMS
;
1190 void AudioCallbackDriver::EnsureNextIteration() {
1191 if (mFallbackDriverState
== FallbackDriverState::Running
) {
1192 auto fallback
= mFallback
.Lock();
1193 if (fallback
.ref()) {
1194 fallback
.ref()->EnsureNextIteration();
1199 TimeDuration
AudioCallbackDriver::AudioOutputLatency() {
1200 TRACE("AudioCallbackDriver::AudioOutputLatency");
1201 uint32_t latencyFrames
;
1202 int rv
= cubeb_stream_get_latency(mAudioStream
, &latencyFrames
);
1203 if (rv
|| mSampleRate
== 0) {
1204 return TimeDuration::FromSeconds(0.0);
1207 return TimeDuration::FromSeconds(static_cast<double>(latencyFrames
) /
1211 bool AudioCallbackDriver::OnFallback() const {
1212 MOZ_ASSERT(InIteration());
1213 return mFallbackDriverState
== FallbackDriverState::Running
;
1216 void AudioCallbackDriver::FallbackToSystemClockDriver() {
1217 MOZ_ASSERT(!ThreadRunning());
1218 MOZ_ASSERT(mAudioStreamState
== AudioStreamState::None
||
1219 mAudioStreamState
== AudioStreamState::Pending
);
1220 MOZ_ASSERT(mFallbackDriverState
== FallbackDriverState::None
);
1221 LOG(LogLevel::Debug
,
1222 ("%p: AudioCallbackDriver %p Falling back to SystemClockDriver.", Graph(),
1224 mFallbackDriverState
= FallbackDriverState::Running
;
1225 mNextReInitBackoffStep
=
1226 TimeDuration::FromMilliseconds(AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS
);
1227 mNextReInitAttempt
= TimeStamp::Now() + mNextReInitBackoffStep
;
1229 MakeRefPtr
<FallbackWrapper
>(Graph(), this, mSampleRate
, mStreamName
,
1230 mIterationEnd
, mStateComputedTime
);
1232 auto driver
= mFallback
.Lock();
1233 driver
.ref() = fallback
;
1238 void AudioCallbackDriver::FallbackDriverStopped(GraphTime aIterationEnd
,
1239 GraphTime aStateComputedTime
,
1240 FallbackDriverState aState
) {
1241 mIterationEnd
= aIterationEnd
;
1242 mStateComputedTime
= aStateComputedTime
;
1243 mNextReInitAttempt
= TimeStamp();
1244 mNextReInitBackoffStep
= TimeDuration();
1246 auto fallback
= mFallback
.Lock();
1247 MOZ_ASSERT(fallback
.ref()->OnThread());
1248 fallback
.ref() = nullptr;
1251 MOZ_ASSERT(aState
== FallbackDriverState::None
||
1252 aState
== FallbackDriverState::Stopped
);
1253 MOZ_ASSERT_IF(aState
== FallbackDriverState::None
,
1254 mAudioStreamState
== AudioStreamState::Running
);
1255 mFallbackDriverState
= aState
;
1258 void AudioCallbackDriver::MaybeStartAudioStream() {
1259 AudioStreamState streamState
= mAudioStreamState
;
1260 if (streamState
!= AudioStreamState::None
) {
1261 LOG(LogLevel::Verbose
,
1262 ("%p: AudioCallbackDriver %p Cannot re-init.", Graph(), this));
1266 TimeStamp now
= TimeStamp::Now();
1267 if (now
< mNextReInitAttempt
) {
1268 LOG(LogLevel::Verbose
,
1269 ("%p: AudioCallbackDriver %p Not time to re-init yet. %.3fs left.",
1270 Graph(), this, (mNextReInitAttempt
- now
).ToSeconds()));
1274 LOG(LogLevel::Debug
, ("%p: AudioCallbackDriver %p Attempting to re-init "
1275 "audio stream from fallback driver.",
1277 mNextReInitBackoffStep
=
1278 std::min(mNextReInitBackoffStep
* 2,
1279 TimeDuration::FromMilliseconds(
1280 StaticPrefs::media_audio_device_retry_ms()));
1281 mNextReInitAttempt
= now
+ mNextReInitBackoffStep
;
1285 } // namespace mozilla
1287 // avoid redefined macro in unified build