Source code

Revision control

Other Tools

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