Bug 1872519 pass planar reverse stream data to AudioProcessingTrack::NotifyOutputData...
[gecko.git] / dom / media / GraphDriver.h
blob150fee0b3d8a3373a13709db67427e82714ab798
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef GRAPHDRIVER_H_
7 #define GRAPHDRIVER_H_
9 #include "nsAutoRef.h"
10 #include "nsIThread.h"
11 #include "AudioBufferUtils.h"
12 #include "AudioMixer.h"
13 #include "AudioSegment.h"
14 #include "SelfRef.h"
15 #include "mozilla/Atomics.h"
16 #include "mozilla/dom/AudioContext.h"
17 #include "mozilla/DataMutex.h"
18 #include "mozilla/SharedThreadPool.h"
19 #include "mozilla/StaticPtr.h"
20 #include "WavDumper.h"
22 #include <thread>
24 struct cubeb_stream;
26 template <>
27 class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream> {
28 public:
29 static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
32 namespace mozilla {
34 // A thread pool containing only one thread to execute the cubeb operations. We
35 // should always use this thread to init, destroy, start, or stop cubeb streams,
36 // to avoid data racing or deadlock issues across platforms.
37 #define CUBEB_TASK_THREAD SharedThreadPool::Get("CubebOperation"_ns, 1)
39 /**
40 * Assume we can run an iteration of the MediaTrackGraph loop in this much time
41 * or less.
42 * We try to run the control loop at this rate.
44 static const int MEDIA_GRAPH_TARGET_PERIOD_MS = 10;
46 /**
47 * Assume that we might miss our scheduled wakeup of the MediaTrackGraph by
48 * this much.
50 static const int SCHEDULE_SAFETY_MARGIN_MS = 10;
52 /**
53 * Try have this much audio buffered in streams and queued to the hardware.
54 * The maximum delay to the end of the next control loop
55 * is 2*MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS.
56 * There is no point in buffering more audio than this in a stream at any
57 * given time (until we add processing).
58 * This is not optimal yet.
60 static const int AUDIO_TARGET_MS =
61 2 * MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS;
63 /**
64 * After starting a fallback driver, wait this long before attempting to re-init
65 * the audio stream the first time.
67 static const int AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS = 10;
69 /**
70 * The backoff step duration for when to next attempt to re-init the audio
71 * stream is capped at this value.
73 static const int AUDIO_MAX_FALLBACK_BACKOFF_STEP_MS = 1000;
75 class AudioCallbackDriver;
76 class GraphDriver;
77 class MediaTrack;
78 class OfflineClockDriver;
79 class SystemClockDriver;
81 namespace dom {
82 enum class AudioContextOperation : uint8_t;
85 struct GraphInterface : public nsISupports {
86 /**
87 * Object returned from OneIteration() instructing the iterating GraphDriver
88 * what to do.
90 * - If the result is StillProcessing: keep the iterations coming.
91 * - If the result is Stop: the driver potentially updates its internal state
92 * and interacts with the graph (e.g., NotifyOutputData), then it must call
93 * Stopped() exactly once.
94 * - If the result is SwitchDriver: the driver updates internal state as for
95 * the Stop result, then it must call Switched() exactly once and start
96 * NextDriver().
98 class IterationResult final {
99 struct Undefined {};
100 struct StillProcessing {};
101 struct Stop {
102 explicit Stop(RefPtr<Runnable> aStoppedRunnable)
103 : mStoppedRunnable(std::move(aStoppedRunnable)) {}
104 Stop(const Stop&) = delete;
105 Stop(Stop&& aOther) noexcept
106 : mStoppedRunnable(std::move(aOther.mStoppedRunnable)) {}
107 ~Stop() { MOZ_ASSERT(!mStoppedRunnable); }
108 RefPtr<Runnable> mStoppedRunnable;
109 void Stopped() {
110 mStoppedRunnable->Run();
111 mStoppedRunnable = nullptr;
114 struct SwitchDriver {
115 SwitchDriver(RefPtr<GraphDriver> aDriver,
116 RefPtr<Runnable> aSwitchedRunnable)
117 : mDriver(std::move(aDriver)),
118 mSwitchedRunnable(std::move(aSwitchedRunnable)) {}
119 SwitchDriver(const SwitchDriver&) = delete;
120 SwitchDriver(SwitchDriver&& aOther) noexcept
121 : mDriver(std::move(aOther.mDriver)),
122 mSwitchedRunnable(std::move(aOther.mSwitchedRunnable)) {}
123 ~SwitchDriver() { MOZ_ASSERT(!mSwitchedRunnable); }
124 RefPtr<GraphDriver> mDriver;
125 RefPtr<Runnable> mSwitchedRunnable;
126 void Switched() {
127 mSwitchedRunnable->Run();
128 mSwitchedRunnable = nullptr;
131 Variant<Undefined, StillProcessing, Stop, SwitchDriver> mResult;
133 explicit IterationResult(StillProcessing&& aArg)
134 : mResult(std::move(aArg)) {}
135 explicit IterationResult(Stop&& aArg) : mResult(std::move(aArg)) {}
136 explicit IterationResult(SwitchDriver&& aArg) : mResult(std::move(aArg)) {}
138 public:
139 IterationResult() : mResult(Undefined()) {}
140 IterationResult(const IterationResult&) = delete;
141 IterationResult(IterationResult&&) = default;
143 IterationResult& operator=(const IterationResult&) = delete;
144 IterationResult& operator=(IterationResult&&) = default;
146 static IterationResult CreateStillProcessing() {
147 return IterationResult(StillProcessing());
149 static IterationResult CreateStop(RefPtr<Runnable> aStoppedRunnable) {
150 return IterationResult(Stop(std::move(aStoppedRunnable)));
152 static IterationResult CreateSwitchDriver(
153 RefPtr<GraphDriver> aDriver, RefPtr<Runnable> aSwitchedRunnable) {
154 return IterationResult(
155 SwitchDriver(std::move(aDriver), std::move(aSwitchedRunnable)));
158 bool IsStillProcessing() const { return mResult.is<StillProcessing>(); }
159 bool IsStop() const { return mResult.is<Stop>(); }
160 bool IsSwitchDriver() const { return mResult.is<SwitchDriver>(); }
162 void Stopped() {
163 MOZ_ASSERT(IsStop());
164 mResult.as<Stop>().Stopped();
167 GraphDriver* NextDriver() const {
168 if (!IsSwitchDriver()) {
169 return nullptr;
171 return mResult.as<SwitchDriver>().mDriver;
174 void Switched() {
175 MOZ_ASSERT(IsSwitchDriver());
176 mResult.as<SwitchDriver>().Switched();
180 /* Called on the graph thread after an AudioCallbackDriver with an input
181 * stream has stopped. */
182 virtual void NotifyInputStopped() = 0;
183 /* Called on the graph thread when there is new input data for listeners. This
184 * is the raw audio input for this MediaTrackGraph. */
185 virtual void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
186 TrackRate aRate, uint32_t aChannels,
187 uint32_t aAlreadyBuffered) = 0;
188 /* Called every time there are changes to input/output audio devices like
189 * plug/unplug etc. This can be called on any thread, and posts a message to
190 * the main thread so that it can post a message to the graph thread. */
191 virtual void DeviceChanged() = 0;
192 /* Called by GraphDriver to iterate the graph. Mixed audio output from the
193 * graph is passed into aMixerReceiver, if it is non-null. */
194 virtual IterationResult OneIteration(
195 GraphTime aStateComputedEnd, GraphTime aIterationEnd,
196 MixerCallbackReceiver* aMixerReceiver) = 0;
197 #ifdef DEBUG
198 /* True if we're on aDriver's thread, or if we're on mGraphRunner's thread
199 * and mGraphRunner is currently run by aDriver. */
200 virtual bool InDriverIteration(const GraphDriver* aDriver) const = 0;
201 #endif
205 * A driver is responsible for the scheduling of the processing, the thread
206 * management, and give the different clocks to a MediaTrackGraph. This is an
207 * abstract base class. A MediaTrackGraph can be driven by an
208 * OfflineClockDriver, if the graph is offline, or a SystemClockDriver or an
209 * AudioCallbackDriver, if the graph is real time.
210 * A MediaTrackGraph holds an owning reference to its driver.
212 * The lifetime of drivers is a complicated affair. Here are the different
213 * scenarii that can happen:
215 * Starting a MediaTrackGraph with an AudioCallbackDriver
216 * - A new thread T is created, from the main thread.
217 * - On this thread T, cubeb is initialized if needed, and a cubeb_stream is
218 * created and started
219 * - The thread T posts a message to the main thread to terminate itself.
220 * - The graph runs off the audio thread
222 * Starting a MediaTrackGraph with a SystemClockDriver:
223 * - A new thread T is created from the main thread.
224 * - The graph runs off this thread.
226 * Switching from a SystemClockDriver to an AudioCallbackDriver:
227 * - At the end of the MTG iteration, the graph tells the current driver to
228 * switch to an AudioCallbackDriver, which is created and initialized on the
229 * graph thread.
230 * - At the end of the MTG iteration, the SystemClockDriver transfers its timing
231 * info and a reference to itself to the AudioCallbackDriver. It then starts
232 * the AudioCallbackDriver.
233 * - When the AudioCallbackDriver starts, it:
234 * - Starts a fallback SystemClockDriver that runs until the
235 * AudioCallbackDriver is running, in case it takes a long time to start (it
236 * could block on I/O, e.g., negotiating a bluetooth connection).
237 * - Checks if it has been switched from a SystemClockDriver, and if that is
238 * the case, sends a message to the main thread to shut the
239 * SystemClockDriver thread down.
240 * - When the AudioCallbackDriver is running, data callbacks are blocked. The
241 * fallback driver detects this in its callback and stops itself. The first
242 * DataCallback after the fallback driver had stopped goes through.
243 * - The graph now runs off an audio callback.
245 * Switching from an AudioCallbackDriver to a SystemClockDriver:
246 * - At the end of the MTG iteration, the graph tells the current driver to
247 * switch to a SystemClockDriver.
248 * - the AudioCallbackDriver transfers its timing info and a reference to itself
249 * to the SystemClockDriver. A new SystemClockDriver is started from the
250 * current audio thread.
251 * - When starting, the SystemClockDriver checks if it has been switched from an
252 * AudioCallbackDriver. If yes, it creates a new temporary thread to release
253 * the cubeb_streams. This temporary thread closes the cubeb_stream, and then
254 * dispatches a message to the main thread to be terminated.
255 * - The graph now runs off a normal thread.
257 * Two drivers cannot run at the same time for the same graph. The thread safety
258 * of the different members of drivers, and their access pattern is documented
259 * next to the members themselves.
261 class GraphDriver {
262 public:
263 using IterationResult = GraphInterface::IterationResult;
265 GraphDriver(GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
266 uint32_t aSampleRate);
268 NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
270 /* Start the graph, init the driver, start the thread.
271 * A driver cannot be started twice, it must be shutdown
272 * before being started again. */
273 virtual void Start() = 0;
274 /* Shutdown GraphDriver */
275 MOZ_CAN_RUN_SCRIPT virtual void Shutdown() = 0;
276 /* Set the UTF-8 name for system audio streams.
277 * Graph thread, or main thread if the graph is not running. */
278 virtual void SetStreamName(const nsACString& aStreamName);
279 /* Rate at which the GraphDriver runs, in ms. This can either be user
280 * controlled (because we are using a {System,Offline}ClockDriver, and decide
281 * how often we want to wakeup/how much we want to process per iteration), or
282 * it can be indirectly set by the latency of the audio backend, and the
283 * number of buffers of this audio backend: say we have four buffers, and 40ms
284 * latency, we will get a callback approximately every 10ms. */
285 virtual uint32_t IterationDuration() = 0;
287 * Signaled by the graph when it needs another iteration. Goes unhandled for
288 * GraphDrivers that are not able to sleep indefinitely (i.e., all drivers but
289 * ThreadedDriver). Can be called on any thread.
291 virtual void EnsureNextIteration() = 0;
293 // Those are simply for accessing the associated pointer. Graph thread only,
294 // or if one is not running, main thread.
295 GraphDriver* PreviousDriver();
296 void SetPreviousDriver(GraphDriver* aPreviousDriver);
298 virtual AudioCallbackDriver* AsAudioCallbackDriver() { return nullptr; }
299 virtual const AudioCallbackDriver* AsAudioCallbackDriver() const {
300 return nullptr;
303 virtual OfflineClockDriver* AsOfflineClockDriver() { return nullptr; }
304 virtual const OfflineClockDriver* AsOfflineClockDriver() const {
305 return nullptr;
308 virtual SystemClockDriver* AsSystemClockDriver() { return nullptr; }
309 virtual const SystemClockDriver* AsSystemClockDriver() const {
310 return nullptr;
314 * Set the state of the driver so it can start at the right point in time,
315 * after switching from another driver.
317 void SetState(const nsACString& aStreamName, GraphTime aIterationEnd,
318 GraphTime aStateComputedTime);
320 GraphInterface* Graph() const { return mGraphInterface; }
322 #ifdef DEBUG
323 // True if the current thread is currently iterating the MTG.
324 bool InIteration() const;
325 #endif
326 // True if the current thread is the GraphDriver's thread.
327 virtual bool OnThread() const = 0;
328 // GraphDriver's thread has started and the thread is running.
329 virtual bool ThreadRunning() const = 0;
331 double MediaTimeToSeconds(GraphTime aTime) const {
332 NS_ASSERTION(aTime > -TRACK_TIME_MAX && aTime <= TRACK_TIME_MAX,
333 "Bad time");
334 return static_cast<double>(aTime) / mSampleRate;
337 GraphTime SecondsToMediaTime(double aS) const {
338 NS_ASSERTION(0 <= aS && aS <= TRACK_TICKS_MAX / TRACK_RATE_MAX,
339 "Bad seconds");
340 return mSampleRate * aS;
343 GraphTime MillisecondsToMediaTime(int32_t aMS) const {
344 return RateConvertTicksRoundDown(mSampleRate, 1000, aMS);
347 protected:
348 // The UTF-8 name for system audio streams. Graph thread.
349 nsCString mStreamName;
350 // Time of the end of this graph iteration.
351 GraphTime mIterationEnd = 0;
352 // Time until which the graph has processed data.
353 GraphTime mStateComputedTime = 0;
354 // The GraphInterface this driver is currently iterating.
355 const RefPtr<GraphInterface> mGraphInterface;
356 // The sample rate for the graph, and in case of an audio driver, also for the
357 // cubeb stream.
358 const uint32_t mSampleRate;
360 // This is non-null only when this driver has recently switched from an other
361 // driver, and has not cleaned it up yet (for example because the audio stream
362 // is currently calling the callback during initialization).
364 // This is written to when changing driver, from the previous driver's thread,
365 // or a thread created for the occasion. This is read each time we need to
366 // check whether we're changing driver (in Switching()), from the graph
367 // thread.
368 // This must be accessed using the {Set,Get}PreviousDriver methods.
369 RefPtr<GraphDriver> mPreviousDriver;
371 virtual ~GraphDriver() = default;
374 class MediaTrackGraphInitThreadRunnable;
377 * This class is a driver that manages its own thread.
379 class ThreadedDriver : public GraphDriver {
380 class IterationWaitHelper {
381 Monitor mMonitor MOZ_UNANNOTATED;
382 // The below members are guarded by mMonitor.
383 bool mNeedAnotherIteration = false;
384 TimeStamp mWakeTime;
386 public:
387 IterationWaitHelper() : mMonitor("IterationWaitHelper::mMonitor") {}
390 * If another iteration is needed we wait for aDuration, otherwise we wait
391 * for a wake-up. If a wake-up occurs before aDuration time has passed, we
392 * wait for aDuration nonetheless.
394 void WaitForNextIterationAtLeast(TimeDuration aDuration) {
395 MonitorAutoLock lock(mMonitor);
396 TimeStamp now = TimeStamp::Now();
397 mWakeTime = now + aDuration;
398 while (true) {
399 if (mNeedAnotherIteration && now >= mWakeTime) {
400 break;
402 if (mNeedAnotherIteration) {
403 lock.Wait(mWakeTime - now);
404 } else {
405 lock.Wait(TimeDuration::Forever());
407 now = TimeStamp::Now();
409 mWakeTime = TimeStamp();
410 mNeedAnotherIteration = false;
414 * Sets mNeedAnotherIteration to true and notifies the monitor, in case a
415 * driver is currently waiting.
417 void EnsureNextIteration() {
418 MonitorAutoLock lock(mMonitor);
419 mNeedAnotherIteration = true;
420 lock.Notify();
424 public:
425 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadedDriver, override);
427 ThreadedDriver(GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
428 uint32_t aSampleRate);
430 void EnsureNextIteration() override;
431 void Start() override;
432 MOZ_CAN_RUN_SCRIPT void Shutdown() override;
434 * Runs main control loop on the graph thread. Normally a single invocation
435 * of this runs for the entire lifetime of the graph thread.
437 virtual void RunThread();
438 friend class MediaTrackGraphInitThreadRunnable;
439 uint32_t IterationDuration() override { return MEDIA_GRAPH_TARGET_PERIOD_MS; }
441 nsIThread* Thread() const { return mThread; }
443 bool OnThread() const override {
444 return !mThread || mThread->IsOnCurrentThread();
447 bool ThreadRunning() const override { return mThreadRunning; }
449 protected:
450 /* Waits until it's time to process more data. */
451 void WaitForNextIteration();
452 /* Implementation dependent time the ThreadedDriver should wait between
453 * iterations. */
454 virtual TimeDuration WaitInterval() = 0;
455 /* When the graph wakes up to do an iteration, implementations return the
456 * range of time that will be processed. This is called only once per
457 * iteration; it may determine the interval from state in a previous
458 * call. */
459 virtual MediaTime GetIntervalForIteration() = 0;
461 virtual ~ThreadedDriver();
463 nsCOMPtr<nsIThread> mThread;
465 private:
466 // This is true if the thread is running. It is false
467 // before starting the thread and after stopping it.
468 Atomic<bool> mThreadRunning;
470 // Any thread.
471 IterationWaitHelper mWaitHelper;
475 * A SystemClockDriver drives a GraphInterface using a system clock, and waits
476 * using a monitor, between each iteration.
478 class SystemClockDriver : public ThreadedDriver {
479 public:
480 SystemClockDriver(GraphInterface* aGraphInterface,
481 GraphDriver* aPreviousDriver, uint32_t aSampleRate);
482 virtual ~SystemClockDriver();
483 SystemClockDriver* AsSystemClockDriver() override { return this; }
484 const SystemClockDriver* AsSystemClockDriver() const override { return this; }
486 protected:
487 /* Return the TimeDuration to wait before the next rendering iteration. */
488 TimeDuration WaitInterval() override;
489 MediaTime GetIntervalForIteration() override;
491 private:
492 // Those are only modified (after initialization) on the graph thread. The
493 // graph thread does not run during the initialization.
494 TimeStamp mInitialTimeStamp;
495 TimeStamp mCurrentTimeStamp;
496 TimeStamp mLastTimeStamp;
500 * An OfflineClockDriver runs the graph as fast as possible, without waiting
501 * between iteration.
503 class OfflineClockDriver : public ThreadedDriver {
504 public:
505 OfflineClockDriver(GraphInterface* aGraphInterface, uint32_t aSampleRate,
506 GraphTime aSlice);
507 virtual ~OfflineClockDriver();
508 OfflineClockDriver* AsOfflineClockDriver() override { return this; }
509 const OfflineClockDriver* AsOfflineClockDriver() const override {
510 return this;
513 void RunThread() override;
515 protected:
516 TimeDuration WaitInterval() override { return TimeDuration(); }
517 MediaTime GetIntervalForIteration() override;
519 private:
520 // Time, in GraphTime, for each iteration
521 GraphTime mSlice;
524 enum class AudioInputType { Unknown, Voice };
527 * This is a graph driver that is based on callback functions called by the
528 * audio api. This ensures minimal audio latency, because it means there is no
529 * buffering happening: the audio is generated inside the callback.
531 * This design is less flexible than running our own thread:
532 * - We have no control over the thread:
533 * - It cannot block, and it has to run for a shorter amount of time than the
534 * buffer it is going to fill, or an under-run is going to occur (short burst
535 * of silence in the final audio output).
536 * - We can't know for sure when the callback function is going to be called
537 * (although we compute an estimation so we can schedule video frames)
538 * - Creating and shutting the thread down is a blocking operation, that can
539 * take _seconds_ in some cases (because IPC has to be set up, and
540 * sometimes hardware components are involved and need to be warmed up)
541 * - We have no control on how much audio we generate, we have to return exactly
542 * the number of frames asked for by the callback. Since for the Web Audio
543 * API, we have to do block processing at 128 frames per block, we need to
544 * keep a little spill buffer to store the extra frames.
546 class AudioCallbackDriver : public GraphDriver, public MixerCallbackReceiver {
547 using IterationResult = GraphInterface::IterationResult;
548 enum class FallbackDriverState;
549 class FallbackWrapper;
551 public:
552 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioCallbackDriver, override);
554 /** If aInputChannelCount is zero, then this driver is output-only. */
555 AudioCallbackDriver(GraphInterface* aGraphInterface,
556 GraphDriver* aPreviousDriver, uint32_t aSampleRate,
557 uint32_t aOutputChannelCount, uint32_t aInputChannelCount,
558 CubebUtils::AudioDeviceID aOutputDeviceID,
559 CubebUtils::AudioDeviceID aInputDeviceID,
560 AudioInputType aAudioInputType);
562 void Start() override;
563 MOZ_CAN_RUN_SCRIPT void Shutdown() override;
564 void SetStreamName(const nsACString& aStreamName) override;
566 /* Static wrapper function cubeb calls back. */
567 static long DataCallback_s(cubeb_stream* aStream, void* aUser,
568 const void* aInputBuffer, void* aOutputBuffer,
569 long aFrames);
570 static void StateCallback_s(cubeb_stream* aStream, void* aUser,
571 cubeb_state aState);
572 static void DeviceChangedCallback_s(void* aUser);
574 /* This function is called by the underlying audio backend when a refill is
575 * needed. This is what drives the whole graph when it is used to output
576 * audio. If the return value is exactly aFrames, this function will get
577 * called again. If it is less than aFrames, the stream will go in draining
578 * mode, and this function will not be called again. */
579 long DataCallback(const AudioDataValue* aInputBuffer,
580 AudioDataValue* aOutputBuffer, long aFrames);
581 /* This function is called by the underlying audio backend, but is only used
582 * for informational purposes at the moment. */
583 void StateCallback(cubeb_state aState);
584 /* This is an approximation of the number of millisecond there are between two
585 * iterations of the graph. */
586 uint32_t IterationDuration() override;
587 /* If the audio stream has started, this does nothing. There will be another
588 * iteration. If there is an active fallback driver, we forward the call so it
589 * can wake up. */
590 void EnsureNextIteration() override;
592 /* This function gets called when the graph has produced the audio frames for
593 * this iteration. */
594 void MixerCallback(AudioChunk* aMixedBuffer, uint32_t aSampleRate) override;
596 AudioCallbackDriver* AsAudioCallbackDriver() override { return this; }
597 const AudioCallbackDriver* AsAudioCallbackDriver() const override {
598 return this;
601 uint32_t OutputChannelCount() { return mOutputChannelCount; }
603 uint32_t InputChannelCount() { return mInputChannelCount; }
605 AudioInputType InputDevicePreference() {
606 if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
607 return AudioInputType::Voice;
609 return AudioInputType::Unknown;
612 std::thread::id ThreadId() const { return mAudioThreadIdInCb.load(); }
614 /* Called when the thread servicing the callback has changed. This can be
615 * fairly expensive */
616 void OnThreadIdChanged();
617 /* Called at the beginning of the audio callback to check if the thread id has
618 * changed. */
619 bool CheckThreadIdChanged();
621 bool OnThread() const override {
622 return mAudioThreadIdInCb.load() == std::this_thread::get_id();
625 /* Returns true if this driver has started (perhaps with a fallback driver)
626 * and not yet stopped. */
627 bool ThreadRunning() const override {
628 return mAudioStreamState == AudioStreamState::Running ||
629 mFallbackDriverState == FallbackDriverState::Running;
632 /* Whether the underlying cubeb stream has been started and has not stopped
633 * or errored. */
634 bool IsStarted() { return mAudioStreamState > AudioStreamState::Starting; };
636 // Returns the output latency for the current audio output stream.
637 TimeDuration AudioOutputLatency();
639 /* Returns true if this driver is currently driven by the fallback driver. */
640 bool OnFallback() const;
642 private:
644 * On certain MacBookPro, the microphone is located near the left speaker.
645 * We need to pan the sound output to the right speaker if we are using the
646 * mic and the built-in speaker, or we will have terrible echo. */
647 void PanOutputIfNeeded(bool aMicrophoneActive);
649 * This is called when the output device used by the cubeb stream changes. */
650 void DeviceChangedCallback();
651 /* Start the cubeb stream */
652 bool StartStream();
653 friend class MediaTrackGraphInitThreadRunnable;
654 void Init(const nsCString& aStreamName);
655 void SetCubebStreamName(const nsCString& aStreamName);
656 void Stop();
658 * Fall back to a SystemClockDriver using a normal thread. If needed,
659 * the graph will try to re-open an audio stream later. */
660 void FallbackToSystemClockDriver();
661 /* Called by the fallback driver when it has fully stopped, after finishing
662 * its last iteration. If it stopped after the audio stream started, aState
663 * will be None. If it stopped after the graph told it to stop, or switch,
664 * aState will be Stopped. Hands over state to the audio driver that may
665 * iterate the graph after this has been called. */
666 void FallbackDriverStopped(GraphTime aIterationEnd,
667 GraphTime aStateComputedTime,
668 FallbackDriverState aState);
670 /* Called at the end of the fallback driver's iteration to see whether we
671 * should attempt to start the AudioStream again. */
672 void MaybeStartAudioStream();
674 /* This is true when the method is executed on CubebOperation thread pool. */
675 bool OnCubebOperationThread() {
676 return mCubebOperationThread->IsOnCurrentThreadInfallible();
679 /* MediaTrackGraphs are always down/up mixed to output channels. */
680 const uint32_t mOutputChannelCount;
681 /* The size of this buffer comes from the fact that some audio backends can
682 * call back with a number of frames lower than one block (128 frames), so we
683 * need to keep at most two block in the SpillBuffer, because we always round
684 * up to block boundaries during an iteration.
685 * This is only ever accessed on the audio callback thread. */
686 SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2> mScratchBuffer;
687 /* Wrapper to ensure we write exactly the number of frames we need in the
688 * audio buffer cubeb passes us. This is only ever accessed on the audio
689 * callback thread. */
690 AudioCallbackBufferWrapper<AudioDataValue> mBuffer;
691 /* cubeb stream for this graph. This is non-null after a successful
692 * cubeb_stream_init(). CubebOperation thread only. */
693 nsAutoRef<cubeb_stream> mAudioStream;
694 /* The number of input channels from cubeb. Set before opening cubeb. If it is
695 * zero then the driver is output-only. */
696 const uint32_t mInputChannelCount;
698 * Devices to use for cubeb input & output, or nullptr for default device.
700 const CubebUtils::AudioDeviceID mOutputDeviceID;
701 const CubebUtils::AudioDeviceID mInputDeviceID;
702 /* Approximation of the time between two callbacks. This is used to schedule
703 * video frames. This is in milliseconds. Only even used (after
704 * inizatialization) on the audio callback thread. */
705 uint32_t mIterationDurationMS;
707 struct AutoInCallback {
708 explicit AutoInCallback(AudioCallbackDriver* aDriver);
709 ~AutoInCallback();
710 AudioCallbackDriver* mDriver;
713 /* Shared thread pool with up to one thread for off-main-thread
714 * initialization and shutdown of the audio stream and for other tasks that
715 * must run serially for access to mAudioStream. */
716 const RefPtr<SharedThreadPool> mCubebOperationThread;
717 cubeb_device_pref mInputDevicePreference;
718 /* Contains the id of the audio thread, from profiler_current_thread_id. */
719 std::atomic<ProfilerThreadId> mAudioThreadId;
720 /* This allows implementing AutoInCallback. This is equal to the current
721 * thread id when in an audio callback, and is an invalid thread id otherwise.
723 std::atomic<std::thread::id> mAudioThreadIdInCb;
724 /* State of the audio stream, see inline comments. */
725 enum class AudioStreamState {
726 /* There is no cubeb_stream or mAudioStream is in CUBEB_STATE_ERROR or
727 * CUBEB_STATE_STOPPED and no pending task exists to Init() a new
728 * cubeb_stream. */
729 None,
730 /* A task to Init() a new cubeb_stream is pending. */
731 Pending,
732 /* cubeb_start_stream() is about to be or has been called on mAudioStream.
733 * Any previous cubeb_streams have been destroyed. */
734 Starting,
735 /* mAudioStream is running. */
736 Running,
737 /* mAudioStream is draining, and will soon stop. */
738 Stopping
740 Atomic<AudioStreamState> mAudioStreamState{AudioStreamState::None};
741 /* State of the fallback driver, see inline comments. */
742 enum class FallbackDriverState {
743 /* There is no fallback driver. */
744 None,
745 /* There is a fallback driver trying to iterate us. */
746 Running,
747 /* There was a fallback driver and the graph stopped it. No audio callback
748 may iterate the graph. */
749 Stopped,
751 Atomic<FallbackDriverState> mFallbackDriverState{FallbackDriverState::None};
752 /* SystemClockDriver used as fallback if this AudioCallbackDriver fails to
753 * init or start. */
754 DataMutex<RefPtr<FallbackWrapper>> mFallback;
755 /* If using a fallback driver, this is the duration to wait after failing to
756 * start it before attempting to start it again. */
757 TimeDuration mNextReInitBackoffStep;
758 /* If using a fallback driver, this is the next time we'll try to start the
759 * audio stream. */
760 TimeStamp mNextReInitAttempt;
761 #ifdef XP_MACOSX
762 /* When using the built-in speakers on macbook pro (13 and 15, all models),
763 * it's best to hard pan the audio on the right, to avoid feedback into the
764 * microphone that is located next to the left speaker. */
765 Atomic<bool> mNeedsPanning;
766 #endif
768 WavDumper mInputStreamFile;
769 WavDumper mOutputStreamFile;
771 virtual ~AudioCallbackDriver();
772 const bool mSandboxed = false;
775 } // namespace mozilla
777 #endif // GRAPHDRIVER_H_