Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / dom / media / GraphDriver.cpp
blob81088086e02556f3b67dd29745edb27b4483363a
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"
23 #include "Tracing.h"
25 #ifdef MOZ_WEBRTC
26 # include "webrtc/MediaEngineWebRTC.h"
27 #endif
29 #ifdef XP_MACOSX
30 # include <sys/sysctl.h>
31 #endif
33 extern mozilla::LazyLogModule gMediaTrackGraphLog;
34 #ifdef LOG
35 # undef LOG
36 #endif // LOG
37 #define LOG(type, msg) MOZ_LOG(gMediaTrackGraphLog, type, msg)
39 namespace mozilla {
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;
64 #ifdef DEBUG
65 bool GraphDriver::InIteration() const {
66 return OnThread() || Graph()->InDriverIteration(this);
68 #endif
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,
82 uint32_t aSampleRate)
83 : GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate),
84 mThreadRunning(false) {}
86 class MediaTrackGraphShutdownThreadRunnable : public Runnable {
87 public:
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());
94 MOZ_ASSERT(mThread);
96 mThread->AsyncShutdown();
97 mThread = nullptr;
98 return NS_OK;
101 private:
102 nsCOMPtr<nsIThread> mThread;
105 ThreadedDriver::~ThreadedDriver() {
106 if (mThread) {
107 nsCOMPtr<nsIRunnable> event =
108 new MediaTrackGraphShutdownThreadRunnable(mThread.forget());
109 SchedulerGroup::Dispatch(event.forget());
113 class MediaTrackGraphInitThreadRunnable : public Runnable {
114 public:
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()) {
124 LOG(LogLevel::Debug,
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();
135 }));
136 mDriver->SetPreviousDriver(nullptr);
139 mDriver->RunThread();
140 return NS_OK;
143 private:
144 RefPtr<ThreadedDriver> mDriver;
147 void ThreadedDriver::Start() {
148 MOZ_ASSERT(!ThreadRunning());
149 LOG(LogLevel::Debug,
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!
156 // See AudioInitTask
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));
168 if (mThread) {
169 LOG(LogLevel::Debug,
170 ("%p: Stopping ThreadedDriver's %p thread", Graph(), this));
171 mThread->AsyncShutdown();
172 mThread = nullptr;
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;
188 while (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
208 // iterationEnd.
209 LOG(LogLevel::Warning,
210 ("%p: Prevent state from going backwards. interval[%ld; %ld] "
211 "state[%ld; "
212 "%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.
228 result.Stopped();
229 break;
231 WaitForNextIteration();
232 if (GraphDriver* nextDriver = result.NextDriver()) {
233 LOG(LogLevel::Debug, ("%p: Switching to AudioCallbackDriver", Graph()));
234 result.Switched();
235 nextDriver->SetState(mStreamName, mIterationEnd, mStateComputedTime);
236 nextDriver->Start();
237 break;
239 MOZ_ASSERT(result.IsStillProcessing());
241 mThreadRunning = false;
244 MediaTime SystemClockDriver::GetIntervalForIteration() {
245 TimeStamp now = TimeStamp::Now();
246 MediaTime interval =
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)));
256 return interval;
259 void ThreadedDriver::EnsureNextIteration() {
260 mWaitHelper.EnsureNextIteration();
263 void ThreadedDriver::WaitForNextIteration() {
264 MOZ_ASSERT(mThread);
265 MOZ_ASSERT(OnThread());
266 mWaitHelper.WaitForNextIterationAtLeast(WaitInterval());
269 TimeDuration SystemClockDriver::WaitInterval() {
270 MOZ_ASSERT(mThread);
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)
307 : mTrack(aTrack),
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 {
322 public:
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)),
329 mFallbackDriver(
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;
340 driver->Shutdown();
342 void SetStreamName(const nsACString& aStreamName) {
343 mFallbackDriver->SetStreamName(aStreamName);
345 void EnsureNextIteration() { mFallbackDriver->EnsureNextIteration(); }
346 #ifdef DEBUG
347 bool InIteration() { return mFallbackDriver->InIteration(); }
348 #endif
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");
367 #ifdef DEBUG
368 bool InDriverIteration(const GraphDriver* aDriver) const override {
369 return mGraph->InDriverIteration(mOwner) && mOwner->OnFallback();
371 #endif
372 IterationResult OneIteration(GraphTime aStateComputedEnd,
373 GraphTime aIterationEnd,
374 AudioMixer* aMixer) override {
375 MOZ_ASSERT(!aMixer);
377 #ifdef DEBUG
378 AutoInCallback aic(mOwner);
379 #endif
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();
391 return result;
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,
408 fallbackState);
410 if (fallbackState == FallbackDriverState::Stopped) {
411 #ifdef DEBUG
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);
415 #endif
416 if (GraphDriver* nextDriver = result.NextDriver()) {
417 LOG(LogLevel::Debug,
418 ("%p: Switching from fallback to other driver.",
419 mOwner.get()));
420 result.Switched();
421 nextDriver->SetState(mOwner->mStreamName, aIterationEnd,
422 aStateComputedEnd);
423 nextDriver->Start();
424 } else if (result.IsStop()) {
425 LOG(LogLevel::Debug,
426 ("%p: Stopping fallback driver.", mOwner.get()));
427 result.Stopped();
430 mOwner = nullptr;
431 NS_DispatchBackgroundTask(NS_NewRunnableFunction(
432 "AudioCallbackDriver::FallbackDriverStopped::Release",
433 [fallback = std::move(self->mFallbackDriver)] {}));
434 }));
436 return stopFallback;
439 private:
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);
483 } else {
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() {
497 #ifdef XP_MACOSX
498 size_t len = 0;
499 sysctlbyname("hw.model", NULL, &len, NULL, 0);
500 if (len) {
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");
507 if (substring) {
508 const size_t offset = strlen("MacBook");
509 if (!strncmp(model.get() + offset, "Air", 3) ||
510 isdigit(model[offset + 1])) {
511 return true;
515 #endif
516 return false;
519 void AudioCallbackDriver::Init(const nsCString& aStreamName) {
520 LOG(LogLevel::Debug,
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.
528 return;
530 bool fromFallback = fallbackState == FallbackDriverState::Running;
531 cubeb* cubebContext = CubebUtils::GetCubebContext();
532 if (!cubebContext) {
533 NS_WARNING("Could not get cubeb context.");
534 LOG(LogLevel::Warning, ("%s: Could not get cubeb context", __func__));
535 mAudioStreamState = AudioStreamState::None;
536 if (!fromFallback) {
537 CubebUtils::ReportCubebStreamInitFailure(true);
538 FallbackToSystemClockDriver();
540 return;
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;
556 if (!fromFallback) {
557 CubebUtils::ReportCubebStreamInitFailure(firstStream);
558 FallbackToSystemClockDriver();
560 return;
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);
576 mScratchBuffer =
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);
608 #endif
609 LOG(LogLevel::Debug, ("Effective latency in frames: %d", latencyFrames));
611 input = output;
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);
632 DebugOnly<int> rv =
633 cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
634 NS_WARNING_ASSERTION(
635 rv == CUBEB_OK,
636 "Could not set the audio stream volume in GraphDriver.cpp");
637 CubebUtils::ReportCubebBackendUsed();
638 } else {
639 NS_WARNING(
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
645 // failure.
646 if (!fromFallback) {
647 CubebUtils::ReportCubebStreamInitFailure(firstStream);
648 FallbackToSystemClockDriver();
650 return;
653 #ifdef XP_MACOSX
654 PanOutputIfNeeded(inputWanted);
655 #endif
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
662 // disabled.
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()));
669 return;
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();
701 }));
702 } else {
703 LOG(LogLevel::Debug,
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);
716 }));
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.");
728 return false;
731 return true;
734 void AudioCallbackDriver::Stop() {
735 LOG(LogLevel::Debug,
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.");
742 } else {
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;
755 if (fallback) {
756 LOG(LogLevel::Debug,
757 ("%p: Releasing fallback driver %p.", Graph(), fallback.get()));
758 fallback->Shutdown();
761 LOG(LogLevel::Debug,
762 ("%p: Releasing audio driver off main thread (GraphDriver::Shutdown).",
763 Graph()));
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) {
775 return;
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();
783 if (fallback) {
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);
795 }));
799 /* static */
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),
806 aFrames);
809 /* static */
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);
816 /* static */
817 void AudioCallbackDriver::DeviceChangedCallback_s(void* aUser) {
818 AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
819 driver->DeviceChangedCallback();
822 AudioCallbackDriver::AutoInCallback::AutoInCallback(
823 AudioCallbackDriver* aDriver)
824 : mDriver(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) {
837 mAudioThreadId = id;
838 return true;
840 return false;
843 long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
844 AudioDataValue* aOutputBuffer,
845 long aFrames) {
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",
855 Graph(), this));
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
861 // sleeping.
862 LOG(LogLevel::Verbose,
863 ("%p: AudioCallbackDriver %p Waiting for the Fallback driver to stop",
864 Graph(), this));
865 EnsureNextIteration();
866 PodZero(aOutputBuffer, aFrames * mOutputChannelCount);
867 return aFrames;
870 if (MOZ_UNLIKELY(fallbackState == FallbackDriverState::Stopped)) {
871 // We're supposed to stop.
872 PodZero(aOutputBuffer, aFrames * mOutputChannelCount);
873 if (!mSandboxed) {
874 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId);
876 return aFrames - 1;
879 MOZ_ASSERT(mAudioStreamState == AudioStreamState::Running);
880 TRACE_AUDIO_CALLBACK_BUDGET("AudioCallbackDriver real-time budget", aFrames,
881 mSampleRate);
882 TRACE("AudioCallbackDriver::DataCallback");
884 #ifdef DEBUG
885 AutoInCallback aic(this);
886 #endif
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;
894 } else {
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);
958 #endif
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);
968 #ifdef XP_MACOSX
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;
978 #endif
980 // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable
981 if (aInputBuffer) {
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.
993 result.Stopped();
994 // Update the flag before handing over the graph and going to drain.
995 mAudioStreamState = AudioStreamState::Stopping;
996 if (!mSandboxed) {
997 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId);
999 return aFrames - 1;
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();
1009 result.Switched();
1010 mAudioStreamState = AudioStreamState::Stopping;
1011 nextDriver->SetState(mStreamName, mIterationEnd, mStateComputedTime);
1012 nextDriver->Start();
1013 if (!mSandboxed) {
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.
1018 return aFrames - 1;
1021 MOZ_ASSERT(result.IsStillProcessing());
1022 return aFrames;
1025 static const char* StateToString(cubeb_state aState) {
1026 switch (aState) {
1027 case CUBEB_STATE_STARTED:
1028 return "STARTED";
1029 case CUBEB_STATE_STOPPED:
1030 return "STOPPED";
1031 case CUBEB_STATE_DRAINED:
1032 return "DRAINED";
1033 case CUBEB_STATE_ERROR:
1034 return "ERROR";
1035 default:
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
1052 return;
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.
1060 return;
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) {
1076 #ifdef DEBUG
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);
1080 #endif
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) {
1112 #ifdef XP_MACOSX
1113 TRACE("AudioCallbackDriver::PanOutputIfNeeded");
1114 cubeb_device* out = nullptr;
1115 int rv;
1116 char name[128];
1117 size_t length = sizeof(name);
1119 rv = sysctlbyname("hw.model", name, &length, NULL, 0);
1120 if (rv) {
1121 return;
1124 int major, minor;
1125 for (uint32_t i = 0; i < length; i++) {
1126 // skip the model name
1127 if (isalpha(name[i])) {
1128 continue;
1130 sscanf(name + i, "%d,%d", &major, &minor);
1131 break;
1134 enum MacbookModel { MacBook, MacBookPro, MacBookAir, NotAMacbook };
1136 MacbookModel model;
1138 if (!strncmp(name, "MacBookPro", length)) {
1139 model = MacBookPro;
1140 } else if (strncmp(name, "MacBookAir", length)) {
1141 model = MacBookAir;
1142 } else if (strncmp(name, "MacBook", length)) {
1143 model = MacBook;
1144 } else {
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) {
1151 MOZ_ASSERT(out);
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;
1158 } else {
1159 LOG(LogLevel::Debug, ("Using an external output device"));
1160 mNeedsPanning = false;
1162 cubeb_stream_device_destroy(mAudioStream, out);
1165 #endif
1168 void AudioCallbackDriver::DeviceChangedCallback() {
1169 MOZ_ASSERT(!InIteration());
1170 // Tell the audio engine the device has changed, it might want to reset some
1171 // state.
1172 Graph()->DeviceChanged();
1173 #ifdef XP_MACOSX
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);
1179 }));
1180 #endif
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) /
1208 mSampleRate);
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(),
1223 this));
1224 mFallbackDriverState = FallbackDriverState::Running;
1225 mNextReInitBackoffStep =
1226 TimeDuration::FromMilliseconds(AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS);
1227 mNextReInitAttempt = TimeStamp::Now() + mNextReInitBackoffStep;
1228 auto fallback =
1229 MakeRefPtr<FallbackWrapper>(Graph(), this, mSampleRate, mStreamName,
1230 mIterationEnd, mStateComputedTime);
1232 auto driver = mFallback.Lock();
1233 driver.ref() = fallback;
1235 fallback->Start();
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));
1263 return;
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()));
1271 return;
1274 LOG(LogLevel::Debug, ("%p: AudioCallbackDriver %p Attempting to re-init "
1275 "audio stream from fallback driver.",
1276 Graph(), this));
1277 mNextReInitBackoffStep =
1278 std::min(mNextReInitBackoffStep * 2,
1279 TimeDuration::FromMilliseconds(
1280 StaticPrefs::media_audio_device_retry_ms()));
1281 mNextReInitAttempt = now + mNextReInitBackoffStep;
1282 Start();
1285 } // namespace mozilla
1287 // avoid redefined macro in unified build
1288 #undef LOG