DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (c68fe15a81fc)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef GRAPHDRIVER_H_
#define GRAPHDRIVER_H_


#include "nsAutoRef.h"
#include "AudioBufferUtils.h"
#include "AudioMixer.h"
#include "AudioSegment.h"
#include "SelfRef.h"
#include "SelfRef.h"
#include "mozilla/Atomics.h"
#include "mozilla/dom/AudioContext.h"
#include "mozilla/DataMutex.h"
#include "mozilla/SharedThreadPool.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/StaticPtr.h"
#include "WavDumper.h"

#include <thread>
#include <thread>

#if defined(XP_WIN)
#  include "mozilla/audio/AudioNotificationReceiver.h"
#endif

struct cubeb_stream;


template <>
class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream> {
 public:
  static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
};
};

namespace mozilla {

/**
 * Assume we can run an iteration of the MediaTrackGraph loop in this much time
 * Assume we can run an iteration of the MediaTrackGraph loop in this much time
 * or less.
 * We try to run the control loop at this rate.
 */
static const int MEDIA_GRAPH_TARGET_PERIOD_MS = 10;


/**
 * Assume that we might miss our scheduled wakeup of the MediaTrackGraph by
 * this much.
 */
static const int SCHEDULE_SAFETY_MARGIN_MS = 10;
static const int SCHEDULE_SAFETY_MARGIN_MS = 10;

/**
 * Try have this much audio buffered in streams and queued to the hardware.
 * The maximum delay to the end of the next control loop
 * is 2*MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS.
 * is 2*MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS.
 * There is no point in buffering more audio than this in a stream at any
 * given time (until we add processing).
 * This is not optimal yet.
 */
static const int AUDIO_TARGET_MS =
static const int AUDIO_TARGET_MS =
    2 * MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS;

/**
 * After starting a fallback driver, wait this long before attempting to re-init
 * the audio stream the first time.
 * the audio stream the first time.
 */
static const int AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS = 10;

/**
 * The backoff step duration for when to next attempt to re-init the audio
 * The backoff step duration for when to next attempt to re-init the audio
 * stream is capped at this value.
 */
static const int AUDIO_MAX_FALLBACK_BACKOFF_STEP_MS = 1000;

class AudioCallbackDriver;
class GraphDriver;
class GraphDriver;
class MediaTrack;
class OfflineClockDriver;
class SystemClockDriver;

namespace dom {
namespace dom {
enum class AudioContextOperation;
}

struct GraphInterface : public nsISupports {
  /**
  /**
   * Object returned from OneIteration() instructing the iterating GraphDriver
   * what to do.
   *
   * - If the result is StillProcessing: keep the iterations coming.
   * - If the result is Stop: the driver potentially updates its internal state
   * - If the result is Stop: the driver potentially updates its internal state
   *   and interacts with the graph (e.g., NotifyOutputData), then it must call
   *   Stopped() exactly once.
   * - If the result is SwitchDriver: the driver updates internal state as for
   *   the Stop result, then it must call Switched() exactly once and start
   *   NextDriver().
   *   NextDriver().
   */
  class IterationResult final {
    struct Undefined {};
    struct StillProcessing {};
    struct StillProcessing {};
    struct Stop {
      explicit Stop(RefPtr<Runnable> aStoppedRunnable)
          : mStoppedRunnable(std::move(aStoppedRunnable)) {}
      Stop(const Stop&) = delete;
      Stop(const Stop&) = delete;
      Stop(Stop&& aOther) noexcept
          : mStoppedRunnable(std::move(aOther.mStoppedRunnable)) {}
      ~Stop() { MOZ_ASSERT(!mStoppedRunnable); }
      RefPtr<Runnable> mStoppedRunnable;
      void Stopped() {
      void Stopped() {
        mStoppedRunnable->Run();
        mStoppedRunnable = nullptr;
      }
    };
    struct SwitchDriver {
    struct SwitchDriver {
      SwitchDriver(RefPtr<GraphDriver> aDriver,
                   RefPtr<Runnable> aSwitchedRunnable)
          : mDriver(std::move(aDriver)),
            mSwitchedRunnable(std::move(aSwitchedRunnable)) {}
      SwitchDriver(const SwitchDriver&) = delete;
      SwitchDriver(const SwitchDriver&) = delete;
      SwitchDriver(SwitchDriver&& aOther) noexcept
          : mDriver(std::move(aOther.mDriver)),
            mSwitchedRunnable(std::move(aOther.mSwitchedRunnable)) {}
      ~SwitchDriver() { MOZ_ASSERT(!mSwitchedRunnable); }
      RefPtr<GraphDriver> mDriver;
      RefPtr<GraphDriver> mDriver;
      RefPtr<Runnable> mSwitchedRunnable;
      void Switched() {
        mSwitchedRunnable->Run();
        mSwitchedRunnable = nullptr;
      }
    };
    };
    Variant<Undefined, StillProcessing, Stop, SwitchDriver> mResult;

    explicit IterationResult(StillProcessing&& aArg)
        : mResult(std::move(aArg)) {}
        : mResult(std::move(aArg)) {}
    explicit IterationResult(Stop&& aArg) : mResult(std::move(aArg)) {}
    explicit IterationResult(SwitchDriver&& aArg) : mResult(std::move(aArg)) {}

   public:
    IterationResult() : mResult(Undefined()) {}
    IterationResult() : mResult(Undefined()) {}
    IterationResult(const IterationResult&) = delete;
    IterationResult(IterationResult&&) = default;

    IterationResult& operator=(const IterationResult&) = delete;
    IterationResult& operator=(IterationResult&&) = default;
    IterationResult& operator=(IterationResult&&) = default;

    static IterationResult CreateStillProcessing() {
      return IterationResult(StillProcessing());
    }
    static IterationResult CreateStop(RefPtr<Runnable> aStoppedRunnable) {
    static IterationResult CreateStop(RefPtr<Runnable> aStoppedRunnable) {
      return IterationResult(Stop(std::move(aStoppedRunnable)));
    }
    static IterationResult CreateSwitchDriver(
        RefPtr<GraphDriver> aDriver, RefPtr<Runnable> aSwitchedRunnable) {
      return IterationResult(
          SwitchDriver(std::move(aDriver), std::move(aSwitchedRunnable)));
    }
    }

    bool IsStillProcessing() const { return mResult.is<StillProcessing>(); }
    bool IsStop() const { return mResult.is<Stop>(); }
    bool IsSwitchDriver() const { return mResult.is<SwitchDriver>(); }


    void Stopped() {
      MOZ_ASSERT(IsStop());
      mResult.as<Stop>().Stopped();
    }


    GraphDriver* NextDriver() const {
      if (!IsSwitchDriver()) {
        return nullptr;
      }
      return mResult.as<SwitchDriver>().mDriver;
      return mResult.as<SwitchDriver>().mDriver;
    }

    void Switched() {
      MOZ_ASSERT(IsSwitchDriver());
      mResult.as<SwitchDriver>().Switched();
    }
  };

  /* Called on the graph thread when there is new output data for listeners.
  /* Called on the graph thread when there is new output data for listeners.
   * This is the mixed audio output of this MediaTrackGraph. */
  virtual void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
                                TrackRate aRate, uint32_t aChannels) = 0;
  /* Called on the graph thread before the first Notify*Data after an
  /* Called on the graph thread before the first Notify*Data after an
   * AudioCallbackDriver starts. */
  virtual void NotifyStarted() = 0;
  /* Called on the graph thread when there is new input data for listeners. This
   * is the raw audio input for this MediaTrackGraph. */
   * is the raw audio input for this MediaTrackGraph. */
  virtual void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
                               TrackRate aRate, uint32_t aChannels) = 0;
  /* Called every time there are changes to input/output audio devices like
   * plug/unplug etc. This can be called on any thread, and posts a message to
   * the main thread so that it can post a message to the graph thread. */
   * the main thread so that it can post a message to the graph thread. */
  virtual void DeviceChanged() = 0;
  /* Called by GraphDriver to iterate the graph. Output from the graph gets
   * mixed into aMixer, if it is non-null. */
  virtual IterationResult OneIteration(GraphTime aStateComputedEnd,
                                       GraphTime aIterationEnd,
                                       GraphTime aIterationEnd,
                                       AudioMixer* aMixer) = 0;
#ifdef DEBUG
  /* True if we're on aDriver's thread, or if we're on mGraphRunner's thread
   * and mGraphRunner is currently run by aDriver. */
  virtual bool InDriverIteration(GraphDriver* aDriver) = 0;
  virtual bool InDriverIteration(GraphDriver* aDriver) = 0;
#endif
};
};

/**
 * A driver is responsible for the scheduling of the processing, the thread
 * management, and give the different clocks to a MediaTrackGraph. This is an
 * abstract base class. A MediaTrackGraph can be driven by an
 * abstract base class. A MediaTrackGraph can be driven by an
 * OfflineClockDriver, if the graph is offline, or a SystemClockDriver or an
 * AudioCallbackDriver, if the graph is real time.
 * A MediaTrackGraph holds an owning reference to its driver.
 *
 * The lifetime of drivers is a complicated affair. Here are the different
 * The lifetime of drivers is a complicated affair. Here are the different
 * scenarii that can happen:
 *
 * Starting a MediaTrackGraph with an AudioCallbackDriver
 * - A new thread T is created, from the main thread.
 * - On this thread T, cubeb is initialized if needed, and a cubeb_stream is
 * - On this thread T, cubeb is initialized if needed, and a cubeb_stream is
 *   created and started
 * - The thread T posts a message to the main thread to terminate itself.
 * - The graph runs off the audio thread
 *
 * Starting a MediaTrackGraph with a SystemClockDriver:
 * Starting a MediaTrackGraph with a SystemClockDriver:
 * - A new thread T is created from the main thread.
 * - The graph runs off this thread.
 *
 * Switching from a SystemClockDriver to an AudioCallbackDriver:
 * - At the end of the MTG iteration, the graph tells the current driver to
 *   switch to an AudioCallbackDriver, which is created and initialized on the
 *   switch to an AudioCallbackDriver, which is created and initialized on the
 *   graph thread.
 * - At the end of the MTG iteration, the SystemClockDriver transfers its timing
 *   info and a reference to itself to the AudioCallbackDriver. It then starts
 *   the AudioCallbackDriver.
 * - When the AudioCallbackDriver starts, it:
 *   - Starts a fallback SystemClockDriver that runs until the
 *     AudioCallbackDriver is running, in case it takes a long time to start (it
 *     AudioCallbackDriver is running, in case it takes a long time to start (it
 *     could block on I/O, e.g., negotiating a bluetooth connection).
 *   - Checks if it has been switched from a SystemClockDriver, and if that is
 *     the case, sends a message to the main thread to shut the
 *     SystemClockDriver thread down.
 * - When the AudioCallbackDriver is running, data callbacks are blocked. The
 * - When the AudioCallbackDriver is running, data callbacks are blocked. The
 *   fallback driver detects this in its callback and stops itself. The first
 *   DataCallback after the fallback driver had stopped goes through.
 * - The graph now runs off an audio callback.
 *
 * Switching from an AudioCallbackDriver to a SystemClockDriver:
 * Switching from an AudioCallbackDriver to a SystemClockDriver:
 * - At the end of the MTG iteration, the graph tells the current driver to
 *   switch to a SystemClockDriver.
 * - the AudioCallbackDriver transfers its timing info and a reference to itself
 *   to the SystemClockDriver. A new SystemClockDriver is started from the
 *   current audio thread.
 * - When starting, the SystemClockDriver checks if it has been switched from an
 *   AudioCallbackDriver. If yes, it creates a new temporary thread to release
 *   AudioCallbackDriver. If yes, it creates a new temporary thread to release
 *   the cubeb_streams. This temporary thread closes the cubeb_stream, and then
 *   dispatches a message to the main thread to be terminated.
 * - The graph now runs off a normal thread.
 *
 * Two drivers cannot run at the same time for the same graph. The thread safety
 * Two drivers cannot run at the same time for the same graph. The thread safety
 * of the different members of drivers, and their access pattern is documented
 * next to the members themselves.
 */
class GraphDriver {
class GraphDriver {
 public:
  using IterationResult = GraphInterface::IterationResult;

  GraphDriver(GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
              uint32_t aSampleRate);
              uint32_t aSampleRate);

  NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING

  /* Start the graph, init the driver, start the thread.
   * A driver cannot be started twice, it must be shutdown
   * A driver cannot be started twice, it must be shutdown
   * before being started again. */
  virtual void Start() = 0;
  /* Shutdown GraphDriver (synchronously) */
  MOZ_CAN_RUN_SCRIPT virtual void Shutdown() = 0;
  /* Rate at which the GraphDriver runs, in ms. This can either be user
   * controlled (because we are using a {System,Offline}ClockDriver, and decide
   * controlled (because we are using a {System,Offline}ClockDriver, and decide
   * how often we want to wakeup/how much we want to process per iteration), or
   * it can be indirectly set by the latency of the audio backend, and the
   * number of buffers of this audio backend: say we have four buffers, and 40ms
   * latency, we will get a callback approximately every 10ms. */
  virtual uint32_t IterationDuration() = 0;
  virtual uint32_t IterationDuration() = 0;
  /*
   * Signaled by the graph when it needs another iteration. Goes unhandled for
   * GraphDrivers that are not able to sleep indefinitely (i.e., all drivers but
   * ThreadedDriver). Can be called on any thread.
   */
   */
  virtual void EnsureNextIteration() = 0;

  /* Implement the switching of the driver and the necessary updates */
  void SwitchToDriver(GraphDriver* aDriver);


  // Those are simply for accessing the associated pointer. Graph thread only,
  // or if one is not running, main thread.
  GraphDriver* PreviousDriver();
  void SetPreviousDriver(GraphDriver* aPreviousDriver);


  virtual AudioCallbackDriver* AsAudioCallbackDriver() { return nullptr; }

  virtual OfflineClockDriver* AsOfflineClockDriver() { return nullptr; }

  virtual SystemClockDriver* AsSystemClockDriver() { return nullptr; }


  /**
   * Set the state of the driver so it can start at the right point in time,
   * after switching from another driver.
   */
  void SetState(GraphTime aIterationStart, GraphTime aIterationEnd,
  void SetState(GraphTime aIterationStart, GraphTime aIterationEnd,
                GraphTime aStateComputedTime);

  GraphInterface* Graph() const { return mGraphInterface; }

#ifdef DEBUG
#ifdef DEBUG
  // True if the current thread is currently iterating the MTG.
  bool InIteration();
#endif
  // True if the current thread is the GraphDriver's thread.
  virtual bool OnThread() = 0;
  virtual bool OnThread() = 0;
  // GraphDriver's thread has started and the thread is running.
  virtual bool ThreadRunning() = 0;

  double MediaTimeToSeconds(GraphTime aTime) const {
    NS_ASSERTION(aTime > -TRACK_TIME_MAX && aTime <= TRACK_TIME_MAX,
    NS_ASSERTION(aTime > -TRACK_TIME_MAX && aTime <= TRACK_TIME_MAX,
                 "Bad time");
                 "Bad time");
    return static_cast<double>(aTime) / mSampleRate;
  }

  GraphTime SecondsToMediaTime(double aS) const {
    NS_ASSERTION(0 <= aS && aS <= TRACK_TICKS_MAX / TRACK_RATE_MAX,
    NS_ASSERTION(0 <= aS && aS <= TRACK_TICKS_MAX / TRACK_RATE_MAX,
                 "Bad seconds");
    return mSampleRate * aS;
  }

  GraphTime MillisecondsToMediaTime(int32_t aMS) const {
  GraphTime MillisecondsToMediaTime(int32_t aMS) const {
    return RateConvertTicksRoundDown(mSampleRate, 1000, aMS);
  }

 protected:
  // Time of the start of this graph iteration.
  // Time of the start of this graph iteration.
  GraphTime mIterationStart = 0;
  // Time of the end of this graph iteration.
  GraphTime mIterationEnd = 0;
  // Time until which the graph has processed data.
  // Time until which the graph has processed data.
  GraphTime mStateComputedTime = 0;
  // The GraphInterface this driver is currently iterating.
  const RefPtr<GraphInterface> mGraphInterface;
  // The sample rate for the graph, and in case of an audio driver, also for the
  // cubeb stream.
  // cubeb stream.
  const uint32_t mSampleRate;

  // This is non-null only when this driver has recently switched from an other
  // driver, and has not cleaned it up yet (for example because the audio stream
  // is currently calling the callback during initialization).
  //
  //
  // This is written to when changing driver, from the previous driver's thread,
  // or a thread created for the occasion. This is read each time we need to
  // check whether we're changing driver (in Switching()), from the graph
  // thread.
  // This must be accessed using the {Set,Get}PreviousDriver methods.
  // This must be accessed using the {Set,Get}PreviousDriver methods.
  RefPtr<GraphDriver> mPreviousDriver;

  virtual ~GraphDriver() = default;
};


class MediaTrackGraphInitThreadRunnable;

/**
 * This class is a driver that manages its own thread.
 */
 */
class ThreadedDriver : public GraphDriver {
  class IterationWaitHelper {
    Monitor mMonitor;
    // The below members are guarded by mMonitor.
    // The below members are guarded by mMonitor.
    bool mNeedAnotherIteration = false;
    TimeStamp mWakeTime;

   public:
    IterationWaitHelper() : mMonitor("IterationWaitHelper::mMonitor") {}

    /**
    /**
     * If another iteration is needed we wait for aDuration, otherwise we wait
     * for a wake-up. If a wake-up occurs before aDuration time has passed, we
     * wait for aDuration nonetheless.
     */
    void WaitForNextIterationAtLeast(TimeDuration aDuration) {
    void WaitForNextIterationAtLeast(TimeDuration aDuration) {
      MonitorAutoLock lock(mMonitor);
      TimeStamp now = TimeStamp::Now();
      mWakeTime = now + aDuration;
      while (true) {
        if (mNeedAnotherIteration && now >= mWakeTime) {
        if (mNeedAnotherIteration && now >= mWakeTime) {
          break;
        }
        if (mNeedAnotherIteration) {
          lock.Wait(mWakeTime - now);
        } else {
        } else {
          lock.Wait(TimeDuration::Forever());
        }
        now = TimeStamp::Now();
      }
      mWakeTime = TimeStamp();
      mWakeTime = TimeStamp();
      mNeedAnotherIteration = false;
    }

    /**
     * Sets mNeedAnotherIteration to true and notifies the monitor, in case a
     * driver is currently waiting.
     */
     */
    void EnsureNextIteration() {
      MonitorAutoLock lock(mMonitor);
      mNeedAnotherIteration = true;
      lock.Notify();
      lock.Notify();
    }
  };

 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadedDriver, override);
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadedDriver, override);

  ThreadedDriver(GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
                 uint32_t aSampleRate);

  void EnsureNextIteration() override;
  void EnsureNextIteration() override;
  void Start() override;
  MOZ_CAN_RUN_SCRIPT void Shutdown() override;
  /**
   * Runs main control loop on the graph thread. Normally a single invocation
   * of this runs for the entire lifetime of the graph thread.
   * of this runs for the entire lifetime of the graph thread.
   */
  virtual void RunThread();
  friend class MediaTrackGraphInitThreadRunnable;
  uint32_t IterationDuration() override { return MEDIA_GRAPH_TARGET_PERIOD_MS; }

  nsIThread* Thread() { return mThread; }


  bool OnThread() override {
    return !mThread || mThread->EventTarget()->IsOnCurrentThread();
  }


  bool ThreadRunning() override { return mThreadRunning; }

 protected:
  /* Waits until it's time to process more data. */
  void WaitForNextIteration();
  void WaitForNextIteration();
  /* Implementation dependent time the ThreadedDriver should wait between
   * iterations. */
  virtual TimeDuration WaitInterval() = 0;
  /* When the graph wakes up to do an iteration, implementations return the
   * range of time that will be processed.  This is called only once per
   * range of time that will be processed.  This is called only once per
   * iteration; it may determine the interval from state in a previous
   * call. */
  virtual MediaTime GetIntervalForIteration() = 0;

  virtual ~ThreadedDriver();
  virtual ~ThreadedDriver();

  nsCOMPtr<nsIThread> mThread;

 private:
  // This is true if the thread is running. It is false
  // This is true if the thread is running. It is false
  // before starting the thread and after stopping it.
  Atomic<bool> mThreadRunning;

  // Any thread.
  IterationWaitHelper mWaitHelper;
  IterationWaitHelper mWaitHelper;
};

/**
 * A SystemClockDriver drives a GraphInterface using a system clock, and waits
 * using a monitor, between each iteration.
 * using a monitor, between each iteration.
 */
class SystemClockDriver : public ThreadedDriver {
 public:
 public:
  SystemClockDriver(GraphInterface* aGraphInterface,
  SystemClockDriver(GraphInterface* aGraphInterface,
                    GraphDriver* aPreviousDriver, uint32_t aSampleRate);
  virtual ~SystemClockDriver();
  SystemClockDriver* AsSystemClockDriver() override { return this; }
  SystemClockDriver* AsSystemClockDriver() override { return this; }

 protected:
  /* Return the TimeDuration to wait before the next rendering iteration. */
  TimeDuration WaitInterval() override;
  MediaTime GetIntervalForIteration() override;
  MediaTime GetIntervalForIteration() override;

 private:
  // Those are only modified (after initialization) on the graph thread. The
  // graph thread does not run during the initialization.
  // graph thread does not run during the initialization.
  TimeStamp mInitialTimeStamp;
  TimeStamp mCurrentTimeStamp;
  TimeStamp mLastTimeStamp;
};


/**
 * An OfflineClockDriver runs the graph as fast as possible, without waiting
 * between iteration.
 */
class OfflineClockDriver : public ThreadedDriver {
 public:
  OfflineClockDriver(GraphInterface* aGraphInterface, uint32_t aSampleRate,
  OfflineClockDriver(GraphInterface* aGraphInterface, uint32_t aSampleRate,
                     GraphTime aSlice);
  virtual ~OfflineClockDriver();
  OfflineClockDriver* AsOfflineClockDriver() override { return this; }

  void RunThread() override;
  void RunThread() override;

 protected:
  TimeDuration WaitInterval() override { return TimeDuration(); }
  MediaTime GetIntervalForIteration() override;


 private:
  // Time, in GraphTime, for each iteration
  GraphTime mSlice;
  GraphTime mSlice;
};

struct TrackAndPromiseForOperation {
  TrackAndPromiseForOperation(
      MediaTrack* aTrack, dom::AudioContextOperation aOperation,
      MediaTrack* aTrack, dom::AudioContextOperation aOperation,
      AbstractThread* aMainThread,
      MozPromiseHolder<MediaTrackGraph::AudioContextOperationPromise>&&
          aHolder);
  TrackAndPromiseForOperation(TrackAndPromiseForOperation&& aOther) noexcept;
  RefPtr<MediaTrack> mTrack;
  RefPtr<MediaTrack> mTrack;
  dom::AudioContextOperation mOperation;
  RefPtr<AbstractThread> mMainThread;
  MozPromiseHolder<MediaTrackGraph::AudioContextOperationPromise> mHolder;
};


enum class AsyncCubebOperation { INIT, SHUTDOWN };
enum class AudioInputType { Unknown, Voice };

/**
 * This is a graph driver that is based on callback functions called by the
 * This is a graph driver that is based on callback functions called by the
 * audio api. This ensures minimal audio latency, because it means there is no
 * buffering happening: the audio is generated inside the callback.
 *
 * This design is less flexible than running our own thread:
 * - We have no control over the thread:
 * - We have no control over the thread:
 * - It cannot block, and it has to run for a shorter amount of time than the
 *   buffer it is going to fill, or an under-run is going to occur (short burst
 *   of silence in the final audio output).
 * - We can't know for sure when the callback function is going to be called
 *   (although we compute an estimation so we can schedule video frames)
 * - Creating and shutting the thread down is a blocking operation, that can
 * - Creating and shutting the thread down is a blocking operation, that can
 *   take _seconds_ in some cases (because IPC has to be set up, and
 *   sometimes hardware components are involved and need to be warmed up)
 * - We have no control on how much audio we generate, we have to return exactly
 *   the number of frames asked for by the callback. Since for the Web Audio
 *   API, we have to do block processing at 128 frames per block, we need to
 *   API, we have to do block processing at 128 frames per block, we need to
 *   keep a little spill buffer to store the extra frames.
 */
class AudioCallbackDriver : public GraphDriver,
                            public MixerCallbackReceiver
#if defined(XP_WIN)
#if defined(XP_WIN)
    ,
                            public audio::DeviceChangeListener
#endif
{
  using IterationResult = GraphInterface::IterationResult;
  using IterationResult = GraphInterface::IterationResult;
  enum class FallbackDriverState;
  class FallbackWrapper;

 public:
  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioCallbackDriver, override);

  /** If aInputChannelCount is zero, then this driver is output-only. */
  AudioCallbackDriver(GraphInterface* aGraphInterface,
  AudioCallbackDriver(GraphInterface* aGraphInterface,
                      GraphDriver* aPreviousDriver, uint32_t aSampleRate,
                      uint32_t aOutputChannelCount, uint32_t aInputChannelCount,
                      CubebUtils::AudioDeviceID aOutputDeviceID,
                      CubebUtils::AudioDeviceID aInputDeviceID,
                      AudioInputType aAudioInputType);
                      AudioInputType aAudioInputType);

  void Start() override;
  MOZ_CAN_RUN_SCRIPT void Shutdown() override;
#if defined(XP_WIN)
  void ResetDefaultDevice() override;
  void ResetDefaultDevice() override;
#endif

  /* Static wrapper function cubeb calls back. */
  static long DataCallback_s(cubeb_stream* aStream, void* aUser,
                             const void* aInputBuffer, void* aOutputBuffer,
                             const void* aInputBuffer, void* aOutputBuffer,
                             long aFrames);
  static void StateCallback_s(cubeb_stream* aStream, void* aUser,
                              cubeb_state aState);
  static void DeviceChangedCallback_s(void* aUser);


  /* This function is called by the underlying audio backend when a refill is
   * needed. This is what drives the whole graph when it is used to output
   * audio. If the return value is exactly aFrames, this function will get
   * called again. If it is less than aFrames, the stream will go in draining
   * called again. If it is less than aFrames, the stream will go in draining
   * mode, and this function will not be called again. */
  long DataCallback(const AudioDataValue* aInputBuffer,
                    AudioDataValue* aOutputBuffer, long aFrames);
  /* This function is called by the underlying audio backend, but is only used
   * for informational purposes at the moment. */
   * for informational purposes at the moment. */
  void StateCallback(cubeb_state aState);
  /* This is an approximation of the number of millisecond there are between two
   * iterations of the graph. */
  uint32_t IterationDuration() override;
  /* If the audio stream has started, this does nothing. There will be another
  /* If the audio stream has started, this does nothing. There will be another
   * iteration. If there is an active fallback driver, we forward the call so it
   * can wake up. */
  void EnsureNextIteration() override;


  /* This function gets called when the graph has produced the audio frames for
   * this iteration. */
  void MixerCallback(AudioDataValue* aMixedBuffer, AudioSampleFormat aFormat,
                     uint32_t aChannels, uint32_t aFrames,
                     uint32_t aSampleRate) override;
                     uint32_t aSampleRate) override;

  AudioCallbackDriver* AsAudioCallbackDriver() override { return this; }
  AudioCallbackDriver* AsAudioCallbackDriver() override { return this; }

  uint32_t OutputChannelCount() { return mOutputChannelCount; }

  uint32_t InputChannelCount() { return mInputChannelCount; }


  AudioInputType InputDevicePreference() {
    if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) {
      return AudioInputType::Voice;
    }
    return AudioInputType::Unknown;
    return AudioInputType::Unknown;
  }

  std::thread::id ThreadId() { return mAudioThreadIdInCb.load(); }

  void EnqueueTrackAndPromiseForOperation(
  /* Called when the thread servicing the callback has changed. This can be
   * fairly expensive */
  void OnThreadIdChanged();
  /* Called at the beginning of the audio callback to check if the thread id has
          aHolder);
   * changed. */
  bool CheckThreadIdChanged();

  bool OnThread() override {
    return mAudioThreadIdInCb.load() == std::this_thread::get_id();
    return mAudioThreadId.load() == std::this_thread::get_id();
  }

  /* Returns true if this audio callback driver has successfully started and not
   * yet stopped. If the fallback driver is active, this returns false. */
  bool ThreadRunning() override {
    return mAudioStreamState == AudioStreamState::Running;
  }
    return mAudioStreamState == AudioStreamState::Running;

  /* Whether the underlying cubeb stream has been started. See comment for
   * mStarted for details. */
  bool IsStarted();

  bool IsStarted();
  // Returns the output latency for the current audio output stream.
  TimeDuration AudioOutputLatency();
  // Returns the input latency for the current audio output stream.
  TimeDuration AudioInputLatency();

 private:
 private:
  /**
   * On certain MacBookPro, the microphone is located near the left speaker.
   * We need to pan the sound output to the right speaker if we are using the
   * mic and the built-in speaker, or we will have terrible echo.  */
  void PanOutputIfNeeded(bool aMicrophoneActive);
  void PanOutputIfNeeded(bool aMicrophoneActive);
  /**
   * This is called when the output device used by the cubeb stream changes. */
  void DeviceChangedCallback();
  /* Start the cubeb stream */
  bool StartStream();
  bool StartStream();
  friend class AsyncCubebTask;
  void Init();
  void Init();
  void Stop();
  /**
   *  Fall back to a SystemClockDriver using a normal thread. If needed,
   *  the graph will try to re-open an audio stream later. */
  void FallbackToSystemClockDriver();
  void FallbackToSystemClockDriver();
  /* Called by the fallback driver when it has fully stopped, after finishing
   * its last iteration. If it stopped after the audio stream started, aState
   * will be None. If it stopped after the graph told it to stop, or switch,
   * aState will be Stopped. Hands over state to the audio driver that may
   * iterate the graph after this has been called. */
   * iterate the graph after this has been called. */
  void FallbackDriverStopped(GraphTime aIterationStart, GraphTime aIterationEnd,
                             GraphTime aStateComputedTime,
                             FallbackDriverState aState);

  /* Called at the end of the fallback driver's iteration to see whether we
  /* Called at the end of the fallback driver's iteration to see whether we
   * should attempt to start the AudioStream again. */
  void MaybeStartAudioStream();

  /* This is true when the method is executed on CubebOperation thread pool. */
  bool OnCubebOperationThread() {
  bool OnCubebOperationThread() {
    return mInitShutdownThread->IsOnCurrentThreadInfallible();
  }

  /* MediaTrackGraphs are always down/up mixed to output channels. */
  const uint32_t mOutputChannelCount;
  /* The size of this buffer comes from the fact that some audio backends can
  const uint32_t mOutputChannelCount;
   * call back with a number of frames lower than one block (128 frames), so we
   * need to keep at most two block in the SpillBuffer, because we always round
   * up to block boundaries during an iteration.
   * This is only ever accessed on the audio callback thread. */
  SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2> mScratchBuffer;
   * This is only ever accessed on the audio callback thread. */
  /* Wrapper to ensure we write exactly the number of frames we need in the
   * audio buffer cubeb passes us. This is only ever accessed on the audio
   * callback thread. */
  AudioCallbackBufferWrapper<AudioDataValue> mBuffer;
  /* cubeb stream for this graph. This is non-null after a successful
  AudioCallbackBufferWrapper<AudioDataValue> mBuffer;
   * cubeb_stream_init(). CubebOperation thread only. */
  nsAutoRef<cubeb_stream> mAudioStream;
  /* The number of input channels from cubeb. Set before opening cubeb. If it is
   * zero then the driver is output-only. */
  const uint32_t mInputChannelCount;
   * zero then the driver is output-only. */
  /**
   * Devices to use for cubeb input & output, or nullptr for default device.
   */
  const CubebUtils::AudioDeviceID mOutputDeviceID;
  const CubebUtils::AudioDeviceID mInputDeviceID;
  const CubebUtils::AudioDeviceID mOutputDeviceID;
  /* Approximation of the time between two callbacks. This is used to schedule
   * video frames. This is in milliseconds. Only even used (after
   * inizatialization) on the audio callback thread. */
  uint32_t mIterationDurationMS;
  uint32_t mIterationDurationMS;
  /* cubeb_stream_init calls the audio callback to prefill the buffers. The
   * previous driver has to be kept alive until the audio stream has been
   * started, because it is responsible to call cubeb_stream_start, so we delay
   * the cleanup of the previous driver until it has started the audio stream.
   * Otherwise, there is a race where we kill the previous driver thread
   * Otherwise, there is a race where we kill the previous driver thread
   * between cubeb_stream_init and cubeb_stream_start,
   * and callbacks after the prefill never get called.
   * This is written on the previous driver's thread (if switching) or main
   * thread (if this driver is the first one).
   * This is read on previous driver's thread (during callbacks from
   * cubeb_stream_init) and the audio thread (when switching away from this
   * This is read on previous driver's thread (during callbacks from
   * driver back to a SystemClockDriver).
   * */
  Atomic<bool> mStarted;
  Atomic<bool> mStarted;

  struct AutoInCallback {
    explicit AutoInCallback(AudioCallbackDriver* aDriver);
    ~AutoInCallback();
    AudioCallbackDriver* mDriver;
    AudioCallbackDriver* mDriver;
  };

  /* Shared thread pool with up to one thread for off-main-thread
   * initialization and shutdown of the audio stream via AsyncCubebTask. */
  const RefPtr<SharedThreadPool> mInitShutdownThread;
   * initialization and shutdown of the audio stream via AsyncCubebTask. */
  cubeb_device_pref mInputDevicePreference;
  /* The mixer that the graph mixes into during an iteration. Audio thread only.
   */
  AudioMixer mMixer;
  /* Contains the id of the audio thread id. When this changed,
   */
   * OnAudioThreadChanged is called. */
  std::atomic<std::thread::id> mAudioThreadId;

  /* This allows implementing AutoInCallback. This is equal to the current
   * thread id when in an audio callback, and is an invalid thread id otherwise.
   */
    /* There is no AudioStream and no pending AsyncCubebTask to INIT one. */
  std::atomic<std::thread::id> mAudioThreadIdInCb;
  /* State of the audio stream, see inline comments. */
  enum class AudioStreamState {
    /* There is no AudioStream and no pending AsyncCubebTask to INIT one. */
    None,
    Running,
    /* There is no AudioStream but an AsyncCubebTask to INIT one is pending. */
    Pending,
    /* There is a running AudioStream. */
    Running,
    /* There is an AudioStream that is draining, and will soon stop. */
    Errored,
    Stopping,
    /* There is an AudioStream that has errored, the Fallback driver needs to
     * continue advancing the graph. */
    Errored,
  };
    /* There is no fallback driver. */
  Atomic<AudioStreamState> mAudioStreamState;
  /* State of the fallback driver, see inline comments. */
  enum class FallbackDriverState {
    /* There is no fallback driver. */
    None,
    /* There is a fallback driver trying to iterate us. */
    Running,
    /* There was a fallback driver and the graph stopped it. No audio callback
  };
       may iterate the graph. */
    Stopped,
  };
  Atomic<FallbackDriverState> mFallbackDriverState{FallbackDriverState::None};
  /* SystemClockDriver used as fallback if this AudioCallbackDriver fails to
  /* Set to true in the first iteration after starting. Accessed in data
   * init or start. */
  DataMutex<RefPtr<FallbackWrapper>> mFallback;
  /* Set to true in the first iteration after starting. Accessed in data
   * callback while running, or in Start(). */
  bool mRanFirstIteration = false;
  TimeDuration mNextReInitBackoffStep;
  /* If using a fallback driver, this is the duration to wait after failing to
   * start it before attempting to start it again. */
  TimeDuration mNextReInitBackoffStep;
  /* If using a fallback driver, this is the next time we'll try to start the
   * audio stream. */
  /* When using the built-in speakers on macbook pro (13 and 15, all models),
  TimeStamp mNextReInitAttempt;
#ifdef XP_MACOSX
  /* When using the built-in speakers on macbook pro (13 and 15, all models),
   * it's best to hard pan the audio on the right, to avoid feedback into the
   * microphone that is located next to the left speaker.  */
  Atomic<bool> mNeedsPanning;
  WavDumper mOutputStreamFile;
#endif

  WavDumper mInputStreamFile;
  WavDumper mOutputStreamFile;

class AsyncCubebTask : public Runnable {
  virtual ~AudioCallbackDriver();
  const bool mSandboxed = false;
};

class AsyncCubebTask : public Runnable {
 public:
    return mDriver->mInitShutdownThread->Dispatch(this, aFlags);
  AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation);

  nsresult Dispatch(uint32_t aFlags = NS_DISPATCH_NORMAL) {
    return mDriver->mInitShutdownThread->Dispatch(this, aFlags);
  }


 protected:
  virtual ~AsyncCubebTask();

 private:
  AsyncCubebOperation mOperation;
  NS_IMETHOD Run() final;

  RefPtr<AudioCallbackDriver> mDriver;
  AsyncCubebOperation mOperation;
  RefPtr<GraphInterface> mShutdownGrip;
};

}  // namespace mozilla

#endif  // GRAPHDRIVER_H_