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