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/. */
6 
7 #include "GraphDriver.h"
8 
9 #include "AudioNodeEngine.h"
10 #include "mozilla/dom/AudioContext.h"
11 #include "mozilla/dom/AudioDeviceInfo.h"
12 #include "mozilla/dom/BaseAudioContextBinding.h"
13 #include "mozilla/SchedulerGroup.h"
14 #include "mozilla/SharedThreadPool.h"
15 #include "mozilla/ClearOnShutdown.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/MathAlgorithms.h"
18 #include "CubebDeviceEnumerator.h"
19 #include "MediaTrackGraphImpl.h"
20 #include "AudioThreadRegistry.h"
21 #include "Tracing.h"
22 
23 #ifdef MOZ_WEBRTC
24 #  include "webrtc/MediaEngineWebRTC.h"
25 #endif
26 
27 #ifdef XP_MACOSX
28 #  include <sys/sysctl.h>
29 #endif
30 
31 extern mozilla::LazyLogModule gMediaTrackGraphLog;
32 #ifdef LOG
33 #  undef LOG
34 #endif  // LOG
35 #define LOG(type, msg) MOZ_LOG(gMediaTrackGraphLog, type, msg)
36 
37 namespace mozilla {
38 
GraphDriver(GraphInterface * aGraphInterface,GraphDriver * aPreviousDriver,uint32_t aSampleRate)39 GraphDriver::GraphDriver(GraphInterface* aGraphInterface,
40                          GraphDriver* aPreviousDriver, uint32_t aSampleRate)
41     : mGraphInterface(aGraphInterface),
42       mSampleRate(aSampleRate),
43       mPreviousDriver(aPreviousDriver) {}
44 
SetState(GraphTime aIterationStart,GraphTime aIterationEnd,GraphTime aStateComputedTime)45 void GraphDriver::SetState(GraphTime aIterationStart, GraphTime aIterationEnd,
46                            GraphTime aStateComputedTime) {
47   MOZ_ASSERT(InIteration() || !ThreadRunning());
48 
49   mIterationStart = aIterationStart;
50   mIterationEnd = aIterationEnd;
51   mStateComputedTime = aStateComputedTime;
52 }
53 
54 #ifdef DEBUG
InIteration() const55 bool GraphDriver::InIteration() const {
56   return OnThread() || Graph()->InDriverIteration(this);
57 }
58 #endif
59 
PreviousDriver()60 GraphDriver* GraphDriver::PreviousDriver() {
61   MOZ_ASSERT(InIteration() || !ThreadRunning());
62   return mPreviousDriver;
63 }
64 
SetPreviousDriver(GraphDriver * aPreviousDriver)65 void GraphDriver::SetPreviousDriver(GraphDriver* aPreviousDriver) {
66   MOZ_ASSERT(InIteration() || !ThreadRunning());
67   mPreviousDriver = aPreviousDriver;
68 }
69 
ThreadedDriver(GraphInterface * aGraphInterface,GraphDriver * aPreviousDriver,uint32_t aSampleRate)70 ThreadedDriver::ThreadedDriver(GraphInterface* aGraphInterface,
71                                GraphDriver* aPreviousDriver,
72                                uint32_t aSampleRate)
73     : GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate),
74       mThreadRunning(false) {}
75 
76 class MediaTrackGraphShutdownThreadRunnable : public Runnable {
77  public:
MediaTrackGraphShutdownThreadRunnable(already_AddRefed<nsIThread> aThread)78   explicit MediaTrackGraphShutdownThreadRunnable(
79       already_AddRefed<nsIThread> aThread)
80       : Runnable("MediaTrackGraphShutdownThreadRunnable"), mThread(aThread) {}
Run()81   NS_IMETHOD Run() override {
82     MOZ_ASSERT(NS_IsMainThread());
83     MOZ_ASSERT(mThread);
84 
85     mThread->Shutdown();
86     mThread = nullptr;
87     return NS_OK;
88   }
89 
90  private:
91   nsCOMPtr<nsIThread> mThread;
92 };
93 
~ThreadedDriver()94 ThreadedDriver::~ThreadedDriver() {
95   if (mThread) {
96     nsCOMPtr<nsIRunnable> event =
97         new MediaTrackGraphShutdownThreadRunnable(mThread.forget());
98     SchedulerGroup::Dispatch(TaskCategory::Other, event.forget());
99   }
100 }
101 
102 class MediaTrackGraphInitThreadRunnable : public Runnable {
103  public:
MediaTrackGraphInitThreadRunnable(ThreadedDriver * aDriver)104   explicit MediaTrackGraphInitThreadRunnable(ThreadedDriver* aDriver)
105       : Runnable("MediaTrackGraphInitThreadRunnable"), mDriver(aDriver) {}
Run()106   NS_IMETHOD Run() override {
107     MOZ_ASSERT(!mDriver->ThreadRunning());
108     LOG(LogLevel::Debug, ("Starting a new system driver for graph %p",
109                           mDriver->mGraphInterface.get()));
110 
111     if (GraphDriver* previousDriver = mDriver->PreviousDriver()) {
112       LOG(LogLevel::Debug,
113           ("%p releasing an AudioCallbackDriver(%p), for graph %p",
114            mDriver.get(), previousDriver, mDriver->Graph()));
115       MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
116       RefPtr<AsyncCubebTask> releaseEvent =
117           new AsyncCubebTask(previousDriver->AsAudioCallbackDriver(),
118                              AsyncCubebOperation::SHUTDOWN);
119       releaseEvent->Dispatch();
120       mDriver->SetPreviousDriver(nullptr);
121     }
122 
123     mDriver->RunThread();
124     return NS_OK;
125   }
126 
127  private:
128   RefPtr<ThreadedDriver> mDriver;
129 };
130 
Start()131 void ThreadedDriver::Start() {
132   MOZ_ASSERT(!ThreadRunning());
133   LOG(LogLevel::Debug,
134       ("Starting thread for a SystemClockDriver  %p", mGraphInterface.get()));
135   Unused << NS_WARN_IF(mThread);
136   MOZ_ASSERT(!mThread);  // Ensure we haven't already started it
137 
138   nsCOMPtr<nsIRunnable> event = new MediaTrackGraphInitThreadRunnable(this);
139   // Note: mThread may be null during event->Run() if we pass to NewNamedThread!
140   // See AudioInitTask
141   nsresult rv = NS_NewNamedThread("MediaTrackGrph", getter_AddRefs(mThread));
142   if (NS_SUCCEEDED(rv)) {
143     mThread->EventTarget()->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
144   }
145 }
146 
Shutdown()147 void ThreadedDriver::Shutdown() {
148   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
149   // mGraph's thread is not running so it's OK to do whatever here
150   LOG(LogLevel::Debug, ("Stopping threads for MediaTrackGraph %p", this));
151 
152   if (mThread) {
153     LOG(LogLevel::Debug,
154         ("%p: Stopping ThreadedDriver's %p thread", Graph(), this));
155     mThread->Shutdown();
156     mThread = nullptr;
157   }
158 }
159 
SystemClockDriver(GraphInterface * aGraphInterface,GraphDriver * aPreviousDriver,uint32_t aSampleRate)160 SystemClockDriver::SystemClockDriver(GraphInterface* aGraphInterface,
161                                      GraphDriver* aPreviousDriver,
162                                      uint32_t aSampleRate)
163     : ThreadedDriver(aGraphInterface, aPreviousDriver, aSampleRate),
164       mInitialTimeStamp(TimeStamp::Now()),
165       mCurrentTimeStamp(TimeStamp::Now()),
166       mLastTimeStamp(TimeStamp::Now()) {}
167 
168 SystemClockDriver::~SystemClockDriver() = default;
169 
RunThread()170 void ThreadedDriver::RunThread() {
171   mThreadRunning = true;
172   while (true) {
173     mIterationStart = mIterationEnd;
174     mIterationEnd += GetIntervalForIteration();
175 
176     if (mStateComputedTime < mIterationEnd) {
177       LOG(LogLevel::Warning, ("%p: Global underrun detected", Graph()));
178       mIterationEnd = mStateComputedTime;
179     }
180 
181     if (mIterationStart >= mIterationEnd) {
182       NS_ASSERTION(mIterationStart == mIterationEnd,
183                    "Time can't go backwards!");
184       // This could happen due to low clock resolution, maybe?
185       LOG(LogLevel::Debug, ("%p: Time did not advance", Graph()));
186     }
187 
188     GraphTime nextStateComputedTime =
189         MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(
190             mIterationEnd + MillisecondsToMediaTime(AUDIO_TARGET_MS));
191     if (nextStateComputedTime < mStateComputedTime) {
192       // A previous driver may have been processing further ahead of
193       // iterationEnd.
194       LOG(LogLevel::Warning,
195           ("%p: Prevent state from going backwards. interval[%ld; %ld] "
196            "state[%ld; "
197            "%ld]",
198            Graph(), (long)mIterationStart, (long)mIterationEnd,
199            (long)mStateComputedTime, (long)nextStateComputedTime));
200       nextStateComputedTime = mStateComputedTime;
201     }
202     LOG(LogLevel::Verbose,
203         ("%p: interval[%ld; %ld] state[%ld; %ld]", Graph(),
204          (long)mIterationStart, (long)mIterationEnd, (long)mStateComputedTime,
205          (long)nextStateComputedTime));
206 
207     mStateComputedTime = nextStateComputedTime;
208     IterationResult result =
209         Graph()->OneIteration(mStateComputedTime, mIterationEnd, nullptr);
210 
211     if (result.IsStop()) {
212       // Signal that we're done stopping.
213       result.Stopped();
214       break;
215     }
216     WaitForNextIteration();
217     if (GraphDriver* nextDriver = result.NextDriver()) {
218       LOG(LogLevel::Debug, ("%p: Switching to AudioCallbackDriver", Graph()));
219       result.Switched();
220       nextDriver->SetState(mIterationStart, mIterationEnd, mStateComputedTime);
221       nextDriver->Start();
222       break;
223     }
224     MOZ_ASSERT(result.IsStillProcessing());
225   }
226   mThreadRunning = false;
227 }
228 
GetIntervalForIteration()229 MediaTime SystemClockDriver::GetIntervalForIteration() {
230   TimeStamp now = TimeStamp::Now();
231   MediaTime interval =
232       SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds());
233   mCurrentTimeStamp = now;
234 
235   MOZ_LOG(gMediaTrackGraphLog, LogLevel::Verbose,
236           ("%p: Updating current time to %f (real %f, StateComputedTime() %f)",
237            Graph(), MediaTimeToSeconds(mIterationEnd + interval),
238            (now - mInitialTimeStamp).ToSeconds(),
239            MediaTimeToSeconds(mStateComputedTime)));
240 
241   return interval;
242 }
243 
EnsureNextIteration()244 void ThreadedDriver::EnsureNextIteration() {
245   mWaitHelper.EnsureNextIteration();
246 }
247 
WaitForNextIteration()248 void ThreadedDriver::WaitForNextIteration() {
249   MOZ_ASSERT(mThread);
250   MOZ_ASSERT(OnThread());
251   mWaitHelper.WaitForNextIterationAtLeast(WaitInterval());
252 }
253 
WaitInterval()254 TimeDuration SystemClockDriver::WaitInterval() {
255   MOZ_ASSERT(mThread);
256   MOZ_ASSERT(OnThread());
257   TimeStamp now = TimeStamp::Now();
258   int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
259                       int64_t((now - mCurrentTimeStamp).ToMilliseconds());
260   // Make sure timeoutMS doesn't overflow 32 bits by waking up at
261   // least once a minute, if we need to wake up at all
262   timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60 * 1000));
263   LOG(LogLevel::Verbose,
264       ("%p: Waiting for next iteration; at %f, timeout=%f", Graph(),
265        (now - mInitialTimeStamp).ToSeconds(), timeoutMS / 1000.0));
266 
267   return TimeDuration::FromMilliseconds(timeoutMS);
268 }
269 
OfflineClockDriver(GraphInterface * aGraphInterface,uint32_t aSampleRate,GraphTime aSlice)270 OfflineClockDriver::OfflineClockDriver(GraphInterface* aGraphInterface,
271                                        uint32_t aSampleRate, GraphTime aSlice)
272     : ThreadedDriver(aGraphInterface, nullptr, aSampleRate), mSlice(aSlice) {}
273 
274 OfflineClockDriver::~OfflineClockDriver() = default;
275 
RunThread()276 void OfflineClockDriver::RunThread() {
277   nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(mThread);
278   nsCOMPtr<nsIThreadObserver> observer = do_QueryInterface(Graph());
279   threadInternal->SetObserver(observer);
280 
281   ThreadedDriver::RunThread();
282 }
283 
GetIntervalForIteration()284 MediaTime OfflineClockDriver::GetIntervalForIteration() {
285   return MillisecondsToMediaTime(mSlice);
286 }
287 
AsyncCubebTask(AudioCallbackDriver * aDriver,AsyncCubebOperation aOperation)288 AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver,
289                                AsyncCubebOperation aOperation)
290     : Runnable("AsyncCubebTask"),
291       mDriver(aDriver),
292       mOperation(aOperation),
293       mShutdownGrip(aDriver->Graph()) {
294   NS_WARNING_ASSERTION(
295       mDriver->mAudioStream || aOperation == AsyncCubebOperation::INIT,
296       "No audio stream!");
297 }
298 
299 AsyncCubebTask::~AsyncCubebTask() = default;
300 
301 NS_IMETHODIMP
Run()302 AsyncCubebTask::Run() {
303   MOZ_ASSERT(mDriver);
304 
305   switch (mOperation) {
306     case AsyncCubebOperation::INIT: {
307       LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::INIT driver=%p",
308                             mDriver->Graph(), mDriver.get()));
309       mDriver->Init();
310       break;
311     }
312     case AsyncCubebOperation::SHUTDOWN: {
313       LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::SHUTDOWN driver=%p",
314                             mDriver->Graph(), mDriver.get()));
315       mDriver->Stop();
316       mDriver = nullptr;
317       mShutdownGrip = nullptr;
318       break;
319     }
320     default:
321       MOZ_CRASH("Operation not implemented.");
322   }
323 
324   // The thread will kill itself after a bit
325   return NS_OK;
326 }
327 
TrackAndPromiseForOperation(MediaTrack * aTrack,dom::AudioContextOperation aOperation,AbstractThread * aMainThread,MozPromiseHolder<MediaTrackGraph::AudioContextOperationPromise> && aHolder)328 TrackAndPromiseForOperation::TrackAndPromiseForOperation(
329     MediaTrack* aTrack, dom::AudioContextOperation aOperation,
330     AbstractThread* aMainThread,
331     MozPromiseHolder<MediaTrackGraph::AudioContextOperationPromise>&& aHolder)
332     : mTrack(aTrack),
333       mOperation(aOperation),
334       mMainThread(aMainThread),
335       mHolder(std::move(aHolder)) {}
336 
TrackAndPromiseForOperation(TrackAndPromiseForOperation && aOther)337 TrackAndPromiseForOperation::TrackAndPromiseForOperation(
338     TrackAndPromiseForOperation&& aOther) noexcept
339     : mTrack(std::move(aOther.mTrack)),
340       mOperation(aOther.mOperation),
341       mMainThread(std::move(aOther.mMainThread)),
342       mHolder(std::move(aOther.mHolder)) {}
343 
344 /* Helper to proxy the GraphInterface methods used by a running
345  * mFallbackDriver. */
346 class AudioCallbackDriver::FallbackWrapper : public GraphInterface {
347  public:
FallbackWrapper(RefPtr<GraphInterface> aGraph,RefPtr<AudioCallbackDriver> aOwner,uint32_t aSampleRate,GraphTime aIterationStart,GraphTime aIterationEnd,GraphTime aStateComputedTime)348   FallbackWrapper(RefPtr<GraphInterface> aGraph,
349                   RefPtr<AudioCallbackDriver> aOwner, uint32_t aSampleRate,
350                   GraphTime aIterationStart, GraphTime aIterationEnd,
351                   GraphTime aStateComputedTime)
352       : mGraph(std::move(aGraph)),
353         mOwner(std::move(aOwner)),
354         mFallbackDriver(
355             MakeRefPtr<SystemClockDriver>(this, nullptr, aSampleRate)),
356         mIterationStart(aIterationStart),
357         mIterationEnd(aIterationEnd),
358         mStateComputedTime(aStateComputedTime) {
359     mFallbackDriver->SetState(mIterationStart, mIterationEnd,
360                               mStateComputedTime);
361   }
362 
363   NS_DECL_THREADSAFE_ISUPPORTS
364 
365   /* Proxied SystemClockDriver methods */
SetState(GraphTime aIterationStart,GraphTime aIterationEnd,GraphTime aStateComputedTime)366   void SetState(GraphTime aIterationStart, GraphTime aIterationEnd,
367                 GraphTime aStateComputedTime) {
368     mIterationStart = aIterationStart;
369     mIterationEnd = aIterationEnd;
370     mStateComputedTime = aStateComputedTime;
371     mFallbackDriver->SetState(aIterationStart, aIterationEnd,
372                               aStateComputedTime);
373   }
Start()374   void Start() { mFallbackDriver->Start(); }
Shutdown()375   MOZ_CAN_RUN_SCRIPT void Shutdown() {
376     RefPtr<SystemClockDriver> driver = mFallbackDriver;
377     driver->Shutdown();
378   }
EnsureNextIteration()379   void EnsureNextIteration() { mFallbackDriver->EnsureNextIteration(); }
380 #ifdef DEBUG
InIteration()381   bool InIteration() { return mFallbackDriver->InIteration(); }
382 #endif
OnThread()383   bool OnThread() { return mFallbackDriver->OnThread(); }
384 
385   /* GraphInterface methods */
NotifyOutputData(AudioDataValue * aBuffer,size_t aFrames,TrackRate aRate,uint32_t aChannels)386   void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
387                         TrackRate aRate, uint32_t aChannels) override {
388     MOZ_CRASH("Unexpected NotifyOutputData from fallback SystemClockDriver");
389   }
NotifyInputStopped()390   void NotifyInputStopped() override {
391     MOZ_CRASH("Unexpected NotifyInputStopped from fallback SystemClockDriver");
392   }
NotifyInputData(const AudioDataValue * aBuffer,size_t aFrames,TrackRate aRate,uint32_t aChannels,uint32_t aAlreadyBuffered)393   void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
394                        TrackRate aRate, uint32_t aChannels,
395                        uint32_t aAlreadyBuffered) override {
396     MOZ_CRASH("Unexpected NotifyInputData from fallback SystemClockDriver");
397   }
DeviceChanged()398   void DeviceChanged() override {
399     MOZ_CRASH("Unexpected DeviceChanged from fallback SystemClockDriver");
400   }
401 #ifdef DEBUG
InDriverIteration(const GraphDriver * aDriver) const402   bool InDriverIteration(const GraphDriver* aDriver) const override {
403     return !mOwner->ThreadRunning() && mOwner->InIteration();
404   }
405 #endif
OneIteration(GraphTime aStateComputedEnd,GraphTime aIterationEnd,AudioMixer * aMixer)406   IterationResult OneIteration(GraphTime aStateComputedEnd,
407                                GraphTime aIterationEnd,
408                                AudioMixer* aMixer) override {
409     MOZ_ASSERT(!aMixer);
410 
411 #ifdef DEBUG
412     AutoInCallback aic(mOwner);
413 #endif
414 
415     mIterationStart = mIterationEnd;
416     mIterationEnd = aIterationEnd;
417     mStateComputedTime = aStateComputedEnd;
418     IterationResult result =
419         mGraph->OneIteration(aStateComputedEnd, aIterationEnd, aMixer);
420 
421     AudioStreamState audioState = mOwner->mAudioStreamState;
422 
423     MOZ_ASSERT(audioState != AudioStreamState::Stopping,
424                "The audio driver can only enter stopping if it iterated the "
425                "graph, which it can only do if there's no fallback driver");
426     if (audioState != AudioStreamState::Running && result.IsStillProcessing()) {
427       mOwner->MaybeStartAudioStream();
428       return result;
429     }
430 
431     MOZ_ASSERT(result.IsStillProcessing() || result.IsStop() ||
432                result.IsSwitchDriver());
433 
434     // Proxy the release of the fallback driver to a background thread, so it
435     // doesn't perform unexpected suicide.
436     IterationResult stopFallback =
437         IterationResult::CreateStop(NS_NewRunnableFunction(
438             "AudioCallbackDriver::FallbackDriverStopped",
439             [self = RefPtr<FallbackWrapper>(this), this,
440              result = std::move(result)]() mutable {
441               FallbackDriverState fallbackState =
442                   result.IsStillProcessing() ? FallbackDriverState::None
443                                              : FallbackDriverState::Stopped;
444               mOwner->FallbackDriverStopped(mIterationStart, mIterationEnd,
445                                             mStateComputedTime, fallbackState);
446 
447               if (fallbackState == FallbackDriverState::Stopped) {
448 #ifdef DEBUG
449                 // The AudioCallbackDriver may not iterate the graph, but we'll
450                 // call into it so we need to be regarded as "in iteration".
451                 AutoInCallback aic(mOwner);
452 #endif
453                 if (GraphDriver* nextDriver = result.NextDriver()) {
454                   LOG(LogLevel::Debug,
455                       ("%p: Switching from fallback to other driver.",
456                        mGraph.get()));
457                   result.Switched();
458                   nextDriver->SetState(mIterationStart, mIterationEnd,
459                                        mStateComputedTime);
460                   nextDriver->Start();
461                 } else if (result.IsStop()) {
462                   LOG(LogLevel::Debug,
463                       ("%p: Stopping fallback driver.", mGraph.get()));
464                   result.Stopped();
465                 }
466               }
467               mOwner = nullptr;
468               NS_DispatchBackgroundTask(NS_NewRunnableFunction(
469                   "AudioCallbackDriver::FallbackDriverStopped::Release",
470                   [fallback = std::move(self->mFallbackDriver)] {}));
471             }));
472 
473     return stopFallback;
474   }
475 
476  private:
477   virtual ~FallbackWrapper() = default;
478 
479   const RefPtr<GraphInterface> mGraph;
480   // Valid until mFallbackDriver has finished its last iteration.
481   RefPtr<AudioCallbackDriver> mOwner;
482   RefPtr<SystemClockDriver> mFallbackDriver;
483 
484   GraphTime mIterationStart;
485   GraphTime mIterationEnd;
486   GraphTime mStateComputedTime;
487 };
488 
NS_IMPL_ISUPPORTS0(AudioCallbackDriver::FallbackWrapper)489 NS_IMPL_ISUPPORTS0(AudioCallbackDriver::FallbackWrapper)
490 
491 AudioCallbackDriver::AudioCallbackDriver(
492     GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
493     uint32_t aSampleRate, uint32_t aOutputChannelCount,
494     uint32_t aInputChannelCount, CubebUtils::AudioDeviceID aOutputDeviceID,
495     CubebUtils::AudioDeviceID aInputDeviceID, AudioInputType aAudioInputType)
496     : GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate),
497       mOutputChannelCount(aOutputChannelCount),
498       mInputChannelCount(aInputChannelCount),
499       mOutputDeviceID(aOutputDeviceID),
500       mInputDeviceID(aInputDeviceID),
501       mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS),
502       mStarted(false),
503       mInitShutdownThread(SharedThreadPool::Get("CubebOperation"_ns, 1)),
504       mAudioThreadId(0),
505       mAudioThreadIdInCb(std::thread::id()),
506       mAudioStreamState(AudioStreamState::None),
507       mFallback("AudioCallbackDriver::mFallback"),
508       mSandboxed(CubebUtils::SandboxEnabled()) {
509   LOG(LogLevel::Debug, ("%p: AudioCallbackDriver ctor", Graph()));
510 
511   NS_WARNING_ASSERTION(mOutputChannelCount != 0,
512                        "Invalid output channel count");
513   MOZ_ASSERT(mOutputChannelCount <= 8);
514 
515   const uint32_t kIdleThreadTimeoutMs = 2000;
516   mInitShutdownThread->SetIdleThreadTimeout(
517       PR_MillisecondsToInterval(kIdleThreadTimeoutMs));
518 
519   if (aAudioInputType == AudioInputType::Voice) {
520     LOG(LogLevel::Debug, ("VOICE."));
521     mInputDevicePreference = CUBEB_DEVICE_PREF_VOICE;
522     CubebUtils::SetInCommunication(true);
523   } else {
524     mInputDevicePreference = CUBEB_DEVICE_PREF_ALL;
525   }
526 
527   mMixer.AddCallback(WrapNotNull(this));
528 }
529 
~AudioCallbackDriver()530 AudioCallbackDriver::~AudioCallbackDriver() {
531   if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
532     CubebUtils::SetInCommunication(false);
533   }
534 }
535 
IsMacbookOrMacbookAir()536 bool IsMacbookOrMacbookAir() {
537 #ifdef XP_MACOSX
538   size_t len = 0;
539   sysctlbyname("hw.model", NULL, &len, NULL, 0);
540   if (len) {
541     UniquePtr<char[]> model(new char[len]);
542     // This string can be
543     // MacBook%d,%d for a normal MacBook
544     // MacBookPro%d,%d for a MacBook Pro
545     // MacBookAir%d,%d for a Macbook Air
546     sysctlbyname("hw.model", model.get(), &len, NULL, 0);
547     char* substring = strstr(model.get(), "MacBook");
548     if (substring) {
549       const size_t offset = strlen("MacBook");
550       if (!strncmp(model.get() + offset, "Air", 3) ||
551           isdigit(model[offset + 1])) {
552         return true;
553       }
554     }
555   }
556 #endif
557   return false;
558 }
559 
Init()560 void AudioCallbackDriver::Init() {
561   TRACE();
562   MOZ_ASSERT(OnCubebOperationThread());
563   MOZ_ASSERT(mAudioStreamState == AudioStreamState::Pending);
564   FallbackDriverState fallbackState = mFallbackDriverState;
565   if (fallbackState == FallbackDriverState::Stopped) {
566     // The graph has already stopped us.
567     return;
568   }
569   bool fromFallback = fallbackState == FallbackDriverState::Running;
570   cubeb* cubebContext = CubebUtils::GetCubebContext();
571   if (!cubebContext) {
572     NS_WARNING("Could not get cubeb context.");
573     LOG(LogLevel::Warning, ("%s: Could not get cubeb context", __func__));
574     mAudioStreamState = AudioStreamState::None;
575     if (!fromFallback) {
576       CubebUtils::ReportCubebStreamInitFailure(true);
577       FallbackToSystemClockDriver();
578     }
579     return;
580   }
581 
582   cubeb_stream_params output;
583   cubeb_stream_params input;
584   bool firstStream = CubebUtils::GetFirstStream();
585 
586   MOZ_ASSERT(!NS_IsMainThread(),
587              "This is blocking and should never run on the main thread.");
588 
589   output.rate = mSampleRate;
590 
591   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
592     output.format = CUBEB_SAMPLE_S16NE;
593   } else {
594     output.format = CUBEB_SAMPLE_FLOAT32NE;
595   }
596 
597   if (!mOutputChannelCount) {
598     LOG(LogLevel::Warning, ("Output number of channels is 0."));
599     mAudioStreamState = AudioStreamState::None;
600     if (!fromFallback) {
601       CubebUtils::ReportCubebStreamInitFailure(firstStream);
602       FallbackToSystemClockDriver();
603     }
604     return;
605   }
606 
607   CubebUtils::AudioDeviceID forcedOutputDeviceId = nullptr;
608 
609   char* forcedOutputDeviceName = CubebUtils::GetForcedOutputDevice();
610   if (forcedOutputDeviceName) {
611     RefPtr<CubebDeviceEnumerator> enumerator = Enumerator::GetInstance();
612     RefPtr<AudioDeviceInfo> device = enumerator->DeviceInfoFromName(
613         NS_ConvertUTF8toUTF16(forcedOutputDeviceName), EnumeratorSide::OUTPUT);
614     if (device && device->DeviceID()) {
615       forcedOutputDeviceId = device->DeviceID();
616     }
617   }
618 
619   mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannelCount);
620   mScratchBuffer =
621       SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2>(mOutputChannelCount);
622 
623   output.channels = mOutputChannelCount;
624   AudioConfig::ChannelLayout::ChannelMap channelMap =
625       AudioConfig::ChannelLayout(mOutputChannelCount).Map();
626 
627   output.layout = static_cast<uint32_t>(channelMap);
628   output.prefs = CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_OUTPUT);
629   if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE &&
630       CubebUtils::RouteOutputAsVoice()) {
631     output.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE);
632   }
633 
634   uint32_t latencyFrames = CubebUtils::GetCubebMTGLatencyInFrames(&output);
635 
636   // Macbook and MacBook air don't have enough CPU to run very low latency
637   // MediaTrackGraphs, cap the minimal latency to 512 frames int this case.
638   if (IsMacbookOrMacbookAir()) {
639     latencyFrames = std::max((uint32_t)512, latencyFrames);
640   }
641 
642   // On OSX, having a latency that is lower than 10ms is very common. It's
643   // not very useful when doing voice, because all the WebRTC code deal in 10ms
644   // chunks of audio.  Take the first power of two above 10ms at the current
645   // rate in this case. It's probably 512, for common rates.
646 #if defined(XP_MACOSX)
647   if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
648     if (latencyFrames < mSampleRate / 100) {
649       latencyFrames = mozilla::RoundUpPow2(mSampleRate / 100);
650     }
651   }
652 #endif
653   LOG(LogLevel::Debug, ("Effective latency in frames: %d", latencyFrames));
654 
655   input = output;
656   input.channels = mInputChannelCount;
657   input.layout = CUBEB_LAYOUT_UNDEFINED;
658   input.prefs = CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_INPUT);
659   if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
660     input.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE);
661   }
662 
663   cubeb_stream* stream = nullptr;
664   bool inputWanted = mInputChannelCount > 0;
665   CubebUtils::AudioDeviceID outputId = mOutputDeviceID;
666   CubebUtils::AudioDeviceID inputId = mInputDeviceID;
667 
668   // XXX Only pass input input if we have an input listener.  Always
669   // set up output because it's easier, and it will just get silence.
670   if (cubeb_stream_init(cubebContext, &stream, "AudioCallbackDriver", inputId,
671                         inputWanted ? &input : nullptr,
672                         forcedOutputDeviceId ? forcedOutputDeviceId : outputId,
673                         &output, latencyFrames, DataCallback_s, StateCallback_s,
674                         this) == CUBEB_OK) {
675     mAudioStream.own(stream);
676     DebugOnly<int> rv =
677         cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
678     NS_WARNING_ASSERTION(
679         rv == CUBEB_OK,
680         "Could not set the audio stream volume in GraphDriver.cpp");
681     CubebUtils::ReportCubebBackendUsed();
682   } else {
683     NS_WARNING(
684         "Could not create a cubeb stream for MediaTrackGraph, falling "
685         "back to a SystemClockDriver");
686     mAudioStreamState = AudioStreamState::None;
687     // Only report failures when we're not coming from a driver that was
688     // created itself as a fallback driver because of a previous audio driver
689     // failure.
690     if (!fromFallback) {
691       CubebUtils::ReportCubebStreamInitFailure(firstStream);
692       FallbackToSystemClockDriver();
693     }
694     return;
695   }
696 
697 #ifdef XP_MACOSX
698   PanOutputIfNeeded(inputWanted);
699 #endif
700 
701   cubeb_stream_register_device_changed_callback(
702       mAudioStream, AudioCallbackDriver::DeviceChangedCallback_s);
703 
704   // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable. This
705   // is intended for diagnosing issues, and only works if the content sandbox is
706   // disabled.
707   mInputStreamFile.Open("GraphDriverInput", input.channels, input.rate);
708   mOutputStreamFile.Open("GraphDriverOutput", output.channels, output.rate);
709 
710   if (NS_WARN_IF(!StartStream())) {
711     LOG(LogLevel::Warning,
712         ("%p: AudioCallbackDriver couldn't start a cubeb stream.", Graph()));
713     return;
714   }
715 
716   LOG(LogLevel::Debug, ("%p: AudioCallbackDriver started.", Graph()));
717 }
718 
Start()719 void AudioCallbackDriver::Start() {
720   MOZ_ASSERT(!IsStarted());
721   MOZ_ASSERT(mAudioStreamState == AudioStreamState::None);
722   MOZ_ASSERT_IF(PreviousDriver(), PreviousDriver()->InIteration());
723   mAudioStreamState = AudioStreamState::Pending;
724 
725   if (mFallbackDriverState == FallbackDriverState::None) {
726     // Starting an audio driver could take a while. We start a system driver in
727     // the meantime so that the graph is kept running.
728     FallbackToSystemClockDriver();
729   }
730 
731   if (mPreviousDriver) {
732     if (mPreviousDriver->AsAudioCallbackDriver()) {
733       LOG(LogLevel::Debug, ("Releasing audio driver off main thread."));
734       RefPtr<AsyncCubebTask> releaseEvent =
735           new AsyncCubebTask(mPreviousDriver->AsAudioCallbackDriver(),
736                              AsyncCubebOperation::SHUTDOWN);
737       releaseEvent->Dispatch();
738     } else {
739       LOG(LogLevel::Debug,
740           ("Dropping driver reference for SystemClockDriver."));
741       MOZ_ASSERT(mPreviousDriver->AsSystemClockDriver());
742     }
743     mPreviousDriver = nullptr;
744   }
745 
746   LOG(LogLevel::Debug, ("Starting new audio driver off main thread, "
747                         "to ensure it runs after previous shutdown."));
748   RefPtr<AsyncCubebTask> initEvent =
749       new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::INIT);
750   initEvent->Dispatch();
751 }
752 
StartStream()753 bool AudioCallbackDriver::StartStream() {
754   TRACE();
755   MOZ_ASSERT(!IsStarted() && OnCubebOperationThread());
756   // Set mStarted before cubeb_stream_start, since starting the cubeb stream can
757   // result in a callback (that may read mStarted) before mStarted would
758   // otherwise be set to true.
759   mStarted = true;
760   if (cubeb_stream_start(mAudioStream) != CUBEB_OK) {
761     NS_WARNING("Could not start cubeb stream for MTG.");
762     mStarted = false;
763     return false;
764   }
765 
766   return true;
767 }
768 
Stop()769 void AudioCallbackDriver::Stop() {
770   TRACE();
771   MOZ_ASSERT(OnCubebOperationThread());
772   cubeb_stream_register_device_changed_callback(mAudioStream, nullptr);
773   if (cubeb_stream_stop(mAudioStream) != CUBEB_OK) {
774     NS_WARNING("Could not stop cubeb stream for MTG.");
775   }
776 }
777 
Shutdown()778 void AudioCallbackDriver::Shutdown() {
779   MOZ_ASSERT(NS_IsMainThread());
780   RefPtr<FallbackWrapper> fallback;
781   {
782     auto fallbackLock = mFallback.Lock();
783     fallback = fallbackLock.ref();
784     fallbackLock.ref() = nullptr;
785   }
786   if (fallback) {
787     LOG(LogLevel::Debug,
788         ("%p: Releasing fallback driver %p.", Graph(), fallback.get()));
789     fallback->Shutdown();
790   }
791 
792   LOG(LogLevel::Debug,
793       ("%p: Releasing audio driver off main thread (GraphDriver::Shutdown).",
794        Graph()));
795 
796   RefPtr<AsyncCubebTask> releaseEvent =
797       new AsyncCubebTask(this, AsyncCubebOperation::SHUTDOWN);
798   releaseEvent->Dispatch(NS_DISPATCH_SYNC);
799 }
800 
801 /* static */
DataCallback_s(cubeb_stream * aStream,void * aUser,const void * aInputBuffer,void * aOutputBuffer,long aFrames)802 long AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream, void* aUser,
803                                          const void* aInputBuffer,
804                                          void* aOutputBuffer, long aFrames) {
805   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
806   return driver->DataCallback(static_cast<const AudioDataValue*>(aInputBuffer),
807                               static_cast<AudioDataValue*>(aOutputBuffer),
808                               aFrames);
809 }
810 
811 /* static */
StateCallback_s(cubeb_stream * aStream,void * aUser,cubeb_state aState)812 void AudioCallbackDriver::StateCallback_s(cubeb_stream* aStream, void* aUser,
813                                           cubeb_state aState) {
814   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
815   driver->StateCallback(aState);
816 }
817 
818 /* static */
DeviceChangedCallback_s(void * aUser)819 void AudioCallbackDriver::DeviceChangedCallback_s(void* aUser) {
820   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
821   driver->DeviceChangedCallback();
822 }
823 
AutoInCallback(AudioCallbackDriver * aDriver)824 AudioCallbackDriver::AutoInCallback::AutoInCallback(
825     AudioCallbackDriver* aDriver)
826     : mDriver(aDriver) {
827   MOZ_ASSERT(mDriver->mAudioThreadIdInCb == std::thread::id());
828   mDriver->mAudioThreadIdInCb = std::this_thread::get_id();
829 }
830 
~AutoInCallback()831 AudioCallbackDriver::AutoInCallback::~AutoInCallback() {
832   MOZ_ASSERT(mDriver->mAudioThreadIdInCb == std::this_thread::get_id());
833   mDriver->mAudioThreadIdInCb = std::thread::id();
834 }
835 
CheckThreadIdChanged()836 bool AudioCallbackDriver::CheckThreadIdChanged() {
837 #ifdef MOZ_GECKO_PROFILER
838   auto id = profiler_current_thread_id();
839   if (id != mAudioThreadId) {
840     mAudioThreadId = id;
841     return true;
842   }
843 #endif
844   return false;
845 }
846 
DataCallback(const AudioDataValue * aInputBuffer,AudioDataValue * aOutputBuffer,long aFrames)847 long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
848                                        AudioDataValue* aOutputBuffer,
849                                        long aFrames) {
850   if (!mSandboxed && CheckThreadIdChanged()) {
851     CubebUtils::GetAudioThreadRegistry()->Register(mAudioThreadId);
852   }
853 
854   if (mAudioStreamState.compareExchange(AudioStreamState::Pending,
855                                         AudioStreamState::Running)) {
856     LOG(LogLevel::Verbose, ("%p: AudioCallbackDriver %p First audio callback "
857                             "close the Fallback driver",
858                             Graph(), this));
859   }
860 
861   FallbackDriverState fallbackState = mFallbackDriverState;
862   if (MOZ_UNLIKELY(fallbackState == FallbackDriverState::Running)) {
863     // Wait for the fallback driver to stop. Wake it up so it can stop if it's
864     // sleeping.
865     LOG(LogLevel::Verbose,
866         ("%p: AudioCallbackDriver %p Waiting for the Fallback driver to stop",
867          Graph(), this));
868     EnsureNextIteration();
869     PodZero(aOutputBuffer, aFrames * mOutputChannelCount);
870     return aFrames;
871   }
872 
873   if (MOZ_UNLIKELY(fallbackState == FallbackDriverState::Stopped)) {
874     // We're supposed to stop.
875     PodZero(aOutputBuffer, aFrames * mOutputChannelCount);
876     if (!mSandboxed) {
877       CubebUtils::GetAudioThreadRegistry()->Unregister(mAudioThreadId);
878     }
879     return aFrames - 1;
880   }
881 
882   MOZ_ASSERT(ThreadRunning());
883   TRACE_AUDIO_CALLBACK_BUDGET(aFrames, mSampleRate);
884   TRACE();
885 
886 #ifdef DEBUG
887   AutoInCallback aic(this);
888 #endif
889 
890   uint32_t durationMS = aFrames * 1000 / mSampleRate;
891 
892   // For now, simply average the duration with the previous
893   // duration so there is some damping against sudden changes.
894   if (!mIterationDurationMS) {
895     mIterationDurationMS = durationMS;
896   } else {
897     mIterationDurationMS = (mIterationDurationMS * 3) + durationMS;
898     mIterationDurationMS /= 4;
899   }
900 
901   mBuffer.SetBuffer(aOutputBuffer, aFrames);
902   // fill part or all with leftover data from last iteration (since we
903   // align to Audio blocks)
904   uint32_t alreadyBuffered = mScratchBuffer.Empty(mBuffer);
905 
906   // State computed time is decided by the audio callback's buffer length. We
907   // compute the iteration start and end from there, trying to keep the amount
908   // of buffering in the graph constant.
909   GraphTime nextStateComputedTime =
910       MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(mStateComputedTime +
911                                                     mBuffer.Available());
912 
913   mIterationStart = mIterationEnd;
914   // inGraph is the number of audio frames there is between the state time and
915   // the current time, i.e. the maximum theoretical length of the interval we
916   // could use as [mIterationStart; mIterationEnd].
917   GraphTime inGraph = mStateComputedTime - mIterationStart;
918   // We want the interval [mIterationStart; mIterationEnd] to be before the
919   // interval [mStateComputedTime; nextStateComputedTime]. We also want
920   // the distance between these intervals to be roughly equivalent each time, to
921   // ensure there is no clock drift between current time and state time. Since
922   // we can't act on the state time because we have to fill the audio buffer, we
923   // reclock the current time against the state time, here.
924   mIterationEnd = mIterationStart + 0.8 * inGraph;
925 
926   LOG(LogLevel::Verbose,
927       ("%p: interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) "
928        "(duration ticks: %ld)",
929        Graph(), (long)mIterationStart, (long)mIterationEnd,
930        (long)mStateComputedTime, (long)nextStateComputedTime, (long)aFrames,
931        (uint32_t)durationMS,
932        (long)(nextStateComputedTime - mStateComputedTime)));
933 
934   if (mStateComputedTime < mIterationEnd) {
935     LOG(LogLevel::Error, ("%p: Media graph global underrun detected", Graph()));
936     MOZ_ASSERT_UNREACHABLE("We should not underrun in full duplex");
937     mIterationEnd = mStateComputedTime;
938   }
939 
940   // Process mic data if any/needed
941   if (aInputBuffer && mInputChannelCount > 0) {
942     Graph()->NotifyInputData(aInputBuffer, static_cast<size_t>(aFrames),
943                              mSampleRate, mInputChannelCount, alreadyBuffered);
944   }
945 
946   bool iterate = mBuffer.Available();
947   IterationResult result =
948       iterate
949           ? Graph()->OneIteration(nextStateComputedTime, mIterationEnd, &mMixer)
950           : IterationResult::CreateStillProcessing();
951   if (iterate) {
952     // We totally filled the buffer (and mScratchBuffer isn't empty).
953     // We don't need to run an iteration and if we do so we may overflow.
954     mStateComputedTime = nextStateComputedTime;
955   } else {
956     LOG(LogLevel::Verbose,
957         ("%p: DataCallback buffer filled entirely from scratch "
958          "buffer, skipping iteration.",
959          Graph()));
960     result = IterationResult::CreateStillProcessing();
961   }
962 
963   MOZ_ASSERT(mBuffer.Available() == 0,
964              "The graph should have filled the buffer");
965 
966   mBuffer.BufferFilled();
967 
968 #ifdef MOZ_SAMPLE_TYPE_FLOAT32
969   // Prevent returning NaN to the OS mixer, and propagating NaN into the reverse
970   // stream of the AEC.
971   NaNToZeroInPlace(aOutputBuffer, aFrames * mOutputChannelCount);
972 #endif
973 
974   // Callback any observers for the AEC speaker data.  Note that one
975   // (maybe) of these will be full-duplex, the others will get their input
976   // data off separate cubeb callbacks.  Take care with how stuff is
977   // removed/added to this list and TSAN issues, but input and output will
978   // use separate callback methods.
979   Graph()->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
980                             mSampleRate, mOutputChannelCount);
981 
982 #ifdef XP_MACOSX
983   // This only happens when the output is on a macbookpro's external speaker,
984   // that are stereo, but let's just be safe.
985   if (mNeedsPanning && mOutputChannelCount == 2) {
986     // hard pan to the right
987     for (uint32_t i = 0; i < aFrames * 2; i += 2) {
988       aOutputBuffer[i + 1] += aOutputBuffer[i];
989       aOutputBuffer[i] = 0.0;
990     }
991   }
992 #endif
993 
994   // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable
995   if (aInputBuffer) {
996     mInputStreamFile.Write(static_cast<const AudioDataValue*>(aInputBuffer),
997                            aFrames * mInputChannelCount);
998   }
999   mOutputStreamFile.Write(static_cast<const AudioDataValue*>(aOutputBuffer),
1000                           aFrames * mOutputChannelCount);
1001 
1002   if (result.IsStop()) {
1003     if (mInputDeviceID) {
1004       mGraphInterface->NotifyInputStopped();
1005     }
1006     // Signal that we have stopped.
1007     result.Stopped();
1008     // Update the flag before handing over the graph and going to drain.
1009     mAudioStreamState = AudioStreamState::Stopping;
1010     if (!mSandboxed) {
1011       CubebUtils::GetAudioThreadRegistry()->Unregister(mAudioThreadId);
1012     }
1013     return aFrames - 1;
1014   }
1015 
1016   if (GraphDriver* nextDriver = result.NextDriver()) {
1017     LOG(LogLevel::Debug,
1018         ("%p: Switching to %s driver.", Graph(),
1019          nextDriver->AsAudioCallbackDriver() ? "audio" : "system"));
1020     if (mInputDeviceID) {
1021       mGraphInterface->NotifyInputStopped();
1022     }
1023     result.Switched();
1024     mAudioStreamState = AudioStreamState::Stopping;
1025     nextDriver->SetState(mIterationStart, mIterationEnd, mStateComputedTime);
1026     nextDriver->Start();
1027     if (!mSandboxed) {
1028       CubebUtils::GetAudioThreadRegistry()->Unregister(mAudioThreadId);
1029     }
1030     // Returning less than aFrames starts the draining and eventually stops the
1031     // audio thread. This function will never get called again.
1032     return aFrames - 1;
1033   }
1034 
1035   MOZ_ASSERT(result.IsStillProcessing());
1036   return aFrames;
1037 }
1038 
StateToString(cubeb_state aState)1039 static const char* StateToString(cubeb_state aState) {
1040   switch (aState) {
1041     case CUBEB_STATE_STARTED:
1042       return "STARTED";
1043     case CUBEB_STATE_STOPPED:
1044       return "STOPPED";
1045     case CUBEB_STATE_DRAINED:
1046       return "DRAINED";
1047     case CUBEB_STATE_ERROR:
1048       return "ERROR";
1049     default:
1050       MOZ_CRASH("Unexpected state!");
1051   }
1052 }
1053 
StateCallback(cubeb_state aState)1054 void AudioCallbackDriver::StateCallback(cubeb_state aState) {
1055   MOZ_ASSERT(!InIteration());
1056   LOG(LogLevel::Debug,
1057       ("AudioCallbackDriver(%p) State: %s", this, StateToString(aState)));
1058 
1059   AudioStreamState streamState = mAudioStreamState;
1060   if (aState != CUBEB_STATE_STARTED) {
1061     // Clear the flag for the not running states: stopped, drained, error.
1062     streamState = mAudioStreamState.exchange(AudioStreamState::None);
1063   }
1064 
1065   if (aState == CUBEB_STATE_ERROR) {
1066     // About to hand over control of the graph.  Do not start a new driver if
1067     // StateCallback() receives an error for this stream while the main thread
1068     // or another driver has control of the graph.
1069     if (streamState == AudioStreamState::Running) {
1070       MOZ_ASSERT(!ThreadRunning());
1071       mStarted = false;
1072       if (mFallbackDriverState == FallbackDriverState::None) {
1073         // Only switch to fallback if it's not already running. It could be
1074         // running with the callback driver having started but not seen a single
1075         // callback yet. I.e., handover from fallback to callback is not done.
1076         if (mInputDeviceID) {
1077 #ifdef DEBUG
1078           // No audio callback after an error. We're calling into the graph here
1079           // so we need to be regarded as "in iteration".
1080           AutoInCallback aic(this);
1081 #endif
1082           mGraphInterface->NotifyInputStopped();
1083         }
1084         FallbackToSystemClockDriver();
1085       }
1086     }
1087   } else if (aState == CUBEB_STATE_STOPPED) {
1088     MOZ_ASSERT(!ThreadRunning());
1089     mStarted = false;
1090   }
1091 }
1092 
MixerCallback(AudioDataValue * aMixedBuffer,AudioSampleFormat aFormat,uint32_t aChannels,uint32_t aFrames,uint32_t aSampleRate)1093 void AudioCallbackDriver::MixerCallback(AudioDataValue* aMixedBuffer,
1094                                         AudioSampleFormat aFormat,
1095                                         uint32_t aChannels, uint32_t aFrames,
1096                                         uint32_t aSampleRate) {
1097   MOZ_ASSERT(InIteration());
1098   uint32_t toWrite = mBuffer.Available();
1099 
1100   if (!mBuffer.Available()) {
1101     NS_WARNING("DataCallback buffer full, expect frame drops.");
1102   }
1103 
1104   MOZ_ASSERT(mBuffer.Available() <= aFrames);
1105 
1106   mBuffer.WriteFrames(aMixedBuffer, mBuffer.Available());
1107   MOZ_ASSERT(mBuffer.Available() == 0,
1108              "Missing frames to fill audio callback's buffer.");
1109 
1110   DebugOnly<uint32_t> written = mScratchBuffer.Fill(
1111       aMixedBuffer + toWrite * aChannels, aFrames - toWrite);
1112   NS_WARNING_ASSERTION(written == aFrames - toWrite, "Dropping frames.");
1113 };
1114 
PanOutputIfNeeded(bool aMicrophoneActive)1115 void AudioCallbackDriver::PanOutputIfNeeded(bool aMicrophoneActive) {
1116 #ifdef XP_MACOSX
1117   TRACE();
1118   cubeb_device* out = nullptr;
1119   int rv;
1120   char name[128];
1121   size_t length = sizeof(name);
1122 
1123   rv = sysctlbyname("hw.model", name, &length, NULL, 0);
1124   if (rv) {
1125     return;
1126   }
1127 
1128   int major, minor;
1129   for (uint32_t i = 0; i < length; i++) {
1130     // skip the model name
1131     if (isalpha(name[i])) {
1132       continue;
1133     }
1134     sscanf(name + i, "%d,%d", &major, &minor);
1135     break;
1136   }
1137 
1138   enum MacbookModel { MacBook, MacBookPro, MacBookAir, NotAMacbook };
1139 
1140   MacbookModel model;
1141 
1142   if (!strncmp(name, "MacBookPro", length)) {
1143     model = MacBookPro;
1144   } else if (strncmp(name, "MacBookAir", length)) {
1145     model = MacBookAir;
1146   } else if (strncmp(name, "MacBook", length)) {
1147     model = MacBook;
1148   } else {
1149     model = NotAMacbook;
1150   }
1151   // For macbook pro before 2016 model (change of chassis), hard pan the audio
1152   // to the right if the speakers are in use to avoid feedback.
1153   if (model == MacBookPro && major <= 12) {
1154     if (cubeb_stream_get_current_device(mAudioStream, &out) == CUBEB_OK) {
1155       MOZ_ASSERT(out);
1156       // Check if we are currently outputing sound on external speakers.
1157       if (out->output_name && !strcmp(out->output_name, "ispk")) {
1158         // Pan everything to the right speaker.
1159         LOG(LogLevel::Debug, ("Using the built-in speakers, with%s audio input",
1160                               aMicrophoneActive ? "" : "out"));
1161         mNeedsPanning = aMicrophoneActive;
1162       } else {
1163         LOG(LogLevel::Debug, ("Using an external output device"));
1164         mNeedsPanning = false;
1165       }
1166       cubeb_stream_device_destroy(mAudioStream, out);
1167     }
1168   }
1169 #endif
1170 }
1171 
DeviceChangedCallback()1172 void AudioCallbackDriver::DeviceChangedCallback() {
1173   MOZ_ASSERT(!InIteration());
1174   // Tell the audio engine the device has changed, it might want to reset some
1175   // state.
1176   Graph()->DeviceChanged();
1177 #ifdef XP_MACOSX
1178   RefPtr<AudioCallbackDriver> self(this);
1179   bool hasInput = mInputChannelCount;
1180   NS_DispatchBackgroundTask(NS_NewRunnableFunction(
1181       "PanOutputIfNeeded", [self{std::move(self)}, hasInput]() {
1182         self->PanOutputIfNeeded(hasInput);
1183       }));
1184 #endif
1185 }
1186 
IterationDuration()1187 uint32_t AudioCallbackDriver::IterationDuration() {
1188   MOZ_ASSERT(InIteration());
1189   // The real fix would be to have an API in cubeb to give us the number. Short
1190   // of that, we approximate it here. bug 1019507
1191   return mIterationDurationMS;
1192 }
1193 
EnsureNextIteration()1194 void AudioCallbackDriver::EnsureNextIteration() {
1195   if (mFallbackDriverState == FallbackDriverState::Running) {
1196     auto fallback = mFallback.Lock();
1197     if (fallback.ref()) {
1198       fallback.ref()->EnsureNextIteration();
1199     }
1200   }
1201 }
1202 
IsStarted()1203 bool AudioCallbackDriver::IsStarted() { return mStarted; }
1204 
AudioOutputLatency()1205 TimeDuration AudioCallbackDriver::AudioOutputLatency() {
1206   TRACE();
1207   uint32_t latencyFrames;
1208   int rv = cubeb_stream_get_latency(mAudioStream, &latencyFrames);
1209   if (rv || mSampleRate == 0) {
1210     return TimeDuration::FromSeconds(0.0);
1211   }
1212 
1213   return TimeDuration::FromSeconds(static_cast<double>(latencyFrames) /
1214                                    mSampleRate);
1215 }
1216 
OnFallback() const1217 bool AudioCallbackDriver::OnFallback() const {
1218   MOZ_ASSERT(InIteration());
1219   return mFallbackDriverState == FallbackDriverState::Running;
1220 }
1221 
FallbackToSystemClockDriver()1222 void AudioCallbackDriver::FallbackToSystemClockDriver() {
1223   MOZ_ASSERT(!ThreadRunning());
1224   MOZ_ASSERT(mAudioStreamState == AudioStreamState::None ||
1225              mAudioStreamState == AudioStreamState::Pending);
1226   MOZ_ASSERT(mFallbackDriverState == FallbackDriverState::None);
1227   LOG(LogLevel::Debug,
1228       ("%p: AudioCallbackDriver %p Falling back to SystemClockDriver.", Graph(),
1229        this));
1230   mFallbackDriverState = FallbackDriverState::Running;
1231   mNextReInitBackoffStep =
1232       TimeDuration::FromMilliseconds(AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS);
1233   mNextReInitAttempt = TimeStamp::Now() + mNextReInitBackoffStep;
1234   auto fallback =
1235       MakeRefPtr<FallbackWrapper>(Graph(), this, mSampleRate, mIterationStart,
1236                                   mIterationEnd, mStateComputedTime);
1237   {
1238     auto driver = mFallback.Lock();
1239     driver.ref() = fallback;
1240   }
1241   fallback->Start();
1242 }
1243 
FallbackDriverStopped(GraphTime aIterationStart,GraphTime aIterationEnd,GraphTime aStateComputedTime,FallbackDriverState aState)1244 void AudioCallbackDriver::FallbackDriverStopped(GraphTime aIterationStart,
1245                                                 GraphTime aIterationEnd,
1246                                                 GraphTime aStateComputedTime,
1247                                                 FallbackDriverState aState) {
1248   mIterationStart = aIterationStart;
1249   mIterationEnd = aIterationEnd;
1250   mStateComputedTime = aStateComputedTime;
1251   mNextReInitAttempt = TimeStamp();
1252   mNextReInitBackoffStep = TimeDuration();
1253   {
1254     auto fallback = mFallback.Lock();
1255     MOZ_ASSERT(fallback.ref()->OnThread());
1256     fallback.ref() = nullptr;
1257   }
1258 
1259   MOZ_ASSERT(aState == FallbackDriverState::None ||
1260              aState == FallbackDriverState::Stopped);
1261   MOZ_ASSERT_IF(aState == FallbackDriverState::None,
1262                 mAudioStreamState == AudioStreamState::Running);
1263   mFallbackDriverState = aState;
1264 }
1265 
MaybeStartAudioStream()1266 void AudioCallbackDriver::MaybeStartAudioStream() {
1267   AudioStreamState streamState = mAudioStreamState;
1268   if (streamState != AudioStreamState::None) {
1269     LOG(LogLevel::Verbose,
1270         ("%p: AudioCallbackDriver %p Cannot re-init.", Graph(), this));
1271     return;
1272   }
1273 
1274   TimeStamp now = TimeStamp::Now();
1275   if (now < mNextReInitAttempt) {
1276     LOG(LogLevel::Verbose,
1277         ("%p: AudioCallbackDriver %p Not time to re-init yet. %.3fs left.",
1278          Graph(), this, (mNextReInitAttempt - now).ToSeconds()));
1279     return;
1280   }
1281 
1282   LOG(LogLevel::Debug, ("%p: AudioCallbackDriver %p Attempting to re-init "
1283                         "audio stream from fallback driver.",
1284                         Graph(), this));
1285   mNextReInitBackoffStep = std::min(
1286       mNextReInitBackoffStep * 2,
1287       TimeDuration::FromMilliseconds(AUDIO_MAX_FALLBACK_BACKOFF_STEP_MS));
1288   mNextReInitAttempt = now + mNextReInitBackoffStep;
1289   Start();
1290 }
1291 
1292 }  // namespace mozilla
1293 
1294 // avoid redefined macro in unified build
1295 #undef LOG
1296