LCOV - code coverage report
Current view: top level - dom/media - GraphDriver.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1 524 0.2 %
Date: 2017-07-14 16:53:18 Functions: 0 80 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       5             : 
       6             : #include <MediaStreamGraphImpl.h>
       7             : #include "mozilla/dom/AudioContext.h"
       8             : #include "mozilla/SharedThreadPool.h"
       9             : #include "mozilla/ClearOnShutdown.h"
      10             : #include "mozilla/Unused.h"
      11             : #include "CubebUtils.h"
      12             : 
      13             : #ifdef MOZ_WEBRTC
      14             : #include "webrtc/MediaEngineWebRTC.h"
      15             : #endif
      16             : 
      17             : #ifdef XP_MACOSX
      18             : #include <sys/sysctl.h>
      19             : #endif
      20             : 
      21             : extern mozilla::LazyLogModule gMediaStreamGraphLog;
      22             : #ifdef LOG
      23             : #undef LOG
      24             : #endif // LOG
      25             : #define LOG(type, msg) MOZ_LOG(gMediaStreamGraphLog, type, msg)
      26             : 
      27             : namespace mozilla {
      28             : 
      29           3 : StaticRefPtr<nsIThreadPool> AsyncCubebTask::sThreadPool;
      30             : 
      31           0 : GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
      32             :   : mIterationStart(0),
      33             :     mIterationEnd(0),
      34             :     mGraphImpl(aGraphImpl),
      35             :     mWaitState(WAITSTATE_RUNNING),
      36             :     mCurrentTimeStamp(TimeStamp::Now()),
      37             :     mPreviousDriver(nullptr),
      38           0 :     mNextDriver(nullptr)
      39           0 : { }
      40             : 
      41           0 : void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver,
      42             :                                GraphTime aLastSwitchNextIterationStart,
      43             :                                GraphTime aLastSwitchNextIterationEnd)
      44             : {
      45           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
      46             :   // We set mIterationEnd here, because the first thing a driver do when it
      47             :   // does an iteration is to update graph times, so we are in fact setting
      48             :   // mIterationStart of the next iteration by setting the end of the previous
      49             :   // iteration.
      50           0 :   mIterationStart = aLastSwitchNextIterationStart;
      51           0 :   mIterationEnd = aLastSwitchNextIterationEnd;
      52             : 
      53           0 :   MOZ_ASSERT(!PreviousDriver());
      54           0 :   MOZ_ASSERT(aPreviousDriver);
      55             : 
      56           0 :   LOG(LogLevel::Debug,
      57             :       ("Setting previous driver: %p (%s)",
      58             :        aPreviousDriver,
      59             :        aPreviousDriver->AsAudioCallbackDriver() ? "AudioCallbackDriver"
      60             :                                                 : "SystemClockDriver"));
      61             : 
      62           0 :   SetPreviousDriver(aPreviousDriver);
      63           0 : }
      64             : 
      65           0 : void GraphDriver::SwitchAtNextIteration(GraphDriver* aNextDriver)
      66             : {
      67           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
      68           0 :   LOG(LogLevel::Debug,
      69             :       ("Switching to new driver: %p (%s)",
      70             :        aNextDriver,
      71             :        aNextDriver->AsAudioCallbackDriver() ? "AudioCallbackDriver"
      72             :                                             : "SystemClockDriver"));
      73           0 :   if (mNextDriver &&
      74           0 :       mNextDriver != GraphImpl()->CurrentDriver()) {
      75           0 :     LOG(LogLevel::Debug,
      76             :         ("Discarding previous next driver: %p (%s)",
      77             :          mNextDriver.get(),
      78             :          mNextDriver->AsAudioCallbackDriver() ? "AudioCallbackDriver"
      79             :                                               : "SystemClockDriver"));
      80             :   }
      81           0 :   SetNextDriver(aNextDriver);
      82           0 : }
      83             : 
      84             : GraphTime
      85           0 : GraphDriver::StateComputedTime() const
      86             : {
      87           0 :   return mGraphImpl->mStateComputedTime;
      88             : }
      89             : 
      90           0 : void GraphDriver::EnsureNextIteration()
      91             : {
      92           0 :   mGraphImpl->EnsureNextIteration();
      93           0 : }
      94             : 
      95           0 : void GraphDriver::Shutdown()
      96             : {
      97           0 :   if (AsAudioCallbackDriver()) {
      98           0 :     LOG(LogLevel::Debug,
      99             :         ("Releasing audio driver off main thread (GraphDriver::Shutdown)."));
     100             :     RefPtr<AsyncCubebTask> releaseEvent =
     101           0 :       new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN);
     102           0 :     releaseEvent->Dispatch(NS_DISPATCH_SYNC);
     103             :   } else {
     104           0 :     Stop();
     105             :   }
     106           0 : }
     107             : 
     108           0 : bool GraphDriver::Switching()
     109             : {
     110           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
     111           0 :   return mNextDriver || mPreviousDriver;
     112             : }
     113             : 
     114           0 : GraphDriver* GraphDriver::NextDriver()
     115             : {
     116           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
     117           0 :   return mNextDriver;
     118             : }
     119             : 
     120           0 : GraphDriver* GraphDriver::PreviousDriver()
     121             : {
     122           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
     123           0 :   return mPreviousDriver;
     124             : }
     125             : 
     126           0 : void GraphDriver::SetNextDriver(GraphDriver* aNextDriver)
     127             : {
     128           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
     129           0 :   mNextDriver = aNextDriver;
     130           0 : }
     131             : 
     132           0 : void GraphDriver::SetPreviousDriver(GraphDriver* aPreviousDriver)
     133             : {
     134           0 :   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
     135           0 :   mPreviousDriver = aPreviousDriver;
     136           0 : }
     137             : 
     138           0 : ThreadedDriver::ThreadedDriver(MediaStreamGraphImpl* aGraphImpl)
     139           0 :   : GraphDriver(aGraphImpl)
     140           0 : { }
     141             : 
     142           0 : class MediaStreamGraphShutdownThreadRunnable : public Runnable {
     143             : public:
     144           0 :   explicit MediaStreamGraphShutdownThreadRunnable(
     145             :     already_AddRefed<nsIThread> aThread)
     146           0 :     : Runnable("MediaStreamGraphShutdownThreadRunnable")
     147           0 :     , mThread(aThread)
     148             :   {
     149           0 :   }
     150           0 :   NS_IMETHOD Run() override
     151             :   {
     152           0 :     MOZ_ASSERT(NS_IsMainThread());
     153           0 :     MOZ_ASSERT(mThread);
     154             : 
     155           0 :     mThread->Shutdown();
     156           0 :     mThread = nullptr;
     157           0 :     return NS_OK;
     158             :   }
     159             : private:
     160             :   nsCOMPtr<nsIThread> mThread;
     161             : };
     162             : 
     163           0 : ThreadedDriver::~ThreadedDriver()
     164             : {
     165           0 :   if (mThread) {
     166           0 :     if (NS_IsMainThread()) {
     167           0 :       mThread->Shutdown();
     168             :     } else {
     169             :       nsCOMPtr<nsIRunnable> event =
     170           0 :         new MediaStreamGraphShutdownThreadRunnable(mThread.forget());
     171           0 :       NS_DispatchToMainThread(event);
     172             :     }
     173             :   }
     174           0 : }
     175           0 : class MediaStreamGraphInitThreadRunnable : public Runnable {
     176             : public:
     177           0 :   explicit MediaStreamGraphInitThreadRunnable(ThreadedDriver* aDriver)
     178           0 :     : Runnable("MediaStreamGraphInitThreadRunnable")
     179           0 :     , mDriver(aDriver)
     180             :   {
     181           0 :   }
     182           0 :   NS_IMETHOD Run() override
     183             :   {
     184           0 :     LOG(LogLevel::Debug,
     185             :         ("Starting a new system driver for graph %p", mDriver->mGraphImpl));
     186             : 
     187           0 :     GraphDriver* previousDriver = nullptr;
     188             :     {
     189           0 :       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
     190           0 :       previousDriver = mDriver->PreviousDriver();
     191             :     }
     192           0 :     if (previousDriver) {
     193           0 :       LOG(LogLevel::Debug,
     194             :           ("%p releasing an AudioCallbackDriver(%p), for graph %p",
     195             :            mDriver.get(),
     196             :            previousDriver,
     197             :            mDriver->GraphImpl()));
     198           0 :       MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
     199             :       RefPtr<AsyncCubebTask> releaseEvent =
     200           0 :         new AsyncCubebTask(previousDriver->AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN);
     201           0 :       releaseEvent->Dispatch();
     202             : 
     203           0 :       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
     204           0 :       mDriver->SetPreviousDriver(nullptr);
     205             :     } else {
     206           0 :       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
     207           0 :       MOZ_ASSERT(mDriver->mGraphImpl->MessagesQueued() ||
     208             :                  mDriver->mGraphImpl->mForceShutDown, "Don't start a graph without messages queued.");
     209           0 :       mDriver->mGraphImpl->SwapMessageQueues();
     210             :     }
     211             : 
     212           0 :     mDriver->RunThread();
     213           0 :     return NS_OK;
     214             :   }
     215             : private:
     216             :   RefPtr<ThreadedDriver> mDriver;
     217             : };
     218             : 
     219             : void
     220           0 : ThreadedDriver::Start()
     221             : {
     222           0 :   LOG(LogLevel::Debug,
     223             :       ("Starting thread for a SystemClockDriver  %p", mGraphImpl));
     224           0 :   Unused << NS_WARN_IF(mThread);
     225           0 :   if (!mThread) { // Ensure we haven't already started it
     226           0 :     nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
     227             :     // Note: mThread may be null during event->Run() if we pass to NewNamedThread!  See AudioInitTask
     228           0 :     nsresult rv = NS_NewNamedThread("MediaStreamGrph", getter_AddRefs(mThread));
     229           0 :     if (NS_SUCCEEDED(rv)) {
     230           0 :       mThread->EventTarget()->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
     231             :     }
     232             :   }
     233           0 : }
     234             : 
     235             : void
     236           0 : ThreadedDriver::Resume()
     237             : {
     238           0 :   Start();
     239           0 : }
     240             : 
     241             : void
     242           0 : ThreadedDriver::Revive()
     243             : {
     244             :   // Note: only called on MainThread, without monitor
     245             :   // We know were weren't in a running state
     246           0 :   LOG(LogLevel::Debug, ("AudioCallbackDriver reviving."));
     247             :   // If we were switching, switch now. Otherwise, tell thread to run the main
     248             :   // loop again.
     249           0 :   MonitorAutoLock mon(mGraphImpl->GetMonitor());
     250           0 :   if (NextDriver()) {
     251           0 :     NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
     252           0 :     mGraphImpl->SetCurrentDriver(NextDriver());
     253           0 :     NextDriver()->Start();
     254             :   } else {
     255           0 :     nsCOMPtr<nsIRunnable> event = new MediaStreamGraphInitThreadRunnable(this);
     256           0 :     mThread->EventTarget()->Dispatch(event.forget(), NS_DISPATCH_NORMAL);
     257             :   }
     258           0 : }
     259             : 
     260             : void
     261           0 : ThreadedDriver::RemoveCallback()
     262             : {
     263           0 : }
     264             : 
     265             : void
     266           0 : ThreadedDriver::Stop()
     267             : {
     268           0 :   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
     269             :   // mGraph's thread is not running so it's OK to do whatever here
     270           0 :   LOG(LogLevel::Debug, ("Stopping threads for MediaStreamGraph %p", this));
     271             : 
     272           0 :   if (mThread) {
     273           0 :     mThread->Shutdown();
     274           0 :     mThread = nullptr;
     275             :   }
     276           0 : }
     277             : 
     278           0 : SystemClockDriver::SystemClockDriver(MediaStreamGraphImpl* aGraphImpl)
     279             :   : ThreadedDriver(aGraphImpl),
     280             :     mInitialTimeStamp(TimeStamp::Now()),
     281             :     mLastTimeStamp(TimeStamp::Now()),
     282           0 :     mIsFallback(false)
     283           0 : {}
     284             : 
     285           0 : SystemClockDriver::~SystemClockDriver()
     286           0 : { }
     287             : 
     288             : void
     289           0 : SystemClockDriver::MarkAsFallback()
     290             : {
     291           0 :   mIsFallback = true;
     292           0 : }
     293             : 
     294             : bool
     295           0 : SystemClockDriver::IsFallback()
     296             : {
     297           0 :   return mIsFallback;
     298             : }
     299             : 
     300             : void
     301           0 : ThreadedDriver::RunThread()
     302             : {
     303           0 :   bool stillProcessing = true;
     304           0 :   while (stillProcessing) {
     305           0 :     mIterationStart = IterationEnd();
     306           0 :     mIterationEnd += GetIntervalForIteration();
     307             : 
     308           0 :     GraphTime stateComputedTime = StateComputedTime();
     309           0 :     if (stateComputedTime < mIterationEnd) {
     310           0 :       LOG(LogLevel::Warning, ("Media graph global underrun detected"));
     311           0 :       mIterationEnd = stateComputedTime;
     312             :     }
     313             : 
     314           0 :     if (mIterationStart >= mIterationEnd) {
     315           0 :       NS_ASSERTION(mIterationStart == mIterationEnd ,
     316             :                    "Time can't go backwards!");
     317             :       // This could happen due to low clock resolution, maybe?
     318           0 :       LOG(LogLevel::Debug, ("Time did not advance"));
     319             :     }
     320             : 
     321             :     GraphTime nextStateComputedTime =
     322           0 :       mGraphImpl->RoundUpToNextAudioBlock(
     323           0 :         mIterationEnd + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS));
     324           0 :     if (nextStateComputedTime < stateComputedTime) {
     325             :       // A previous driver may have been processing further ahead of
     326             :       // iterationEnd.
     327           0 :       LOG(LogLevel::Warning,
     328             :           ("Prevent state from going backwards. interval[%ld; %ld] state[%ld; "
     329             :            "%ld]",
     330             :            (long)mIterationStart,
     331             :            (long)mIterationEnd,
     332             :            (long)stateComputedTime,
     333             :            (long)nextStateComputedTime));
     334           0 :       nextStateComputedTime = stateComputedTime;
     335             :     }
     336           0 :     LOG(LogLevel::Verbose,
     337             :         ("interval[%ld; %ld] state[%ld; %ld]",
     338             :          (long)mIterationStart,
     339             :          (long)mIterationEnd,
     340             :          (long)stateComputedTime,
     341             :          (long)nextStateComputedTime));
     342             : 
     343           0 :     stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime);
     344             : 
     345           0 :     MonitorAutoLock lock(GraphImpl()->GetMonitor());
     346           0 :     if (NextDriver() && stillProcessing) {
     347           0 :       LOG(LogLevel::Debug, ("Switching to AudioCallbackDriver"));
     348           0 :       RemoveCallback();
     349           0 :       NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
     350           0 :       mGraphImpl->SetCurrentDriver(NextDriver());
     351           0 :       NextDriver()->Start();
     352           0 :       return;
     353             :     }
     354             :   }
     355             : }
     356             : 
     357             : MediaTime
     358           0 : SystemClockDriver::GetIntervalForIteration()
     359             : {
     360           0 :   TimeStamp now = TimeStamp::Now();
     361             :   MediaTime interval =
     362           0 :     mGraphImpl->SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds());
     363           0 :   mCurrentTimeStamp = now;
     364             : 
     365           0 :   MOZ_LOG(gMediaStreamGraphLog, LogLevel::Verbose,
     366             :           ("Updating current time to %f (real %f, StateComputedTime() %f)",
     367             :            mGraphImpl->MediaTimeToSeconds(IterationEnd() + interval),
     368             :            (now - mInitialTimeStamp).ToSeconds(),
     369             :            mGraphImpl->MediaTimeToSeconds(StateComputedTime())));
     370             : 
     371           0 :   return interval;
     372             : }
     373             : 
     374             : TimeStamp
     375           0 : OfflineClockDriver::GetCurrentTimeStamp()
     376             : {
     377           0 :   MOZ_CRASH("This driver does not support getting the current timestamp.");
     378             :   return TimeStamp();
     379             : }
     380             : 
     381             : void
     382           0 : SystemClockDriver::WaitForNextIteration()
     383             : {
     384           0 :   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
     385             : 
     386           0 :   PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
     387           0 :   TimeStamp now = TimeStamp::Now();
     388             : 
     389             :   // This lets us avoid hitting the Atomic twice when we know we won't sleep
     390           0 :   bool another = mGraphImpl->mNeedAnotherIteration; // atomic
     391           0 :   if (!another) {
     392           0 :     mGraphImpl->mGraphDriverAsleep = true; // atomic
     393           0 :     mWaitState = WAITSTATE_WAITING_INDEFINITELY;
     394             :   }
     395             :   // NOTE: mNeedAnotherIteration while also atomic may have changed before
     396             :   // we could set mGraphDriverAsleep, so we must re-test it.
     397             :   // (EnsureNextIteration sets mNeedAnotherIteration, then tests
     398             :   // mGraphDriverAsleep
     399           0 :   if (another || mGraphImpl->mNeedAnotherIteration) { // atomic
     400           0 :     int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
     401           0 :       int64_t((now - mCurrentTimeStamp).ToMilliseconds());
     402             :     // Make sure timeoutMS doesn't overflow 32 bits by waking up at
     403             :     // least once a minute, if we need to wake up at all
     404           0 :     timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
     405           0 :     timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
     406           0 :     LOG(LogLevel::Verbose,
     407             :         ("Waiting for next iteration; at %f, timeout=%f",
     408             :          (now - mInitialTimeStamp).ToSeconds(),
     409             :          timeoutMS / 1000.0));
     410           0 :     if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
     411           0 :       mGraphImpl->mGraphDriverAsleep = false; // atomic
     412             :     }
     413           0 :     mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
     414             :   }
     415           0 :   if (timeout > 0) {
     416           0 :     mGraphImpl->GetMonitor().Wait(timeout);
     417           0 :     LOG(LogLevel::Verbose,
     418             :         ("Resuming after timeout; at %f, elapsed=%f",
     419             :          (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
     420             :          (TimeStamp::Now() - now).ToSeconds()));
     421             :   }
     422             : 
     423           0 :   if (mWaitState == WAITSTATE_WAITING_INDEFINITELY) {
     424           0 :     mGraphImpl->mGraphDriverAsleep = false; // atomic
     425             :   }
     426             :   // Note: this can race against the EnsureNextIteration setting
     427             :   // WAITSTATE_RUNNING and setting mGraphDriverAsleep to false, so you can
     428             :   // have an iteration with WAITSTATE_WAKING_UP instead of RUNNING.
     429           0 :   mWaitState = WAITSTATE_RUNNING;
     430           0 :   mGraphImpl->mNeedAnotherIteration = false; // atomic
     431           0 : }
     432             : 
     433           0 : void SystemClockDriver::WakeUp()
     434             : {
     435           0 :   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
     436             :   // Note: this can race against the thread setting WAITSTATE_RUNNING and
     437             :   // setting mGraphDriverAsleep to false, so you can have an iteration
     438             :   // with WAITSTATE_WAKING_UP instead of RUNNING.
     439           0 :   mWaitState = WAITSTATE_WAKING_UP;
     440           0 :   mGraphImpl->mGraphDriverAsleep = false; // atomic
     441           0 :   mGraphImpl->GetMonitor().Notify();
     442           0 : }
     443             : 
     444           0 : OfflineClockDriver::OfflineClockDriver(MediaStreamGraphImpl* aGraphImpl, GraphTime aSlice)
     445             :   : ThreadedDriver(aGraphImpl),
     446           0 :     mSlice(aSlice)
     447             : {
     448             : 
     449           0 : }
     450             : 
     451           0 : OfflineClockDriver::~OfflineClockDriver()
     452             : {
     453           0 : }
     454             : 
     455             : MediaTime
     456           0 : OfflineClockDriver::GetIntervalForIteration()
     457             : {
     458           0 :   return mGraphImpl->MillisecondsToMediaTime(mSlice);
     459             : }
     460             : 
     461             : void
     462           0 : OfflineClockDriver::WaitForNextIteration()
     463             : {
     464             :   // No op: we want to go as fast as possible when we are offline
     465           0 : }
     466             : 
     467             : void
     468           0 : OfflineClockDriver::WakeUp()
     469             : {
     470           0 :   MOZ_ASSERT(false, "An offline graph should not have to wake up.");
     471             : }
     472             : 
     473           0 : AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver,
     474           0 :                                AsyncCubebOperation aOperation)
     475             :   : Runnable("AsyncCubebTask")
     476             :   , mDriver(aDriver)
     477             :   , mOperation(aOperation)
     478           0 :   , mShutdownGrip(aDriver->GraphImpl())
     479             : {
     480           0 :   NS_WARNING_ASSERTION(mDriver->mAudioStream || aOperation == INIT,
     481             :                        "No audio stream!");
     482           0 : }
     483             : 
     484           0 : AsyncCubebTask::~AsyncCubebTask()
     485             : {
     486           0 : }
     487             : 
     488             : /* static */
     489             : nsresult
     490           0 : AsyncCubebTask::EnsureThread()
     491             : {
     492           0 :   if (!sThreadPool) {
     493             :     nsCOMPtr<nsIThreadPool> threadPool =
     494           0 :       SharedThreadPool::Get(NS_LITERAL_CSTRING("CubebOperation"), 1);
     495           0 :     sThreadPool = threadPool;
     496             :     // Need to null this out before xpcom-shutdown-threads Observers run
     497             :     // since we don't know the order that the shutdown-threads observers
     498             :     // will run.  ClearOnShutdown guarantees it runs first.
     499           0 :     if (!NS_IsMainThread()) {
     500           0 :       NS_DispatchToMainThread(
     501           0 :         NS_NewRunnableFunction("AsyncCubebTask::EnsureThread", []() -> void {
     502           0 :           ClearOnShutdown(&sThreadPool, ShutdownPhase::ShutdownThreads);
     503           0 :         }));
     504             :     } else {
     505           0 :       ClearOnShutdown(&sThreadPool, ShutdownPhase::ShutdownThreads);
     506             :     }
     507             : 
     508           0 :     const uint32_t kIdleThreadTimeoutMs = 2000;
     509             : 
     510           0 :     nsresult rv = sThreadPool->SetIdleThreadTimeout(PR_MillisecondsToInterval(kIdleThreadTimeoutMs));
     511           0 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     512           0 :       return rv;
     513             :     }
     514             :   }
     515             : 
     516           0 :   return NS_OK;
     517             : }
     518             : 
     519             : NS_IMETHODIMP
     520           0 : AsyncCubebTask::Run()
     521             : {
     522           0 :   MOZ_ASSERT(mDriver);
     523             : 
     524           0 :   switch(mOperation) {
     525             :     case AsyncCubebOperation::INIT: {
     526           0 :       LOG(LogLevel::Debug,
     527             :           ("AsyncCubebOperation::INIT driver=%p", mDriver.get()));
     528           0 :       if (!mDriver->Init()) {
     529           0 :         return NS_ERROR_FAILURE;
     530             :       }
     531           0 :       mDriver->CompleteAudioContextOperations(mOperation);
     532           0 :       break;
     533             :     }
     534             :     case AsyncCubebOperation::SHUTDOWN: {
     535           0 :       LOG(LogLevel::Debug,
     536             :           ("AsyncCubebOperation::SHUTDOWN driver=%p", mDriver.get()));
     537           0 :       mDriver->Stop();
     538             : 
     539           0 :       mDriver->CompleteAudioContextOperations(mOperation);
     540             : 
     541           0 :       mDriver = nullptr;
     542           0 :       mShutdownGrip = nullptr;
     543           0 :       break;
     544             :     }
     545             :     default:
     546           0 :       MOZ_CRASH("Operation not implemented.");
     547             :   }
     548             : 
     549             :   // The thread will kill itself after a bit
     550           0 :   return NS_OK;
     551             : }
     552             : 
     553           0 : StreamAndPromiseForOperation::StreamAndPromiseForOperation(MediaStream* aStream,
     554             :                                           void* aPromise,
     555           0 :                                           dom::AudioContextOperation aOperation)
     556             :   : mStream(aStream)
     557             :   , mPromise(aPromise)
     558           0 :   , mOperation(aOperation)
     559             : {
     560             :   // MOZ_ASSERT(aPromise);
     561           0 : }
     562             : 
     563           0 : AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
     564             :   : GraphDriver(aGraphImpl)
     565             :   , mSampleRate(0)
     566             :   , mInputChannels(1)
     567             :   , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
     568             :   , mStarted(false)
     569             :   , mAudioInput(nullptr)
     570           0 :   , mAudioChannel(aGraphImpl->AudioChannel())
     571             :   , mAddedMixer(false)
     572             :   , mInCallback(false)
     573             :   , mMicrophoneActive(false)
     574           0 :   , mFromFallback(false)
     575             : {
     576           0 :   LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
     577           0 : }
     578             : 
     579           0 : AudioCallbackDriver::~AudioCallbackDriver()
     580             : {
     581           0 :   MOZ_ASSERT(mPromisesForOperation.IsEmpty());
     582           0 : }
     583             : 
     584           0 : bool IsMacbookOrMacbookAir()
     585             : {
     586             : #ifdef XP_MACOSX
     587             :   size_t len = 0;
     588             :   sysctlbyname("hw.model", NULL, &len, NULL, 0);
     589             :   if (len) {
     590             :     UniquePtr<char[]> model(new char[len]);
     591             :     // This string can be
     592             :     // MacBook%d,%d for a normal MacBook
     593             :     // MacBookPro%d,%d for a MacBook Pro
     594             :     // MacBookAir%d,%d for a Macbook Air
     595             :     sysctlbyname("hw.model", model.get(), &len, NULL, 0);
     596             :     char* substring = strstr(model.get(), "MacBook");
     597             :     if (substring) {
     598             :       const size_t offset = strlen("MacBook");
     599             :       if (strncmp(model.get() + offset, "Air", len - offset) ||
     600             :           isdigit(model[offset + 1])) {
     601             :         return true;
     602             :       }
     603             :     }
     604             :     return false;
     605             :   }
     606             : #endif
     607           0 :   return false;
     608             : }
     609             : 
     610             : bool
     611           0 : AudioCallbackDriver::Init()
     612             : {
     613           0 :   cubeb* cubebContext = CubebUtils::GetCubebContext();
     614           0 :   if (!cubebContext) {
     615           0 :     NS_WARNING("Could not get cubeb context.");
     616           0 :     if (!mFromFallback) {
     617           0 :       CubebUtils::ReportCubebStreamInitFailure(true);
     618             :     }
     619           0 :     return false;
     620             :   }
     621             : 
     622             :   cubeb_stream_params output;
     623             :   cubeb_stream_params input;
     624             :   uint32_t latency_frames;
     625           0 :   bool firstStream = CubebUtils::GetFirstStream();
     626             : 
     627           0 :   MOZ_ASSERT(!NS_IsMainThread(),
     628             :       "This is blocking and should never run on the main thread.");
     629             : 
     630           0 :   mSampleRate = output.rate = CubebUtils::PreferredSampleRate();
     631             : 
     632             : #if defined(__ANDROID__)
     633             : #if defined(MOZ_B2G)
     634             :   output.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
     635             : #else
     636             :   output.stream_type = CUBEB_STREAM_TYPE_MUSIC;
     637             : #endif
     638             :   if (output.stream_type == CUBEB_STREAM_TYPE_MAX) {
     639             :     NS_WARNING("Bad stream type");
     640             :     return false;
     641             :   }
     642             : #else
     643             :   (void)mAudioChannel;
     644             : #endif
     645             : 
     646           0 :   output.channels = mGraphImpl->AudioChannelCount();
     647             :   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
     648             :     output.format = CUBEB_SAMPLE_S16NE;
     649             :   } else {
     650           0 :     output.format = CUBEB_SAMPLE_FLOAT32NE;
     651             :   }
     652             : 
     653             :   // Graphs are always stereo for now.
     654           0 :   output.layout = CUBEB_LAYOUT_STEREO;
     655             : 
     656           0 :   Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
     657           0 :   if (latencyPref) {
     658           0 :     latency_frames = latencyPref.value();
     659             :   } else {
     660           0 :     if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
     661           0 :       NS_WARNING("Could not get minimal latency from cubeb.");
     662             :     }
     663             :   }
     664             : 
     665             :   // Macbook and MacBook air don't have enough CPU to run very low latency
     666             :   // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
     667           0 :   if (IsMacbookOrMacbookAir()) {
     668           0 :     latency_frames = std::max((uint32_t) 512, latency_frames);
     669             :   }
     670             : 
     671           0 :   input = output;
     672           0 :   input.channels = mInputChannels;
     673           0 :   input.layout = CUBEB_LAYOUT_UNDEFINED;
     674             : 
     675             : #ifdef MOZ_WEBRTC
     676           0 :   if (mGraphImpl->mInputWanted) {
     677           0 :     StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
     678           0 :     uint32_t userChannels = 0;
     679           0 :     AudioInputCubeb::GetUserChannelCount(mGraphImpl->mInputDeviceID, userChannels);
     680           0 :     input.channels = mInputChannels = userChannels;
     681             :   }
     682             : #endif
     683             : 
     684           0 :   cubeb_stream* stream = nullptr;
     685           0 :   CubebUtils::AudioDeviceID input_id = nullptr, output_id = nullptr;
     686             :   // We have to translate the deviceID values to cubeb devid's since those can be
     687             :   // freed whenever enumerate is called.
     688             :   {
     689             : #ifdef MOZ_WEBRTC
     690           0 :     StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
     691             : #endif
     692           0 :     if ((!mGraphImpl->mInputWanted
     693             : #ifdef MOZ_WEBRTC
     694           0 :          || AudioInputCubeb::GetDeviceID(mGraphImpl->mInputDeviceID, input_id)
     695             : #endif
     696           0 :          ) &&
     697           0 :         (mGraphImpl->mOutputDeviceID == -1 // pass nullptr for ID for default output
     698             : #ifdef MOZ_WEBRTC
     699             :          // XXX we should figure out how we would use a deviceID for output without webrtc.
     700             :          // Currently we don't set this though, so it's ok
     701           0 :          || AudioInputCubeb::GetDeviceID(mGraphImpl->mOutputDeviceID, output_id)
     702             : #endif
     703           0 :          ) &&
     704             :         // XXX Only pass input input if we have an input listener.  Always
     705             :         // set up output because it's easier, and it will just get silence.
     706             :         // XXX Add support for adding/removing an input listener later.
     707           0 :         cubeb_stream_init(cubebContext, &stream,
     708             :                           "AudioCallbackDriver",
     709             :                           input_id,
     710           0 :                           mGraphImpl->mInputWanted ? &input : nullptr,
     711             :                           output_id,
     712           0 :                           mGraphImpl->mOutputWanted ? &output : nullptr, latency_frames,
     713             :                           DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
     714           0 :       mAudioStream.own(stream);
     715           0 :       DebugOnly<int> rv = cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
     716           0 :       NS_WARNING_ASSERTION(
     717             :         rv == CUBEB_OK,
     718             :         "Could not set the audio stream volume in GraphDriver.cpp");
     719           0 :       CubebUtils::ReportCubebBackendUsed();
     720             :     } else {
     721             : #ifdef MOZ_WEBRTC
     722           0 :       StaticMutexAutoUnlock unlock(AudioInputCubeb::Mutex());
     723             : #endif
     724           0 :       NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
     725             :       // Only report failures when we're not coming from a driver that was
     726             :       // created itself as a fallback driver because of a previous audio driver
     727             :       // failure.
     728           0 :       if (!mFromFallback) {
     729           0 :         CubebUtils::ReportCubebStreamInitFailure(firstStream);
     730             :       }
     731             :       // Fall back to a driver using a normal thread. If needed,
     732             :       // the graph will try to re-open an audio stream later.
     733           0 :       MonitorAutoLock lock(GraphImpl()->GetMonitor());
     734           0 :       SystemClockDriver* nextDriver = new SystemClockDriver(GraphImpl());
     735           0 :       SetNextDriver(nextDriver);
     736           0 :       nextDriver->MarkAsFallback();
     737           0 :       nextDriver->SetGraphTime(this, mIterationStart, mIterationEnd);
     738             :       // We're not using SwitchAtNextIteration here, because there
     739             :       // won't be a next iteration if we don't restart things manually:
     740             :       // the audio stream just signaled that it's in error state.
     741           0 :       mGraphImpl->SetCurrentDriver(nextDriver);
     742           0 :       nextDriver->Start();
     743           0 :       return true;
     744             :     }
     745             :   }
     746           0 :   SetMicrophoneActive(mGraphImpl->mInputWanted);
     747             : 
     748           0 :   cubeb_stream_register_device_changed_callback(mAudioStream,
     749           0 :                                                 AudioCallbackDriver::DeviceChangedCallback_s);
     750             : 
     751           0 :   if (!StartStream()) {
     752           0 :     LOG(LogLevel::Warning, ("AudioCallbackDriver couldn't start stream."));
     753           0 :     return false;
     754             :   }
     755             : 
     756           0 :   LOG(LogLevel::Debug, ("AudioCallbackDriver started."));
     757           0 :   return true;
     758             : }
     759             : 
     760             : 
     761             : void
     762           0 : AudioCallbackDriver::Destroy()
     763             : {
     764           0 :   LOG(LogLevel::Debug, ("AudioCallbackDriver destroyed."));
     765           0 :   mAudioInput = nullptr;
     766           0 :   mAudioStream.reset();
     767           0 : }
     768             : 
     769             : void
     770           0 : AudioCallbackDriver::Resume()
     771             : {
     772           0 :   LOG(LogLevel::Debug,
     773             :       ("Resuming audio threads for MediaStreamGraph %p", mGraphImpl));
     774           0 :   if (cubeb_stream_start(mAudioStream) != CUBEB_OK) {
     775           0 :     NS_WARNING("Could not start cubeb stream for MSG.");
     776             :   }
     777           0 : }
     778             : 
     779             : void
     780           0 : AudioCallbackDriver::Start()
     781             : {
     782           0 :   if (mPreviousDriver) {
     783           0 :     if (mPreviousDriver->AsAudioCallbackDriver()) {
     784           0 :       LOG(LogLevel::Debug, ("Releasing audio driver off main thread."));
     785             :       RefPtr<AsyncCubebTask> releaseEvent =
     786           0 :         new AsyncCubebTask(mPreviousDriver->AsAudioCallbackDriver(),
     787           0 :                            AsyncCubebOperation::SHUTDOWN);
     788           0 :       releaseEvent->Dispatch();
     789           0 :       mPreviousDriver = nullptr;
     790             :     } else {
     791           0 :       LOG(LogLevel::Debug,
     792             :           ("Dropping driver reference for SystemClockDriver."));
     793           0 :       MOZ_ASSERT(mPreviousDriver->AsSystemClockDriver());
     794           0 :       mFromFallback = mPreviousDriver->AsSystemClockDriver()->IsFallback();
     795           0 :       mPreviousDriver = nullptr;
     796             :     }
     797             :   }
     798             : 
     799           0 :   LOG(LogLevel::Debug,
     800             :       ("Starting new audio driver off main thread, "
     801             :        "to ensure it runs after previous shutdown."));
     802             :   RefPtr<AsyncCubebTask> initEvent =
     803           0 :     new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::INIT);
     804           0 :   initEvent->Dispatch();
     805           0 : }
     806             : 
     807             : bool
     808           0 : AudioCallbackDriver::StartStream()
     809             : {
     810           0 :   if (cubeb_stream_start(mAudioStream) != CUBEB_OK) {
     811           0 :     NS_WARNING("Could not start cubeb stream for MSG.");
     812           0 :     return false;
     813             :   }
     814             : 
     815             :   {
     816           0 :     MonitorAutoLock mon(mGraphImpl->GetMonitor());
     817           0 :     mStarted = true;
     818           0 :     mWaitState = WAITSTATE_RUNNING;
     819             :   }
     820           0 :   return true;
     821             : }
     822             : 
     823             : void
     824           0 : AudioCallbackDriver::Stop()
     825             : {
     826           0 :   if (cubeb_stream_stop(mAudioStream) != CUBEB_OK) {
     827           0 :     NS_WARNING("Could not stop cubeb stream for MSG.");
     828             :   }
     829           0 : }
     830             : 
     831             : void
     832           0 : AudioCallbackDriver::Revive()
     833             : {
     834             :   // Note: only called on MainThread, without monitor
     835             :   // We know were weren't in a running state
     836           0 :   LOG(LogLevel::Debug, ("AudioCallbackDriver reviving."));
     837             :   // If we were switching, switch now. Otherwise, start the audio thread again.
     838           0 :   MonitorAutoLock mon(mGraphImpl->GetMonitor());
     839           0 :   if (NextDriver()) {
     840           0 :     RemoveCallback();
     841           0 :     NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
     842           0 :     mGraphImpl->SetCurrentDriver(NextDriver());
     843           0 :     NextDriver()->Start();
     844             :   } else {
     845           0 :     LOG(LogLevel::Debug,
     846             :         ("Starting audio threads for MediaStreamGraph %p from a new thread.",
     847             :          mGraphImpl));
     848             :     RefPtr<AsyncCubebTask> initEvent =
     849           0 :       new AsyncCubebTask(this, AsyncCubebOperation::INIT);
     850           0 :     initEvent->Dispatch();
     851             :   }
     852           0 : }
     853             : 
     854             : void
     855           0 : AudioCallbackDriver::RemoveCallback()
     856             : {
     857           0 :   if (mAddedMixer) {
     858           0 :     mGraphImpl->mMixer.RemoveCallback(this);
     859           0 :     mAddedMixer = false;
     860             :   }
     861           0 : }
     862             : 
     863             : void
     864           0 : AudioCallbackDriver::WaitForNextIteration()
     865             : {
     866           0 : }
     867             : 
     868             : void
     869           0 : AudioCallbackDriver::WakeUp()
     870             : {
     871           0 :   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
     872           0 :   mGraphImpl->GetMonitor().Notify();
     873           0 : }
     874             : 
     875             : /* static */ long
     876           0 : AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream,
     877             :                                     void* aUser,
     878             :                                     const void* aInputBuffer,
     879             :                                     void* aOutputBuffer,
     880             :                                     long aFrames)
     881             : {
     882           0 :   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
     883             :   return driver->DataCallback(static_cast<const AudioDataValue*>(aInputBuffer),
     884           0 :                               static_cast<AudioDataValue*>(aOutputBuffer), aFrames);
     885             : }
     886             : 
     887             : /* static */ void
     888           0 : AudioCallbackDriver::StateCallback_s(cubeb_stream* aStream, void * aUser,
     889             :                                      cubeb_state aState)
     890             : {
     891           0 :   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
     892           0 :   driver->StateCallback(aState);
     893           0 : }
     894             : 
     895             : /* static */ void
     896           0 : AudioCallbackDriver::DeviceChangedCallback_s(void* aUser)
     897             : {
     898           0 :   AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser);
     899           0 :   driver->DeviceChangedCallback();
     900           0 : }
     901             : 
     902           0 : bool AudioCallbackDriver::InCallback() {
     903           0 :   return mInCallback;
     904             : }
     905             : 
     906           0 : AudioCallbackDriver::AutoInCallback::AutoInCallback(AudioCallbackDriver* aDriver)
     907           0 :   : mDriver(aDriver)
     908             : {
     909           0 :   mDriver->mInCallback = true;
     910           0 : }
     911             : 
     912           0 : AudioCallbackDriver::AutoInCallback::~AutoInCallback() {
     913           0 :   mDriver->mInCallback = false;
     914           0 : }
     915             : 
     916             : long
     917           0 : AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
     918             :                                   AudioDataValue* aOutputBuffer, long aFrames)
     919             : {
     920             :   bool stillProcessing;
     921             : 
     922             :   // Don't add the callback until we're inited and ready
     923           0 :   if (!mAddedMixer) {
     924           0 :     mGraphImpl->mMixer.AddCallback(this);
     925           0 :     mAddedMixer = true;
     926             :   }
     927             : 
     928             : #ifdef DEBUG
     929             :   // DebugOnly<> doesn't work here... it forces an initialization that will cause
     930             :   // mInCallback to be set back to false before we exit the statement.  Do it by
     931             :   // hand instead.
     932           0 :   AutoInCallback aic(this);
     933             : #endif
     934             : 
     935           0 :   GraphTime stateComputedTime = StateComputedTime();
     936           0 :   if (stateComputedTime == 0) {
     937           0 :     MonitorAutoLock mon(mGraphImpl->GetMonitor());
     938             :     // Because this function is called during cubeb_stream_init (to prefill the
     939             :     // audio buffers), it can be that we don't have a message here (because this
     940             :     // driver is the first one for this graph), and the graph would exit. Simply
     941             :     // return here until we have messages.
     942           0 :     if (!mGraphImpl->MessagesQueued()) {
     943           0 :       PodZero(aOutputBuffer, aFrames * mGraphImpl->AudioChannelCount());
     944           0 :       return aFrames;
     945             :     }
     946           0 :     mGraphImpl->SwapMessageQueues();
     947             :   }
     948             : 
     949           0 :   uint32_t durationMS = aFrames * 1000 / mSampleRate;
     950             : 
     951             :   // For now, simply average the duration with the previous
     952             :   // duration so there is some damping against sudden changes.
     953           0 :   if (!mIterationDurationMS) {
     954           0 :     mIterationDurationMS = durationMS;
     955             :   } else {
     956           0 :     mIterationDurationMS = (mIterationDurationMS*3) + durationMS;
     957           0 :     mIterationDurationMS /= 4;
     958             :   }
     959             : 
     960             :   // Process mic data if any/needed
     961           0 :   if (aInputBuffer) {
     962           0 :     if (mAudioInput) { // for this specific input-only or full-duplex stream
     963           0 :       mAudioInput->NotifyInputData(mGraphImpl, aInputBuffer,
     964             :                                    static_cast<size_t>(aFrames),
     965           0 :                                    mSampleRate, mInputChannels);
     966             :     }
     967             :   }
     968             : 
     969           0 :   mBuffer.SetBuffer(aOutputBuffer, aFrames);
     970             :   // fill part or all with leftover data from last iteration (since we
     971             :   // align to Audio blocks)
     972           0 :   mScratchBuffer.Empty(mBuffer);
     973             :   // if we totally filled the buffer (and mScratchBuffer isn't empty),
     974             :   // we don't need to run an iteration and if we do so we may overflow.
     975           0 :   if (mBuffer.Available()) {
     976             : 
     977             :     // State computed time is decided by the audio callback's buffer length. We
     978             :     // compute the iteration start and end from there, trying to keep the amount
     979             :     // of buffering in the graph constant.
     980             :     GraphTime nextStateComputedTime =
     981           0 :       mGraphImpl->RoundUpToNextAudioBlock(stateComputedTime + mBuffer.Available());
     982             : 
     983           0 :     mIterationStart = mIterationEnd;
     984             :     // inGraph is the number of audio frames there is between the state time and
     985             :     // the current time, i.e. the maximum theoretical length of the interval we
     986             :     // could use as [mIterationStart; mIterationEnd].
     987           0 :     GraphTime inGraph = stateComputedTime - mIterationStart;
     988             :     // We want the interval [mIterationStart; mIterationEnd] to be before the
     989             :     // interval [stateComputedTime; nextStateComputedTime]. We also want
     990             :     // the distance between these intervals to be roughly equivalent each time, to
     991             :     // ensure there is no clock drift between current time and state time. Since
     992             :     // we can't act on the state time because we have to fill the audio buffer, we
     993             :     // reclock the current time against the state time, here.
     994           0 :     mIterationEnd = mIterationStart + 0.8 * inGraph;
     995             : 
     996           0 :     LOG(LogLevel::Verbose,
     997             :         ("interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) "
     998             :          "(duration ticks: %ld)",
     999             :          (long)mIterationStart,
    1000             :          (long)mIterationEnd,
    1001             :          (long)stateComputedTime,
    1002             :          (long)nextStateComputedTime,
    1003             :          (long)aFrames,
    1004             :          (uint32_t)durationMS,
    1005             :          (long)(nextStateComputedTime - stateComputedTime)));
    1006             : 
    1007           0 :     mCurrentTimeStamp = TimeStamp::Now();
    1008             : 
    1009           0 :     if (stateComputedTime < mIterationEnd) {
    1010           0 :       LOG(LogLevel::Warning, ("Media graph global underrun detected"));
    1011           0 :       mIterationEnd = stateComputedTime;
    1012             :     }
    1013             : 
    1014           0 :     stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime);
    1015             :   } else {
    1016           0 :     LOG(LogLevel::Verbose,
    1017             :         ("DataCallback buffer filled entirely from scratch "
    1018             :          "buffer, skipping iteration."));
    1019           0 :     stillProcessing = true;
    1020             :   }
    1021             : 
    1022           0 :   mBuffer.BufferFilled();
    1023             : 
    1024             :   // Callback any observers for the AEC speaker data.  Note that one
    1025             :   // (maybe) of these will be full-duplex, the others will get their input
    1026             :   // data off separate cubeb callbacks.  Take care with how stuff is
    1027             :   // removed/added to this list and TSAN issues, but input and output will
    1028             :   // use separate callback methods.
    1029           0 :   mGraphImpl->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
    1030           0 :                                mSampleRate, ChannelCount);
    1031             : 
    1032           0 :   bool switching = false;
    1033             :   {
    1034           0 :     MonitorAutoLock mon(mGraphImpl->GetMonitor());
    1035           0 :     switching = !!NextDriver();
    1036             :   }
    1037             : 
    1038           0 :   if (switching && stillProcessing) {
    1039             :     // If the audio stream has not been started by the previous driver or
    1040             :     // the graph itself, keep it alive.
    1041           0 :     MonitorAutoLock mon(mGraphImpl->GetMonitor());
    1042           0 :     if (!IsStarted()) {
    1043           0 :       return aFrames;
    1044             :     }
    1045           0 :     LOG(LogLevel::Debug, ("Switching to system driver."));
    1046           0 :     RemoveCallback();
    1047           0 :     NextDriver()->SetGraphTime(this, mIterationStart, mIterationEnd);
    1048           0 :     mGraphImpl->SetCurrentDriver(NextDriver());
    1049           0 :     NextDriver()->Start();
    1050             :     // Returning less than aFrames starts the draining and eventually stops the
    1051             :     // audio thread. This function will never get called again.
    1052           0 :     return aFrames - 1;
    1053             :   }
    1054             : 
    1055           0 :   if (!stillProcessing) {
    1056           0 :     LOG(LogLevel::Debug,
    1057             :         ("Stopping audio thread for MediaStreamGraph %p", this));
    1058           0 :     return aFrames - 1;
    1059             :   }
    1060           0 :   return aFrames;
    1061             : }
    1062             : 
    1063             : void
    1064           0 : AudioCallbackDriver::StateCallback(cubeb_state aState)
    1065             : {
    1066           0 :   LOG(LogLevel::Debug, ("AudioCallbackDriver State: %d", aState));
    1067           0 :   if (aState == CUBEB_STATE_ERROR) {
    1068             :     // Fall back to a driver using a normal thread. If needed,
    1069             :     // the graph will try to re-open an audio stream later.
    1070           0 :     MonitorAutoLock lock(GraphImpl()->GetMonitor());
    1071           0 :     SystemClockDriver* nextDriver = new SystemClockDriver(GraphImpl());
    1072           0 :     SetNextDriver(nextDriver);
    1073           0 :     RemoveCallback();
    1074           0 :     nextDriver->MarkAsFallback();
    1075           0 :     nextDriver->SetGraphTime(this, mIterationStart, mIterationEnd);
    1076             :     // We're not using SwitchAtNextIteration here, because there
    1077             :     // won't be a next iteration if we don't restart things manually:
    1078             :     // the audio stream just signaled that it's in error state.
    1079           0 :     mGraphImpl->SetCurrentDriver(nextDriver);
    1080           0 :     nextDriver->Start();
    1081             :   }
    1082           0 : }
    1083             : 
    1084             : void
    1085           0 : AudioCallbackDriver::MixerCallback(AudioDataValue* aMixedBuffer,
    1086             :                                    AudioSampleFormat aFormat,
    1087             :                                    uint32_t aChannels,
    1088             :                                    uint32_t aFrames,
    1089             :                                    uint32_t aSampleRate)
    1090             : {
    1091           0 :   uint32_t toWrite = mBuffer.Available();
    1092             : 
    1093           0 :   if (!mBuffer.Available()) {
    1094           0 :     NS_WARNING("DataCallback buffer full, expect frame drops.");
    1095             :   }
    1096             : 
    1097           0 :   MOZ_ASSERT(mBuffer.Available() <= aFrames);
    1098             : 
    1099           0 :   mBuffer.WriteFrames(aMixedBuffer, mBuffer.Available());
    1100           0 :   MOZ_ASSERT(mBuffer.Available() == 0, "Missing frames to fill audio callback's buffer.");
    1101             : 
    1102           0 :   DebugOnly<uint32_t> written = mScratchBuffer.Fill(aMixedBuffer + toWrite * aChannels, aFrames - toWrite);
    1103           0 :   NS_WARNING_ASSERTION(written == aFrames - toWrite, "Dropping frames.");
    1104           0 : };
    1105             : 
    1106           0 : void AudioCallbackDriver::PanOutputIfNeeded(bool aMicrophoneActive)
    1107             : {
    1108             : #ifdef XP_MACOSX
    1109             :   cubeb_device* out;
    1110             :   int rv;
    1111             :   char name[128];
    1112             :   size_t length = sizeof(name);
    1113             : 
    1114             :   rv = sysctlbyname("hw.model", name, &length, NULL, 0);
    1115             :   if (rv) {
    1116             :     return;
    1117             :   }
    1118             : 
    1119             :   if (!strncmp(name, "MacBookPro", 10)) {
    1120             :     if (cubeb_stream_get_current_device(mAudioStream, &out) == CUBEB_OK) {
    1121             :       // Check if we are currently outputing sound on external speakers.
    1122             :       if (!strcmp(out->output_name, "ispk")) {
    1123             :         // Pan everything to the right speaker.
    1124             :         if (aMicrophoneActive) {
    1125             :           if (cubeb_stream_set_panning(mAudioStream, 1.0) != CUBEB_OK) {
    1126             :             NS_WARNING("Could not pan audio output to the right.");
    1127             :           }
    1128             :         } else {
    1129             :           if (cubeb_stream_set_panning(mAudioStream, 0.0) != CUBEB_OK) {
    1130             :             NS_WARNING("Could not pan audio output to the center.");
    1131             :           }
    1132             :         }
    1133             :       } else {
    1134             :         if (cubeb_stream_set_panning(mAudioStream, 0.0) != CUBEB_OK) {
    1135             :           NS_WARNING("Could not pan audio output to the center.");
    1136             :         }
    1137             :       }
    1138             :       cubeb_stream_device_destroy(mAudioStream, out);
    1139             :     }
    1140             :   }
    1141             : #endif
    1142           0 : }
    1143             : 
    1144             : void
    1145           0 : AudioCallbackDriver::DeviceChangedCallback() {
    1146             :   // Tell the audio engine the device has changed, it might want to reset some
    1147             :   // state.
    1148           0 :   MonitorAutoLock mon(mGraphImpl->GetMonitor());
    1149           0 :   if (mAudioInput) {
    1150           0 :     mAudioInput->DeviceChanged();
    1151             :   }
    1152             : #ifdef XP_MACOSX
    1153             :   PanOutputIfNeeded(mMicrophoneActive);
    1154             : #endif
    1155           0 : }
    1156             : 
    1157             : void
    1158           0 : AudioCallbackDriver::SetMicrophoneActive(bool aActive)
    1159             : {
    1160           0 :   mMicrophoneActive = aActive;
    1161             : 
    1162             : #ifdef XP_MACOSX
    1163             :   PanOutputIfNeeded(mMicrophoneActive);
    1164             : #endif
    1165           0 : }
    1166             : 
    1167             : uint32_t
    1168           0 : AudioCallbackDriver::IterationDuration()
    1169             : {
    1170             :   // The real fix would be to have an API in cubeb to give us the number. Short
    1171             :   // of that, we approximate it here. bug 1019507
    1172           0 :   return mIterationDurationMS;
    1173             : }
    1174             : 
    1175             : bool
    1176           0 : AudioCallbackDriver::IsStarted() {
    1177           0 :   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
    1178           0 :   return mStarted;
    1179             : }
    1180             : 
    1181             : void
    1182           0 : AudioCallbackDriver::EnqueueStreamAndPromiseForOperation(MediaStream* aStream,
    1183             :                                           void* aPromise,
    1184             :                                           dom::AudioContextOperation aOperation)
    1185             : {
    1186           0 :   MonitorAutoLock mon(mGraphImpl->GetMonitor());
    1187           0 :   mPromisesForOperation.AppendElement(StreamAndPromiseForOperation(aStream,
    1188             :                                                                    aPromise,
    1189           0 :                                                                    aOperation));
    1190           0 : }
    1191             : 
    1192           0 : void AudioCallbackDriver::CompleteAudioContextOperations(AsyncCubebOperation aOperation)
    1193             : {
    1194           0 :   AutoTArray<StreamAndPromiseForOperation, 1> array;
    1195             : 
    1196             :   // We can't lock for the whole function because AudioContextOperationCompleted
    1197             :   // will grab the monitor
    1198             :   {
    1199           0 :     MonitorAutoLock mon(GraphImpl()->GetMonitor());
    1200           0 :     array.SwapElements(mPromisesForOperation);
    1201             :   }
    1202             : 
    1203           0 :   for (uint32_t i = 0; i < array.Length(); i++) {
    1204           0 :     StreamAndPromiseForOperation& s = array[i];
    1205           0 :     if ((aOperation == AsyncCubebOperation::INIT &&
    1206           0 :          s.mOperation == dom::AudioContextOperation::Resume) ||
    1207           0 :         (aOperation == AsyncCubebOperation::SHUTDOWN &&
    1208           0 :          s.mOperation != dom::AudioContextOperation::Resume)) {
    1209             : 
    1210           0 :       GraphImpl()->AudioContextOperationCompleted(s.mStream,
    1211             :                                                   s.mPromise,
    1212           0 :                                                   s.mOperation);
    1213           0 :       array.RemoveElementAt(i);
    1214           0 :       i--;
    1215             :     }
    1216             :   }
    1217             : 
    1218           0 :   if (!array.IsEmpty()) {
    1219           0 :     MonitorAutoLock mon(GraphImpl()->GetMonitor());
    1220           0 :     mPromisesForOperation.AppendElements(array);
    1221             :   }
    1222           0 : }
    1223             : 
    1224             : 
    1225             : } // namespace mozilla
    1226             : 
    1227             : // avoid redefined macro in unified build
    1228             : #undef LOG

Generated by: LCOV version 1.13