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
|