Coverage Report

Created: 2018-09-25 14:53

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