GraphDriver.cpp (56769B)
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 "GraphDriver.h" 8 9 #include "AudioNodeEngine.h" 10 #include "CallbackThreadRegistry.h" 11 #include "CubebDeviceEnumerator.h" 12 #include "MediaTrackGraphImpl.h" 13 #include "Tracing.h" 14 #include "cubeb/cubeb.h" 15 #include "mozilla/ClearOnShutdown.h" 16 #include "mozilla/MathAlgorithms.h" 17 #include "mozilla/SchedulerGroup.h" 18 #include "mozilla/SharedThreadPool.h" 19 #include "mozilla/StaticPrefs_media.h" 20 #include "mozilla/dom/AudioContext.h" 21 #include "mozilla/dom/AudioDeviceInfo.h" 22 #include "mozilla/dom/BaseAudioContextBinding.h" 23 24 #ifdef MOZ_WEBRTC 25 # include "webrtc/MediaEngineWebRTC.h" 26 #endif 27 28 #ifdef XP_MACOSX 29 # include <sys/sysctl.h> 30 31 # include "nsCocoaFeatures.h" 32 #endif 33 34 extern mozilla::LazyLogModule gMediaTrackGraphLog; 35 #ifdef LOG 36 # undef LOG 37 #endif // LOG 38 #define LOG(type, msg) MOZ_LOG(gMediaTrackGraphLog, type, msg) 39 40 namespace mozilla { 41 42 GraphDriver::GraphDriver(GraphInterface* aGraphInterface, 43 GraphDriver* aPreviousDriver, uint32_t aSampleRate) 44 : mGraphInterface(aGraphInterface), 45 mSampleRate(aSampleRate), 46 mPreviousDriver(aPreviousDriver) {} 47 48 void GraphDriver::SetStreamName(const nsACString& aStreamName) { 49 MOZ_ASSERT(InIteration() || (!ThreadRunning() && NS_IsMainThread())); 50 mStreamName = aStreamName; 51 LOG(LogLevel::Debug, ("%p: GraphDriver::SetStreamName driver=%p %s", Graph(), 52 this, mStreamName.get())); 53 } 54 55 void GraphDriver::SetState(const nsACString& aStreamName, 56 GraphTime aStateComputedTime, 57 TimeStamp aIterationTimeStamp) { 58 MOZ_ASSERT(InIteration() || !ThreadRunning()); 59 60 mStreamName = aStreamName; 61 mStateComputedTime = aStateComputedTime; 62 mTargetIterationTimeStamp = aIterationTimeStamp; 63 } 64 65 #ifdef DEBUG 66 bool GraphDriver::InIteration() const { 67 return OnThread() || Graph()->InDriverIteration(this); 68 } 69 #endif 70 71 GraphDriver* GraphDriver::PreviousDriver() { 72 MOZ_ASSERT(InIteration() || !ThreadRunning()); 73 return mPreviousDriver; 74 } 75 76 void GraphDriver::SetPreviousDriver(GraphDriver* aPreviousDriver) { 77 MOZ_ASSERT(InIteration() || !ThreadRunning()); 78 mPreviousDriver = aPreviousDriver; 79 } 80 81 ThreadedDriver::ThreadedDriver(GraphInterface* aGraphInterface, 82 GraphDriver* aPreviousDriver, 83 uint32_t aSampleRate) 84 : GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate), 85 mThreadRunning(false) {} 86 87 class MediaTrackGraphShutdownThreadRunnable : public Runnable { 88 public: 89 explicit MediaTrackGraphShutdownThreadRunnable( 90 already_AddRefed<nsIThread> aThread) 91 : Runnable("MediaTrackGraphShutdownThreadRunnable"), mThread(aThread) {} 92 NS_IMETHOD Run() override { 93 TRACE("MediaTrackGraphShutdownThreadRunnable"); 94 MOZ_ASSERT(NS_IsMainThread()); 95 MOZ_ASSERT(mThread); 96 97 mThread->AsyncShutdown(); 98 mThread = nullptr; 99 return NS_OK; 100 } 101 102 private: 103 nsCOMPtr<nsIThread> mThread; 104 }; 105 106 ThreadedDriver::~ThreadedDriver() { 107 if (mThread) { 108 nsCOMPtr<nsIRunnable> event = 109 new MediaTrackGraphShutdownThreadRunnable(mThread.forget()); 110 SchedulerGroup::Dispatch(event.forget()); 111 } 112 } 113 114 class MediaTrackGraphInitThreadRunnable : public Runnable { 115 public: 116 explicit MediaTrackGraphInitThreadRunnable(ThreadedDriver* aDriver) 117 : Runnable("MediaTrackGraphInitThreadRunnable"), mDriver(aDriver) {} 118 NS_IMETHOD Run() override { 119 TRACE("MediaTrackGraphInitThreadRunnable"); 120 MOZ_ASSERT(!mDriver->ThreadRunning()); 121 LOG(LogLevel::Debug, ("Starting a new system driver for graph %p", 122 mDriver->mGraphInterface.get())); 123 124 if (GraphDriver* previousDriver = mDriver->PreviousDriver()) { 125 LOG(LogLevel::Debug, 126 ("%p releasing an AudioCallbackDriver(%p), for graph %p", 127 mDriver.get(), previousDriver, mDriver->Graph())); 128 MOZ_ASSERT(!mDriver->AsAudioCallbackDriver()); 129 AudioCallbackDriver* audioCallbackDriver = 130 previousDriver->AsAudioCallbackDriver(); 131 MOZ_ALWAYS_SUCCEEDS(audioCallbackDriver->mCubebOperationThread->Dispatch( 132 NS_NewRunnableFunction( 133 "ThreadedDriver previousDriver::Stop()", 134 [audioCallbackDriver = RefPtr{audioCallbackDriver}] { 135 audioCallbackDriver->Stop(); 136 }))); 137 mDriver->SetPreviousDriver(nullptr); 138 } 139 140 mDriver->RunThread(); 141 return NS_OK; 142 } 143 144 private: 145 RefPtr<ThreadedDriver> mDriver; 146 }; 147 148 void ThreadedDriver::Start() { 149 MOZ_ASSERT(!ThreadRunning()); 150 LOG(LogLevel::Debug, 151 ("Starting thread for a SystemClockDriver %p", mGraphInterface.get())); 152 (void)NS_WARN_IF(mThread); 153 MOZ_ASSERT(!mThread); // Ensure we haven't already started it 154 155 nsCOMPtr<nsIRunnable> event = new MediaTrackGraphInitThreadRunnable(this); 156 // Note: mThread may be null during event->Run() if we pass to NewNamedThread! 157 // See AudioInitTask 158 nsresult rv = NS_NewNamedThread("MediaTrackGrph", getter_AddRefs(mThread)); 159 if (NS_SUCCEEDED(rv)) { 160 mThread->Dispatch(event.forget(), NS_DISPATCH_NORMAL); 161 } 162 } 163 164 void ThreadedDriver::Shutdown() { 165 NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread"); 166 // mGraph's thread is not running so it's OK to do whatever here 167 LOG(LogLevel::Debug, ("Stopping threads for MediaTrackGraph %p", this)); 168 169 if (mThread) { 170 LOG(LogLevel::Debug, 171 ("%p: Stopping ThreadedDriver's %p thread", Graph(), this)); 172 mThread->AsyncShutdown(); 173 mThread = nullptr; 174 } 175 } 176 177 SystemClockDriver::SystemClockDriver(GraphInterface* aGraphInterface, 178 GraphDriver* aPreviousDriver, 179 uint32_t aSampleRate) 180 : ThreadedDriver(aGraphInterface, aPreviousDriver, aSampleRate), 181 mInitialTimeStamp(TimeStamp::Now()) {} 182 183 SystemClockDriver::~SystemClockDriver() = default; 184 185 void ThreadedDriver::RunThread() { 186 mThreadRunning = true; 187 while (true) { 188 WaitForNextIteration(); 189 190 MediaTime interval = GetIntervalForIteration(); 191 GraphTime nextStateComputedTime = mStateComputedTime + interval; 192 LOG(LogLevel::Verbose, 193 ("%p: interval[%ld; %ld]", Graph(), (long)mStateComputedTime, 194 (long)nextStateComputedTime)); 195 196 mStateComputedTime = nextStateComputedTime; 197 IterationResult result = Graph()->OneIteration(mStateComputedTime, nullptr); 198 199 if (result.IsStop()) { 200 // Signal that we're done stopping. 201 result.Stopped(); 202 break; 203 } 204 if (GraphDriver* nextDriver = result.NextDriver()) { 205 LOG(LogLevel::Debug, ("%p: Switching to AudioCallbackDriver", Graph())); 206 result.Switched(); 207 nextDriver->SetState(mStreamName, mStateComputedTime, 208 mTargetIterationTimeStamp); 209 nextDriver->Start(); 210 break; 211 } 212 MOZ_ASSERT(result.IsStillProcessing()); 213 } 214 mThreadRunning = false; 215 } 216 217 MediaTime SystemClockDriver::GetIntervalForIteration() { 218 return MediaTrackGraphImpl::RoundUpToEndOfAudioBlock( 219 MillisecondsToMediaTime(MEDIA_GRAPH_TARGET_PERIOD_MS)); 220 } 221 222 void ThreadedDriver::EnsureNextIteration() { 223 mWaitHelper.EnsureNextIteration(); 224 } 225 226 void ThreadedDriver::WaitForNextIteration() { 227 MOZ_ASSERT(mThread); 228 MOZ_ASSERT(OnThread()); 229 mWaitHelper.WaitForNextIterationAtLeast(NextIterationWaitDuration()); 230 } 231 232 TimeDuration ThreadedDriver::IterationDuration() { 233 return MediaTimeToTimeDuration(GetIntervalForIteration()); 234 } 235 236 TimeDuration SystemClockDriver::NextIterationWaitDuration() { 237 MOZ_ASSERT(mThread); 238 MOZ_ASSERT(OnThread()); 239 TimeStamp now = TimeStamp::Now(); 240 if (mTargetIterationTimeStamp.IsNull()) { 241 // No previous driver with which to synchronize rendering. 242 // Start rendering now. 243 mTargetIterationTimeStamp = now; 244 } else { 245 mTargetIterationTimeStamp += IterationDuration(); 246 } 247 TimeDuration timeout = mTargetIterationTimeStamp - now; 248 if (timeout < 249 TimeDuration::FromMilliseconds(-SYSTEM_CLOCK_BANKRUPTCY_THRESHOLD_MS)) { 250 // Don't try to catch up because rendering has fallen so far behind. 251 // Instead try to render at consistent time intervals from now. 252 LOG(LogLevel::Warning, ("%p: Global underrun detected", Graph())); 253 mTargetIterationTimeStamp = now; 254 } 255 256 LOG(LogLevel::Verbose, 257 ("%p: Waiting for next iteration; at %f (real %f), timeout=%f", Graph(), 258 MediaTimeToSeconds(mStateComputedTime), 259 (now - mInitialTimeStamp).ToSeconds(), timeout.ToSeconds())); 260 return timeout; 261 } 262 263 OfflineClockDriver::OfflineClockDriver(GraphInterface* aGraphInterface, 264 uint32_t aSampleRate) 265 : ThreadedDriver(aGraphInterface, nullptr, aSampleRate) {} 266 267 OfflineClockDriver::~OfflineClockDriver() = default; 268 269 void OfflineClockDriver::RunThread() { 270 nsCOMPtr<nsIThreadInternal> threadInternal = do_QueryInterface(mThread); 271 nsCOMPtr<nsIThreadObserver> observer = do_QueryInterface(Graph()); 272 threadInternal->SetObserver(observer); 273 274 ThreadedDriver::RunThread(); 275 } 276 277 MediaTime OfflineClockDriver::GetIntervalForIteration() { 278 return MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(std::clamp<MediaTime>( 279 mEndTime - mStateComputedTime, 0, 280 MillisecondsToMediaTime(MEDIA_GRAPH_TARGET_PERIOD_MS))); 281 } 282 283 /* Helper to proxy the GraphInterface methods used by a running 284 * mFallbackDriver. */ 285 class AudioCallbackDriver::FallbackWrapper : public GraphInterface { 286 public: 287 FallbackWrapper(RefPtr<GraphInterface> aGraph, 288 RefPtr<AudioCallbackDriver> aOwner, uint32_t aSampleRate, 289 const nsACString& aStreamName, GraphTime aStateComputedTime, 290 TimeStamp aIterationTimeStamp) 291 : mGraph(std::move(aGraph)), 292 mOwner(std::move(aOwner)), 293 mFallbackDriver( 294 MakeRefPtr<SystemClockDriver>(this, nullptr, aSampleRate)) { 295 mFallbackDriver->SetState(aStreamName, aStateComputedTime, 296 aIterationTimeStamp); 297 } 298 299 NS_DECL_THREADSAFE_ISUPPORTS 300 301 /* Proxied SystemClockDriver methods */ 302 void Start() { mFallbackDriver->Start(); } 303 MOZ_CAN_RUN_SCRIPT void Shutdown() { 304 RefPtr<SystemClockDriver> driver = mFallbackDriver; 305 driver->Shutdown(); 306 } 307 void SetStreamName(const nsACString& aStreamName) { 308 mFallbackDriver->SetStreamName(aStreamName); 309 } 310 void EnsureNextIteration() { mFallbackDriver->EnsureNextIteration(); } 311 #ifdef DEBUG 312 bool InIteration() { return mFallbackDriver->InIteration(); } 313 #endif 314 bool OnThread() { return mFallbackDriver->OnThread(); } 315 316 /* GraphInterface methods */ 317 void NotifyInputStopped() override { 318 MOZ_CRASH("Unexpected NotifyInputStopped from fallback SystemClockDriver"); 319 } 320 void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames, 321 TrackRate aRate, uint32_t aChannels, 322 uint32_t aAlreadyBuffered) override { 323 MOZ_CRASH("Unexpected NotifyInputData from fallback SystemClockDriver"); 324 } 325 void NotifySetRequestedInputProcessingParamsResult( 326 AudioCallbackDriver* aDriver, int aGeneration, 327 Result<cubeb_input_processing_params, int>&& aResult) override { 328 MOZ_CRASH( 329 "Unexpected processing params result from fallback SystemClockDriver"); 330 } 331 void DeviceChanged() override { 332 MOZ_CRASH("Unexpected DeviceChanged from fallback SystemClockDriver"); 333 } 334 #ifdef DEBUG 335 bool InDriverIteration(const GraphDriver* aDriver) const override { 336 return mGraph->InDriverIteration(mOwner) && mOwner->OnFallback(); 337 } 338 #endif 339 IterationResult OneIteration(GraphTime aStateComputedEnd, 340 MixerCallbackReceiver* aMixerReceiver) override { 341 MOZ_ASSERT(!aMixerReceiver); 342 343 #ifdef DEBUG 344 AutoInCallback aic(mOwner); 345 #endif 346 347 IterationResult result = 348 mGraph->OneIteration(aStateComputedEnd, aMixerReceiver); 349 350 AudioStreamState audioState = mOwner->mAudioStreamState; 351 352 MOZ_ASSERT(audioState != AudioStreamState::Stopping, 353 "The audio driver can only enter stopping if it iterated the " 354 "graph, which it can only do if there's no fallback driver"); 355 356 // After a devicechange event from the audio driver, wait for a five 357 // millisecond grace period before handing control to the audio driver. We 358 // do this because cubeb leaves no guarantee on audio callbacks coming in 359 // after a device change event. 360 if (audioState == AudioStreamState::ChangingDevice && 361 mOwner->mChangingDeviceStartTime + TimeDuration::FromMilliseconds(5) < 362 TimeStamp::Now()) { 363 mOwner->mChangingDeviceStartTime = TimeStamp(); 364 if (mOwner->mAudioStreamState.compareExchange( 365 AudioStreamState::ChangingDevice, AudioStreamState::Starting)) { 366 audioState = AudioStreamState::Starting; 367 LOG(LogLevel::Debug, ("%p: Fallback driver has started. Waiting for " 368 "audio driver to start.", 369 mOwner.get())); 370 } 371 } 372 373 if (audioState != AudioStreamState::Running && result.IsStillProcessing()) { 374 mOwner->MaybeStartAudioStream(); 375 return result; 376 } 377 378 MOZ_ASSERT(result.IsStillProcessing() || result.IsStop() || 379 result.IsSwitchDriver()); 380 381 IterationResult stopFallback = 382 IterationResult::CreateStop(NS_NewRunnableFunction( 383 "AudioCallbackDriver::FallbackDriverStopped", 384 [self = RefPtr<FallbackWrapper>(this), this, aStateComputedEnd, 385 result = std::move(result)]() mutable { 386 FallbackDriverState fallbackState = 387 result.IsStillProcessing() ? FallbackDriverState::None 388 : FallbackDriverState::Stopped; 389 mOwner->FallbackDriverStopped( 390 aStateComputedEnd, mFallbackDriver->IterationTimeStamp(), 391 fallbackState); 392 393 if (fallbackState == FallbackDriverState::Stopped) { 394 #ifdef DEBUG 395 // The AudioCallbackDriver may not iterate the graph, but we'll 396 // call into it so we need to be regarded as "in iteration". 397 AutoInCallback aic(mOwner); 398 #endif 399 if (GraphDriver* nextDriver = result.NextDriver()) { 400 LOG(LogLevel::Debug, 401 ("%p: Switching from fallback to other driver.", 402 mOwner.get())); 403 result.Switched(); 404 nextDriver->SetState(mOwner->mStreamName, aStateComputedEnd, 405 mFallbackDriver->IterationTimeStamp()); 406 nextDriver->Start(); 407 } else if (result.IsStop()) { 408 LOG(LogLevel::Debug, 409 ("%p: Stopping fallback driver.", mOwner.get())); 410 result.Stopped(); 411 } 412 } 413 mOwner = nullptr; 414 // Proxy the release of the fallback driver to a background 415 // thread, so it doesn't perform unexpected suicide. 416 NS_DispatchBackgroundTask(NS_NewRunnableFunction( 417 "AudioCallbackDriver::FallbackDriverStopped::Release", 418 [fallback = std::move(self->mFallbackDriver)] {})); 419 })); 420 421 return stopFallback; 422 } 423 424 private: 425 virtual ~FallbackWrapper() = default; 426 427 const RefPtr<GraphInterface> mGraph; 428 // Valid until mFallbackDriver has finished its last iteration. 429 RefPtr<AudioCallbackDriver> mOwner; 430 RefPtr<SystemClockDriver> mFallbackDriver; 431 }; 432 433 NS_IMPL_ISUPPORTS0(AudioCallbackDriver::FallbackWrapper) 434 435 /* static */ 436 already_AddRefed<TaskQueue> AudioCallbackDriver::CreateTaskQueue() { 437 return TaskQueue::Create(CubebUtils::GetCubebOperationThread(), 438 "AudioCallbackDriver cubeb task queue") 439 .forget(); 440 } 441 442 AudioCallbackDriver::AudioCallbackDriver( 443 GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver, 444 uint32_t aSampleRate, uint32_t aOutputChannelCount, 445 uint32_t aInputChannelCount, CubebUtils::AudioDeviceID aOutputDeviceID, 446 CubebUtils::AudioDeviceID aInputDeviceID, AudioInputType aAudioInputType, 447 Maybe<AudioInputProcessingParamsRequest> aRequestedInputProcessingParams) 448 : GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate), 449 mOutputChannelCount(aOutputChannelCount), 450 mInputChannelCount(aInputChannelCount), 451 mOutputDeviceID(aOutputDeviceID), 452 mInputDeviceID(aInputDeviceID), 453 mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS), 454 mCubebOperationThread(CreateTaskQueue()), 455 mInputProcessingRequest(aRequestedInputProcessingParams.valueOr( 456 AudioInputProcessingParamsRequest{})), 457 mAudioThreadId(ProfilerThreadId{}), 458 mAudioThreadIdInCb(std::thread::id()), 459 mFallback("AudioCallbackDriver::mFallback"), 460 mSandboxed(CubebUtils::SandboxEnabled()) { 461 LOG(LogLevel::Debug, ("%p: AudioCallbackDriver %p ctor - input: device %p, " 462 "channel %d, output: device %p, channel %d", 463 Graph(), this, mInputDeviceID, mInputChannelCount, 464 mOutputDeviceID, mOutputChannelCount)); 465 466 NS_WARNING_ASSERTION(mOutputChannelCount != 0, 467 "Invalid output channel count"); 468 469 if (aAudioInputType == AudioInputType::Voice && 470 StaticPrefs:: 471 media_getusermedia_microphone_prefer_voice_stream_with_processing_enabled()) { 472 LOG(LogLevel::Debug, 473 ("%p: AudioCallbackDriver %p ctor - using VOICE and requesting input " 474 "processing params %s (Gen %d).", 475 Graph(), this, 476 CubebUtils::ProcessingParamsToString(mInputProcessingRequest.mParams) 477 .get(), 478 mInputProcessingRequest.mGeneration)); 479 mInputDevicePreference = CUBEB_DEVICE_PREF_VOICE; 480 CubebUtils::SetInCommunication(true); 481 } else { 482 mInputDevicePreference = CUBEB_DEVICE_PREF_ALL; 483 } 484 } 485 486 AudioCallbackDriver::~AudioCallbackDriver() { 487 if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) { 488 CubebUtils::SetInCommunication(false); 489 } 490 } 491 492 bool IsMacbookOrMacbookAir() { 493 #ifdef XP_MACOSX 494 size_t len = 0; 495 sysctlbyname("hw.model", NULL, &len, NULL, 0); 496 if (len) { 497 UniquePtr<char[]> model(new char[len]); 498 // This string can be 499 // MacBook%d,%d for a normal MacBook 500 // MacBookAir%d,%d for a Macbook Air 501 sysctlbyname("hw.model", model.get(), &len, NULL, 0); 502 char* substring = strstr(model.get(), "MacBook"); 503 if (substring) { 504 const size_t offset = strlen("MacBook"); 505 if (!strncmp(model.get() + offset, "Air", 3) || 506 isdigit(model[offset + 1])) { 507 return true; 508 } 509 } 510 } 511 #endif 512 return false; 513 } 514 515 void AudioCallbackDriver::Init(const nsCString& aStreamName) { 516 LOG(LogLevel::Debug, 517 ("%p: AudioCallbackDriver::Init driver=%p", Graph(), this)); 518 TRACE("AudioCallbackDriver::Init"); 519 MOZ_ASSERT(OnCubebOperationThread()); 520 MOZ_ASSERT(mAudioStreamState == AudioStreamState::Pending); 521 if (mFallbackDriverState == FallbackDriverState::Stopped) { 522 // The graph has already stopped us. 523 return; 524 } 525 RefPtr<CubebUtils::CubebHandle> handle = CubebUtils::GetCubeb(); 526 if (!handle) { 527 NS_WARNING("Could not get cubeb context."); 528 LOG(LogLevel::Warning, ("%s: Could not get cubeb context", __func__)); 529 mAudioStreamState = AudioStreamState::None; 530 if (TryStartingFallbackDriver().isOk()) { 531 CubebUtils::ReportCubebStreamInitFailure(true); 532 } 533 return; 534 } 535 536 cubeb_stream_params output; 537 cubeb_stream_params input; 538 bool firstStream = CubebUtils::GetFirstStream(); 539 540 MOZ_ASSERT(!NS_IsMainThread(), 541 "This is blocking and should never run on the main thread."); 542 543 output.rate = mSampleRate; 544 output.format = CUBEB_SAMPLE_FLOAT32NE; 545 546 if (!mOutputChannelCount) { 547 LOG(LogLevel::Warning, ("Output number of channels is 0.")); 548 mAudioStreamState = AudioStreamState::None; 549 if (TryStartingFallbackDriver().isOk()) { 550 CubebUtils::ReportCubebStreamInitFailure(firstStream); 551 } 552 return; 553 } 554 555 CubebUtils::AudioDeviceID forcedOutputDeviceId = nullptr; 556 557 char* forcedOutputDeviceName = CubebUtils::GetForcedOutputDevice(); 558 if (forcedOutputDeviceName) { 559 RefPtr<CubebDeviceEnumerator> enumerator = Enumerator::GetInstance(); 560 RefPtr<AudioDeviceInfo> device = enumerator->DeviceInfoFromName( 561 NS_ConvertUTF8toUTF16(forcedOutputDeviceName), EnumeratorSide::OUTPUT); 562 if (device && device->DeviceID()) { 563 forcedOutputDeviceId = device->DeviceID(); 564 } 565 } 566 567 mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannelCount); 568 mScratchBuffer = 569 SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2>(mOutputChannelCount); 570 571 output.channels = mOutputChannelCount; 572 AudioConfig::ChannelLayout::ChannelMap channelMap = 573 AudioConfig::ChannelLayout(mOutputChannelCount).Map(); 574 575 output.layout = static_cast<uint32_t>(channelMap); 576 output.prefs = CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_OUTPUT); 577 if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE && 578 CubebUtils::RouteOutputAsVoice()) { 579 output.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE); 580 } 581 output.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE; 582 583 uint32_t latencyFrames = CubebUtils::GetCubebMTGLatencyInFrames(&output); 584 585 LOG(LogLevel::Debug, ("Minimum latency in frames: %d", latencyFrames)); 586 587 // Macbook and MacBook air don't have enough CPU to run very low latency 588 // MediaTrackGraphs, cap the minimal latency to 512 frames int this case. 589 if (IsMacbookOrMacbookAir()) { 590 latencyFrames = std::max((uint32_t)512, latencyFrames); 591 LOG(LogLevel::Debug, 592 ("Macbook or macbook air, new latency: %d", latencyFrames)); 593 } 594 595 // Buffer sizes lower than 10ms are nowadays common. It's not very useful 596 // when doing voice, because all the WebRTC code that does audio input 597 // processing deals in 10ms chunks of audio. Take the first power of two 598 // above 10ms at the current rate in this case. It's probably 512, for common 599 // rates. 600 if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) { 601 if (latencyFrames < mSampleRate / 100) { 602 latencyFrames = mozilla::RoundUpPow2(mSampleRate / 100); 603 LOG(LogLevel::Debug, 604 ("AudioProcessing enabled, new latency %d", latencyFrames)); 605 } 606 } 607 608 // It's not useful for the graph to run with a block size lower than the Web 609 // Audio API block size, but increasingly devices report that they can do 610 // audio latencies lower than that. 611 if (latencyFrames < WEBAUDIO_BLOCK_SIZE) { 612 LOG(LogLevel::Debug, 613 ("Latency clamped to %d from %d", WEBAUDIO_BLOCK_SIZE, latencyFrames)); 614 latencyFrames = WEBAUDIO_BLOCK_SIZE; 615 } 616 LOG(LogLevel::Debug, ("Effective latency in frames: %d", latencyFrames)); 617 618 input = output; 619 input.channels = mInputChannelCount; 620 input.layout = CUBEB_LAYOUT_UNDEFINED; 621 input.prefs = CubebUtils::GetDefaultStreamPrefs(CUBEB_DEVICE_TYPE_INPUT); 622 if (mInputDevicePreference == CUBEB_DEVICE_PREF_VOICE) { 623 input.prefs |= static_cast<cubeb_stream_prefs>(CUBEB_STREAM_PREF_VOICE); 624 } 625 input.input_params = CUBEB_INPUT_PROCESSING_PARAM_NONE; 626 627 cubeb_stream* stream = nullptr; 628 const char* streamName = 629 aStreamName.IsEmpty() ? "AudioCallbackDriver" : aStreamName.get(); 630 bool inputWanted = mInputChannelCount > 0; 631 CubebUtils::AudioDeviceID outputId = mOutputDeviceID; 632 CubebUtils::AudioDeviceID inputId = mInputDeviceID; 633 634 if (CubebUtils::CubebStreamInit( 635 handle->Context(), &stream, streamName, inputId, 636 inputWanted ? &input : nullptr, 637 forcedOutputDeviceId ? forcedOutputDeviceId : outputId, &output, 638 latencyFrames, DataCallback_s, StateCallback_s, this) == CUBEB_OK) { 639 mCubeb = handle; 640 mAudioStream.own(stream); 641 DebugOnly<int> rv = 642 cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale()); 643 NS_WARNING_ASSERTION( 644 rv == CUBEB_OK, 645 "Could not set the audio stream volume in GraphDriver.cpp"); 646 CubebUtils::ReportCubebBackendUsed(); 647 } else { 648 NS_WARNING( 649 "Could not create a cubeb stream for MediaTrackGraph, falling " 650 "back to a SystemClockDriver"); 651 mAudioStreamState = AudioStreamState::None; 652 // Only report failures when we're not coming from a driver that was 653 // created itself as a fallback driver because of a previous audio driver 654 // failure. 655 if (TryStartingFallbackDriver().isOk()) { 656 CubebUtils::ReportCubebStreamInitFailure(firstStream); 657 } 658 return; 659 } 660 661 #ifdef XP_MACOSX 662 PanOutputIfNeeded(inputWanted); 663 #endif 664 665 if (inputWanted && InputDevicePreference() == AudioInputType::Voice) { 666 SetInputProcessingParams(mInputProcessingRequest); 667 } 668 669 cubeb_stream_register_device_changed_callback( 670 mAudioStream, AudioCallbackDriver::DeviceChangedCallback_s); 671 672 // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable. This 673 // is intended for diagnosing issues, and only works if the content sandbox is 674 // disabled. 675 mInputStreamFile.Open("GraphDriverInput", input.channels, input.rate); 676 mOutputStreamFile.Open("GraphDriverOutput", output.channels, output.rate); 677 678 if (NS_WARN_IF(!StartStream())) { 679 LOG(LogLevel::Warning, 680 ("%p: AudioCallbackDriver couldn't start a cubeb stream.", Graph())); 681 return; 682 } 683 684 LOG(LogLevel::Debug, ("%p: AudioCallbackDriver started.", Graph())); 685 } 686 687 void AudioCallbackDriver::SetCubebStreamName(const nsCString& aStreamName) { 688 MOZ_ASSERT(OnCubebOperationThread()); 689 MOZ_ASSERT(mAudioStream); 690 cubeb_stream_set_name(mAudioStream, aStreamName.get()); 691 } 692 693 void AudioCallbackDriver::Start() { 694 MOZ_ASSERT(!IsStarted()); 695 MOZ_ASSERT(mAudioStreamState == AudioStreamState::None); 696 MOZ_ASSERT_IF(PreviousDriver(), PreviousDriver()->InIteration()); 697 mAudioStreamState = AudioStreamState::Pending; 698 699 // Starting an audio driver could take a while. We start a system driver in 700 // the meantime so that the graph is kept running. 701 (void)TryStartingFallbackDriver(); 702 703 if (mPreviousDriver) { 704 if (AudioCallbackDriver* previousAudioCallback = 705 mPreviousDriver->AsAudioCallbackDriver()) { 706 LOG(LogLevel::Debug, ("Releasing audio driver off main thread.")); 707 MOZ_ALWAYS_SUCCEEDS( 708 previousAudioCallback->mCubebOperationThread->Dispatch( 709 NS_NewRunnableFunction( 710 "AudioCallbackDriver previousDriver::Stop()", 711 [previousDriver = RefPtr{previousAudioCallback}] { 712 previousDriver->Stop(); 713 }))); 714 } else { 715 LOG(LogLevel::Debug, 716 ("Dropping driver reference for SystemClockDriver.")); 717 MOZ_ASSERT(mPreviousDriver->AsSystemClockDriver()); 718 } 719 mPreviousDriver = nullptr; 720 } 721 722 LOG(LogLevel::Debug, ("Starting new audio driver off main thread, " 723 "to ensure it runs after previous shutdown.")); 724 MOZ_ALWAYS_SUCCEEDS(mCubebOperationThread->Dispatch( 725 NS_NewRunnableFunction("AudioCallbackDriver Init()", 726 [self = RefPtr{this}, streamName = mStreamName] { 727 self->Init(streamName); 728 }))); 729 } 730 731 bool AudioCallbackDriver::StartStream() { 732 TRACE("AudioCallbackDriver::StartStream"); 733 MOZ_ASSERT(!IsStarted() && OnCubebOperationThread()); 734 // Set STARTING before cubeb_stream_start, since starting the cubeb stream 735 // can result in a callback (that may read mAudioStreamState) before 736 // mAudioStreamState would otherwise be set. 737 mAudioStreamState = AudioStreamState::Starting; 738 if (cubeb_stream_start(mAudioStream) != CUBEB_OK) { 739 NS_WARNING("Could not start cubeb stream for MTG."); 740 return false; 741 } 742 743 return true; 744 } 745 746 void AudioCallbackDriver::Stop() { 747 LOG(LogLevel::Debug, 748 ("%p: AudioCallbackDriver::Stop driver=%p", Graph(), this)); 749 TRACE("AudioCallbackDriver::Stop"); 750 MOZ_ASSERT(OnCubebOperationThread()); 751 cubeb_stream_register_device_changed_callback(mAudioStream, nullptr); 752 if (cubeb_stream_stop(mAudioStream) != CUBEB_OK) { 753 NS_WARNING("Could not stop cubeb stream for MTG."); 754 } else { 755 mAudioStreamState = AudioStreamState::None; 756 } 757 } 758 759 void AudioCallbackDriver::Shutdown() { 760 MOZ_ASSERT(NS_IsMainThread()); 761 RefPtr<FallbackWrapper> fallback; 762 { 763 auto fallbackLock = mFallback.Lock(); 764 fallback = fallbackLock.ref(); 765 fallbackLock.ref() = nullptr; 766 } 767 if (fallback) { 768 LOG(LogLevel::Debug, 769 ("%p: Releasing fallback driver %p.", Graph(), fallback.get())); 770 fallback->Shutdown(); 771 } 772 773 LOG(LogLevel::Debug, 774 ("%p: Releasing audio driver off main thread (GraphDriver::Shutdown).", 775 Graph())); 776 777 nsLiteralCString reason("AudioCallbackDriver::Shutdown"); 778 NS_DispatchAndSpinEventLoopUntilComplete( 779 reason, mCubebOperationThread, 780 NS_NewRunnableFunction(reason.get(), 781 [self = RefPtr{this}] { self->Stop(); })); 782 } 783 784 void AudioCallbackDriver::SetStreamName(const nsACString& aStreamName) { 785 MOZ_ASSERT(InIteration() || !ThreadRunning()); 786 if (aStreamName == mStreamName) { 787 return; 788 } 789 // Record the stream name, which will be passed onto the next driver, if 790 // any, either from this driver or the fallback driver. 791 GraphDriver::SetStreamName(aStreamName); 792 { 793 auto fallbackLock = mFallback.Lock(); 794 FallbackWrapper* fallback = fallbackLock.ref().get(); 795 if (fallback) { 796 MOZ_ASSERT(fallback->InIteration()); 797 fallback->SetStreamName(aStreamName); 798 } 799 } 800 AudioStreamState streamState = mAudioStreamState; 801 if (streamState != AudioStreamState::None && 802 streamState != AudioStreamState::Stopping) { 803 MOZ_ALWAYS_SUCCEEDS(mCubebOperationThread->Dispatch( 804 NS_NewRunnableFunction("AudioCallbackDriver SetStreamName()", 805 [self = RefPtr{this}, streamName = mStreamName] { 806 self->SetCubebStreamName(streamName); 807 }))); 808 } 809 } 810 811 /* static */ 812 long AudioCallbackDriver::DataCallback_s(cubeb_stream* aStream, void* aUser, 813 const void* aInputBuffer, 814 void* aOutputBuffer, long aFrames) { 815 AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser); 816 return driver->DataCallback(static_cast<const AudioDataValue*>(aInputBuffer), 817 static_cast<AudioDataValue*>(aOutputBuffer), 818 aFrames); 819 } 820 821 /* static */ 822 void AudioCallbackDriver::StateCallback_s(cubeb_stream* aStream, void* aUser, 823 cubeb_state aState) { 824 AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser); 825 driver->StateCallback(aState); 826 } 827 828 /* static */ 829 void AudioCallbackDriver::DeviceChangedCallback_s(void* aUser) { 830 AudioCallbackDriver* driver = reinterpret_cast<AudioCallbackDriver*>(aUser); 831 driver->DeviceChangedCallback(); 832 } 833 834 AudioCallbackDriver::AutoInCallback::AutoInCallback( 835 AudioCallbackDriver* aDriver) 836 : mDriver(aDriver) { 837 MOZ_ASSERT(mDriver->mAudioThreadIdInCb == std::thread::id()); 838 mDriver->mAudioThreadIdInCb = std::this_thread::get_id(); 839 } 840 841 AudioCallbackDriver::AutoInCallback::~AutoInCallback() { 842 MOZ_ASSERT(mDriver->mAudioThreadIdInCb == std::this_thread::get_id()); 843 mDriver->mAudioThreadIdInCb = std::thread::id(); 844 } 845 846 bool AudioCallbackDriver::CheckThreadIdChanged() { 847 ProfilerThreadId id = profiler_current_thread_id(); 848 if (id != mAudioThreadId) { 849 mAudioThreadId = id; 850 return true; 851 } 852 return false; 853 } 854 855 long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer, 856 AudioDataValue* aOutputBuffer, 857 long aFrames) { 858 TimeStamp iterationStartTimeStamp = TimeStamp::Now(); 859 860 if (!mSandboxed && CheckThreadIdChanged()) { 861 CallbackThreadRegistry::Get()->Register(mAudioThreadId, 862 "NativeAudioCallback"); 863 } 864 865 if (mAudioStreamState.compareExchange(AudioStreamState::Starting, 866 AudioStreamState::Running)) { 867 MOZ_ASSERT(mScratchBuffer.IsEmpty()); 868 mFirstCallbackIteration = true; 869 LOG(LogLevel::Verbose, ("%p: AudioCallbackDriver %p First audio callback " 870 "close the Fallback driver", 871 Graph(), this)); 872 } 873 874 FallbackDriverState fallbackState = mFallbackDriverState; 875 if (MOZ_UNLIKELY(fallbackState == FallbackDriverState::Stopped)) { 876 // We're supposed to stop. 877 PodZero(aOutputBuffer, aFrames * mOutputChannelCount); 878 if (!mSandboxed) { 879 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId); 880 } 881 return aFrames - 1; 882 } 883 884 AudioStreamState audioStreamState = mAudioStreamState; 885 if (MOZ_UNLIKELY(audioStreamState == AudioStreamState::ChangingDevice || 886 fallbackState == FallbackDriverState::Running)) { 887 // Wait for the fallback driver to stop. Wake it up so it can stop if it's 888 // sleeping. 889 LOG(LogLevel::Verbose, 890 ("%p: AudioCallbackDriver %p Waiting for the Fallback driver to stop", 891 Graph(), this)); 892 EnsureNextIteration(); 893 PodZero(aOutputBuffer, aFrames * mOutputChannelCount); 894 return aFrames; 895 } 896 897 MOZ_ASSERT(audioStreamState == AudioStreamState::Running); 898 TRACE_AUDIO_CALLBACK_FRAME_COUNT("AudioCallbackDriver real-time budget", 899 aFrames, mSampleRate); 900 TRACE("AudioCallbackDriver::DataCallback"); 901 902 #ifdef DEBUG 903 AutoInCallback aic(this); 904 #endif 905 906 uint32_t durationMS = aFrames * 1000 / mSampleRate; 907 908 // For now, simply average the duration with the previous 909 // duration so there is some damping against sudden changes. 910 if (!mIterationDurationMS) { 911 mIterationDurationMS = durationMS; 912 } else { 913 mIterationDurationMS = (mIterationDurationMS * 3) + durationMS; 914 mIterationDurationMS /= 4; 915 } 916 917 mBuffer.SetBuffer(aOutputBuffer, aFrames); 918 // fill part or all with leftover data from last iteration (since we 919 // align to Audio blocks) 920 uint32_t prefilledFrameCount = mScratchBuffer.Empty(mBuffer); 921 922 if (mFirstCallbackIteration && !mTargetIterationTimeStamp.IsNull()) { 923 MediaTime renderingTime = 924 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(SecondsToMediaTime( 925 (iterationStartTimeStamp - mTargetIterationTimeStamp).ToSeconds())); 926 // There is no previous iteration from which to carry over mScratchBuffer. 927 MOZ_ASSERT(prefilledFrameCount == 0); 928 if (renderingTime < aFrames) { 929 // The audio data callback has occurred soon after the previous driver's 930 // rendering time. Synchronize the rendering times of graph frames 931 // under the audio callback with the rendering times under the previous 932 // driver by padding the start of the provided buffer with silence. 933 prefilledFrameCount = AssertedCast<uint32_t>(aFrames - renderingTime); 934 mBuffer.WriteSilence(prefilledFrameCount); 935 } 936 } 937 mFirstCallbackIteration = false; 938 939 // State computed time is decided by the audio callback's buffer length. 940 GraphTime bufferEndGraphTime = mStateComputedTime + mBuffer.Available(); 941 GraphTime nextStateComputedTime = 942 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(bufferEndGraphTime); 943 LOG(LogLevel::Verbose, 944 ("%p: interval[%ld; %ld] (frames: %ld) (durationMS: %u) " 945 "(duration ticks: %ld)", 946 Graph(), (long)mStateComputedTime, (long)nextStateComputedTime, 947 (long)aFrames, (uint32_t)durationMS, 948 (long)(nextStateComputedTime - mStateComputedTime))); 949 950 // mTargetIterationTimeStamp is used to synchronize the timing of rendering 951 // when switching to a different driver. iterationStartTimeStamp, which 952 // corresponds to bufferEndGraphTime, is adjusted for the extra rendering by 953 // the graph due to rounding to block boundaries, so that 954 // mTargetIterationTimeStamp corresponds to nextStateComputedTime. 955 // This is calculated for all iterations, not just when the IterationResult 956 // indicates a switch, because DeviceChangedCallback() might start a 957 // fallback driver for the next iteration. 958 mTargetIterationTimeStamp = 959 iterationStartTimeStamp + 960 MediaTimeToTimeDuration(nextStateComputedTime - bufferEndGraphTime); 961 962 // Process mic data if any/needed 963 if (aInputBuffer && mInputChannelCount > 0) { 964 Graph()->NotifyInputData(aInputBuffer, static_cast<size_t>(aFrames), 965 mSampleRate, mInputChannelCount, 966 prefilledFrameCount); 967 } 968 969 IterationResult result = Graph()->OneIteration(nextStateComputedTime, this); 970 971 mStateComputedTime = nextStateComputedTime; 972 973 MOZ_ASSERT(mBuffer.Available() == 0, 974 "The graph should have filled the buffer"); 975 976 mBuffer.BufferFilled(); 977 978 #ifdef MOZ_SAMPLE_TYPE_FLOAT32 979 // Prevent returning NaN to the OS mixer, and propagating NaN into the reverse 980 // stream of the AEC. 981 NaNToZeroInPlace(aOutputBuffer, aFrames * mOutputChannelCount); 982 #endif 983 984 #ifdef XP_MACOSX 985 // This only happens when the output is on a macbookpro's external speaker, 986 // that are stereo, but let's just be safe. 987 if (mNeedsPanning && mOutputChannelCount == 2) { 988 // hard pan to the right 989 for (uint32_t i = 0; i < aFrames * 2; i += 2) { 990 aOutputBuffer[i + 1] += aOutputBuffer[i]; 991 aOutputBuffer[i] = 0.0; 992 } 993 } 994 #endif 995 996 // No-op if MOZ_DUMP_AUDIO is not defined as an environment variable 997 if (aInputBuffer) { 998 mInputStreamFile.Write(static_cast<const AudioDataValue*>(aInputBuffer), 999 aFrames * mInputChannelCount); 1000 } 1001 mOutputStreamFile.Write(static_cast<const AudioDataValue*>(aOutputBuffer), 1002 aFrames * mOutputChannelCount); 1003 1004 if (result.IsStop()) { 1005 if (mInputDeviceID) { 1006 mGraphInterface->NotifyInputStopped(); 1007 } 1008 // Signal that we have stopped. 1009 result.Stopped(); 1010 // Update the flag before handing over the graph and going to drain. 1011 mAudioStreamState = AudioStreamState::Stopping; 1012 if (!mSandboxed) { 1013 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId); 1014 } 1015 return aFrames - 1; 1016 } 1017 1018 if (GraphDriver* nextDriver = result.NextDriver()) { 1019 LOG(LogLevel::Debug, 1020 ("%p: Switching to %s driver.", Graph(), 1021 nextDriver->AsAudioCallbackDriver() ? "audio" : "system")); 1022 if (mInputDeviceID) { 1023 mGraphInterface->NotifyInputStopped(); 1024 } 1025 result.Switched(); 1026 mAudioStreamState = AudioStreamState::Stopping; 1027 nextDriver->SetState(mStreamName, mStateComputedTime, 1028 iterationStartTimeStamp); 1029 nextDriver->Start(); 1030 if (!mSandboxed) { 1031 CallbackThreadRegistry::Get()->Unregister(mAudioThreadId); 1032 } 1033 // Returning less than aFrames starts the draining and eventually stops the 1034 // audio thread. This function will never get called again. 1035 return aFrames - 1; 1036 } 1037 1038 MOZ_ASSERT(result.IsStillProcessing()); 1039 return aFrames; 1040 } 1041 1042 static const char* StateToString(cubeb_state aState) { 1043 switch (aState) { 1044 case CUBEB_STATE_STARTED: 1045 return "STARTED"; 1046 case CUBEB_STATE_STOPPED: 1047 return "STOPPED"; 1048 case CUBEB_STATE_DRAINED: 1049 return "DRAINED"; 1050 case CUBEB_STATE_ERROR: 1051 return "ERROR"; 1052 default: 1053 MOZ_CRASH("Unexpected state!"); 1054 } 1055 } 1056 1057 void AudioCallbackDriver::StateCallback(cubeb_state aState) { 1058 MOZ_ASSERT(!InIteration()); 1059 LOG(LogLevel::Debug, 1060 ("AudioCallbackDriver(%p) State: %s", this, StateToString(aState))); 1061 1062 if (aState == CUBEB_STATE_STARTED || aState == CUBEB_STATE_STOPPED) { 1063 // Nothing to do for STARTED. 1064 // 1065 // For STOPPED, don't reset mAudioStreamState until after 1066 // cubeb_stream_stop() returns, as wasapi_stream_stop() dispatches 1067 // CUBEB_STATE_STOPPED before ensuring that data callbacks have finished. 1068 // https://searchfox.org/mozilla-central/rev/f9beb753a84aa297713d1565dcd0c5e3c66e4174/media/libcubeb/src/cubeb_wasapi.cpp#3009,3012 1069 return; 1070 } 1071 1072 AudioStreamState streamState = mAudioStreamState; 1073 if (streamState < AudioStreamState::Starting) { 1074 // mAudioStream has already entered STOPPED, DRAINED, or ERROR. 1075 // Don't reset a Pending state indicating that a task to destroy 1076 // mAudioStream and init a new cubeb_stream has already been triggered. 1077 return; 1078 } 1079 1080 // Reset for DRAINED or ERROR. 1081 streamState = mAudioStreamState.exchange(AudioStreamState::None); 1082 1083 if (aState == CUBEB_STATE_ERROR) { 1084 // About to hand over control of the graph. Do not start a new driver if 1085 // StateCallback() receives an error for this stream while the main thread 1086 // or another driver has control of the graph. 1087 if (streamState == AudioStreamState::Starting || 1088 streamState == AudioStreamState::ChangingDevice || 1089 streamState == AudioStreamState::Running) { 1090 if (mFallbackDriverState.compareExchange(FallbackDriverState::None, 1091 FallbackDriverState::Running)) { 1092 // Only switch to fallback if it's not already running. It could be 1093 // running with the callback driver having started but not seen a single 1094 // callback yet. I.e., handover from fallback to callback is not done. 1095 if (mInputDeviceID) { 1096 #ifdef DEBUG 1097 // No audio callback after an error. We're calling into the graph here 1098 // so we need to be regarded as "in iteration". 1099 AutoInCallback aic(this); 1100 #endif 1101 mGraphInterface->NotifyInputStopped(); 1102 } 1103 FallbackToSystemClockDriver(); 1104 } 1105 } 1106 } 1107 } 1108 1109 void AudioCallbackDriver::MixerCallback(AudioChunk* aMixedBuffer, 1110 uint32_t aSampleRate) { 1111 MOZ_ASSERT(InIteration()); 1112 uint32_t toWrite = mBuffer.Available(); 1113 1114 TrackTime frameCount = aMixedBuffer->mDuration; 1115 if (!mBuffer.Available() && frameCount > 0) { 1116 NS_WARNING("DataCallback buffer full, expect frame drops."); 1117 } 1118 1119 MOZ_ASSERT(mBuffer.Available() <= frameCount); 1120 1121 mBuffer.WriteFrames(*aMixedBuffer, mBuffer.Available()); 1122 MOZ_ASSERT(mBuffer.Available() == 0, 1123 "Missing frames to fill audio callback's buffer."); 1124 if (toWrite == frameCount) { 1125 return; 1126 } 1127 1128 aMixedBuffer->SliceTo(toWrite, frameCount); 1129 DebugOnly<uint32_t> written = mScratchBuffer.Fill(*aMixedBuffer); 1130 NS_WARNING_ASSERTION(written == frameCount - toWrite, "Dropping frames."); 1131 }; 1132 1133 void AudioCallbackDriver::PanOutputIfNeeded(bool aMicrophoneActive) { 1134 #ifdef XP_MACOSX 1135 TRACE("AudioCallbackDriver::PanOutputIfNeeded"); 1136 cubeb_device* out = nullptr; 1137 int rv; 1138 char name[128]; 1139 size_t length = sizeof(name); 1140 1141 rv = sysctlbyname("hw.model", name, &length, NULL, 0); 1142 if (rv) { 1143 return; 1144 } 1145 1146 int major, minor; 1147 for (uint32_t i = 0; i < length; i++) { 1148 // skip the model name 1149 if (isalpha(name[i])) { 1150 continue; 1151 } 1152 sscanf(name + i, "%d,%d", &major, &minor); 1153 break; 1154 } 1155 1156 enum MacbookModel { MacBook, MacBookPro, MacBookAir, NotAMacbook }; 1157 1158 MacbookModel model; 1159 1160 if (!strncmp(name, "MacBookPro", length)) { 1161 model = MacBookPro; 1162 } else if (strncmp(name, "MacBookAir", length)) { 1163 model = MacBookAir; 1164 } else if (strncmp(name, "MacBook", length)) { 1165 model = MacBook; 1166 } else { 1167 model = NotAMacbook; 1168 } 1169 // For macbook pro before 2016 model (change of chassis), hard pan the audio 1170 // to the right if the speakers are in use to avoid feedback. 1171 if (model == MacBookPro && major <= 12) { 1172 if (cubeb_stream_get_current_device(mAudioStream, &out) == CUBEB_OK) { 1173 MOZ_ASSERT(out); 1174 // Check if we are currently outputing sound on external speakers. 1175 if (out->output_name && !strcmp(out->output_name, "ispk")) { 1176 // Pan everything to the right speaker. 1177 LOG(LogLevel::Debug, ("Using the built-in speakers, with%s audio input", 1178 aMicrophoneActive ? "" : "out")); 1179 mNeedsPanning = aMicrophoneActive; 1180 } else { 1181 LOG(LogLevel::Debug, ("Using an external output device")); 1182 mNeedsPanning = false; 1183 } 1184 cubeb_stream_device_destroy(mAudioStream, out); 1185 } 1186 } 1187 #endif 1188 } 1189 1190 void AudioCallbackDriver::DeviceChangedCallback() { 1191 MOZ_ASSERT(!InIteration()); 1192 // Set this before the atomic write. 1193 mChangingDeviceStartTime = TimeStamp::Now(); 1194 1195 if (mAudioStreamState.compareExchange(AudioStreamState::Running, 1196 AudioStreamState::ChangingDevice)) { 1197 // Change to ChangingDevice only if we're running, i.e. there has been a 1198 // data callback and no state callback saying otherwise. 1199 // - If the audio stream is not running, it has either been stopped or it is 1200 // starting. In the latter case we assume there will be no data callback 1201 // coming until after the device change is done. 1202 // - If the audio stream is running here, there is no guarantee from the 1203 // cubeb mac backend that no more data callback will occur before the 1204 // device change takes place. They will however stop *soon*, and we hope 1205 // they stop before the first callback from the fallback driver. If the 1206 // fallback driver callback occurs before the last data callback before 1207 // the device switch, the worst case is that a long period of time 1208 // (seconds) may pass without the graph getting iterated at all. 1209 Result<bool, FallbackDriverState> res = TryStartingFallbackDriver(); 1210 1211 LOG(LogLevel::Info, 1212 ("%p: AudioCallbackDriver %p underlying default device is changing. " 1213 "Fallback %s.", 1214 Graph(), this, 1215 res.isOk() ? "started" 1216 : (res.inspectErr() == FallbackDriverState::Running 1217 ? "already running" 1218 : "has been stopped"))); 1219 1220 if (res.isErr() && res.inspectErr() == FallbackDriverState::Stopped) { 1221 mChangingDeviceStartTime = TimeStamp(); 1222 } 1223 } 1224 1225 // Tell the audio engine the device has changed, it might want to reset some 1226 // state. 1227 Graph()->DeviceChanged(); 1228 #ifdef XP_MACOSX 1229 RefPtr<AudioCallbackDriver> self(this); 1230 bool hasInput = mInputChannelCount; 1231 NS_DispatchBackgroundTask(NS_NewRunnableFunction( 1232 "PanOutputIfNeeded", [self{std::move(self)}, hasInput]() { 1233 self->PanOutputIfNeeded(hasInput); 1234 })); 1235 #endif 1236 } 1237 1238 TimeDuration AudioCallbackDriver::IterationDuration() { 1239 MOZ_ASSERT(InIteration()); 1240 // The real fix would be to have an API in cubeb to give us the number. Short 1241 // of that, we approximate it here. bug 1019507 1242 return TimeDuration::FromMilliseconds(mIterationDurationMS); 1243 } 1244 1245 void AudioCallbackDriver::EnsureNextIteration() { 1246 if (mFallbackDriverState == FallbackDriverState::Running) { 1247 auto fallback = mFallback.Lock(); 1248 if (fallback.ref()) { 1249 fallback.ref()->EnsureNextIteration(); 1250 } 1251 } 1252 } 1253 1254 TimeDuration AudioCallbackDriver::AudioOutputLatency() { 1255 TRACE("AudioCallbackDriver::AudioOutputLatency"); 1256 uint32_t latencyFrames; 1257 int rv = cubeb_stream_get_latency(mAudioStream, &latencyFrames); 1258 if (rv || mSampleRate == 0) { 1259 return TimeDuration::FromSeconds(0.0); 1260 } 1261 1262 return TimeDuration::FromSeconds(static_cast<double>(latencyFrames) / 1263 mSampleRate); 1264 } 1265 1266 bool AudioCallbackDriver::HasFallback() const { 1267 MOZ_ASSERT(InIteration()); 1268 return mFallbackDriverState != FallbackDriverState::None; 1269 } 1270 1271 bool AudioCallbackDriver::OnFallback() const { 1272 MOZ_ASSERT(InIteration()); 1273 return mFallbackDriverState == FallbackDriverState::Running; 1274 } 1275 1276 Result<bool, AudioCallbackDriver::FallbackDriverState> 1277 AudioCallbackDriver::TryStartingFallbackDriver() { 1278 FallbackDriverState oldState = 1279 mFallbackDriverState.exchange(FallbackDriverState::Running); 1280 switch (oldState) { 1281 case FallbackDriverState::None: 1282 // None -> Running: we can start the fallback. 1283 FallbackToSystemClockDriver(); 1284 return true; 1285 case FallbackDriverState::Stopped: 1286 // Stopped -> Running: Invalid edge, the graph has told us to stop. 1287 // Restore the state. 1288 mFallbackDriverState = oldState; 1289 [[fallthrough]]; 1290 case FallbackDriverState::Running: 1291 // Nothing to do, return the state. 1292 return Err(oldState); 1293 } 1294 MOZ_CRASH("Unexpected fallback state"); 1295 } 1296 1297 void AudioCallbackDriver::FallbackToSystemClockDriver() { 1298 MOZ_ASSERT(mFallbackDriverState == FallbackDriverState::Running); 1299 DebugOnly<AudioStreamState> audioStreamState = 1300 static_cast<AudioStreamState>(mAudioStreamState); 1301 MOZ_ASSERT(audioStreamState == AudioStreamState::None || 1302 audioStreamState == AudioStreamState::Pending || 1303 audioStreamState == AudioStreamState::ChangingDevice); 1304 LOG(LogLevel::Debug, 1305 ("%p: AudioCallbackDriver %p Falling back to SystemClockDriver.", Graph(), 1306 this)); 1307 // On DeviceChangedCallback() or StateChangeCallback(), mScratchBuffer might 1308 // not be empty, but switching to a fallback driver is giving up on 1309 // outputting mScratchBuffer contiguously. 1310 // Clear the buffer so that it is not output later when an audio callback 1311 // arrives for a new discontiguous output stream. 1312 mScratchBuffer.Empty(); 1313 1314 mNextReInitBackoffStep = 1315 TimeDuration::FromMilliseconds(AUDIO_INITIAL_FALLBACK_BACKOFF_STEP_MS); 1316 mNextReInitAttempt = TimeStamp::Now() + mNextReInitBackoffStep; 1317 auto fallback = MakeRefPtr<FallbackWrapper>(Graph(), this, mSampleRate, 1318 mStreamName, mStateComputedTime, 1319 mTargetIterationTimeStamp); 1320 { 1321 auto driver = mFallback.Lock(); 1322 MOZ_RELEASE_ASSERT(!driver.ref()); 1323 driver.ref() = fallback; 1324 } 1325 fallback->Start(); 1326 } 1327 1328 void AudioCallbackDriver::FallbackDriverStopped(GraphTime aStateComputedTime, 1329 TimeStamp aIterationTimeStamp, 1330 FallbackDriverState aState) { 1331 LOG(LogLevel::Debug, 1332 ("%p: AudioCallbackDriver %p Fallback driver has stopped.", Graph(), 1333 this)); 1334 mStateComputedTime = aStateComputedTime; 1335 mTargetIterationTimeStamp = aIterationTimeStamp; 1336 mNextReInitAttempt = TimeStamp(); 1337 mNextReInitBackoffStep = TimeDuration(); 1338 { 1339 auto fallback = mFallback.Lock(); 1340 MOZ_ASSERT(fallback.ref()->OnThread()); 1341 fallback.ref() = nullptr; 1342 } 1343 1344 MOZ_ASSERT(aState == FallbackDriverState::None || 1345 aState == FallbackDriverState::Stopped); 1346 mFallbackDriverState = aState; 1347 AudioStreamState audioState = mAudioStreamState; 1348 LOG(LogLevel::Debug, 1349 ("%p: AudioCallbackDriver %p Fallback driver stopped.%s%s", Graph(), this, 1350 aState == FallbackDriverState::Stopped ? " Draining." : "", 1351 aState == FallbackDriverState::None && 1352 audioState == AudioStreamState::ChangingDevice 1353 ? " Starting another due to device change." 1354 : "")); 1355 1356 if (aState == FallbackDriverState::None) { 1357 MOZ_ASSERT(audioState == AudioStreamState::Running || 1358 audioState == AudioStreamState::ChangingDevice); 1359 if (audioState == AudioStreamState::ChangingDevice) { 1360 MOZ_ALWAYS_OK(TryStartingFallbackDriver()); 1361 } 1362 } 1363 } 1364 1365 void AudioCallbackDriver::MaybeStartAudioStream() { 1366 AudioStreamState streamState = mAudioStreamState; 1367 if (streamState != AudioStreamState::None) { 1368 LOG(LogLevel::Verbose, 1369 ("%p: AudioCallbackDriver %p Cannot re-init.", Graph(), this)); 1370 return; 1371 } 1372 1373 TimeStamp now = TimeStamp::Now(); 1374 if (now < mNextReInitAttempt) { 1375 LOG(LogLevel::Verbose, 1376 ("%p: AudioCallbackDriver %p Not time to re-init yet. %.3fs left.", 1377 Graph(), this, (mNextReInitAttempt - now).ToSeconds())); 1378 return; 1379 } 1380 1381 LOG(LogLevel::Debug, ("%p: AudioCallbackDriver %p Attempting to re-init " 1382 "audio stream from fallback driver.", 1383 Graph(), this)); 1384 mNextReInitBackoffStep = 1385 std::min(mNextReInitBackoffStep * 2, 1386 TimeDuration::FromMilliseconds( 1387 StaticPrefs::media_audio_device_retry_ms())); 1388 mNextReInitAttempt = now + mNextReInitBackoffStep; 1389 Start(); 1390 } 1391 1392 const AudioInputProcessingParamsRequest& 1393 AudioCallbackDriver::RequestedInputProcessingParams() const { 1394 MOZ_ASSERT(InIteration()); 1395 return mInputProcessingRequest; 1396 } 1397 1398 void AudioCallbackDriver::RequestInputProcessingParams( 1399 AudioInputProcessingParamsRequest aRequest) { 1400 MOZ_ASSERT(InIteration()); 1401 MOZ_ASSERT(aRequest.mGeneration > mInputProcessingRequest.mGeneration); 1402 MOZ_ASSERT(aRequest.mParams != mInputProcessingRequest.mParams); 1403 LOG(LogLevel::Info, 1404 ("AudioCallbackDriver %p, Input processing params %s (Gen %d) requested.", 1405 this, CubebUtils::ProcessingParamsToString(aRequest.mParams).get(), 1406 aRequest.mGeneration)); 1407 mInputProcessingRequest = aRequest; 1408 MOZ_ALWAYS_SUCCEEDS(mCubebOperationThread->Dispatch( 1409 NS_NewRunnableFunction(__func__, [this, self = RefPtr(this), aRequest] { 1410 SetInputProcessingParams(aRequest); 1411 }))); 1412 } 1413 1414 void AudioCallbackDriver::SetInputProcessingParams( 1415 AudioInputProcessingParamsRequest aRequest) { 1416 MOZ_ASSERT(OnCubebOperationThread()); 1417 const auto requested = aRequest.mParams; 1418 auto params = aRequest.mParams; 1419 const auto generation = aRequest.mGeneration; 1420 auto result = ([&]() -> Maybe<Result<cubeb_input_processing_params, int>> { 1421 // This function decides how to handle the request. 1422 // Returning Nothing() does nothing, because either 1423 // 1) there is no update since the previous state, or 1424 // 2) handling is deferred to a later time. 1425 // Returning Some() result will forward that result to 1426 // AudioDataListener::OnInputProcessingParamsResult on the callback 1427 // thread. 1428 if (!mAudioStream) { 1429 // No Init yet. 1430 LOG(LogLevel::Debug, ("AudioCallbackDriver %p, has no cubeb stream to " 1431 "set processing params on!", 1432 this)); 1433 return Nothing(); 1434 } 1435 if (mAudioStreamState == AudioStreamState::None) { 1436 // Driver (and cubeb stream) was stopped. 1437 return Nothing(); 1438 } 1439 cubeb_input_processing_params supported; 1440 auto handle = CubebUtils::GetCubeb(); 1441 int r = cubeb_get_supported_input_processing_params(handle->Context(), 1442 &supported); 1443 if (r != CUBEB_OK) { 1444 LOG(LogLevel::Debug, 1445 ("AudioCallbackDriver %p, no supported processing params", this)); 1446 return Some(Err(CUBEB_ERROR_NOT_SUPPORTED)); 1447 } 1448 params &= supported; 1449 LOG(LogLevel::Debug, 1450 ("AudioCallbackDriver %p, requested processing params %s (gen %d) " 1451 "reduced to %s by supported params %s", 1452 this, CubebUtils::ProcessingParamsToString(requested).get(), 1453 generation, CubebUtils::ProcessingParamsToString(params).get(), 1454 CubebUtils::ProcessingParamsToString(supported).get())); 1455 if (params == mConfiguredInputProcessingParams) { 1456 LOG(LogLevel::Debug, 1457 ("AudioCallbackDriver %p, no change in processing params %s. Not " 1458 "attempting reconfiguration.", 1459 this, CubebUtils::ProcessingParamsToString(params).get())); 1460 return Some(params); 1461 } 1462 mConfiguredInputProcessingParams = params; 1463 r = cubeb_stream_set_input_processing_params(mAudioStream, params); 1464 if (r == CUBEB_OK) { 1465 LOG(LogLevel::Info, 1466 ("AudioCallbackDriver %p, input processing params set to %s", this, 1467 CubebUtils::ProcessingParamsToString(params).get())); 1468 return Some(params); 1469 } 1470 LOG(LogLevel::Info, 1471 ("AudioCallbackDriver %p, failed setting input processing params to " 1472 "%s. r=%d", 1473 this, CubebUtils::ProcessingParamsToString(params).get(), r)); 1474 return Some(Err(r)); 1475 })(); 1476 if (!result) { 1477 return; 1478 } 1479 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread( 1480 NS_NewRunnableFunction(__func__, [this, self = RefPtr(this), generation, 1481 result = result.extract()]() mutable { 1482 LOG(LogLevel::Debug, 1483 ("AudioCallbackDriver %p, Notifying of input processing params %s " 1484 "(Gen %d). r=%d", 1485 this, 1486 CubebUtils::ProcessingParamsToString( 1487 result.unwrapOr(CUBEB_INPUT_PROCESSING_PARAM_NONE)) 1488 .get(), 1489 generation, result.isErr() ? result.inspectErr() : CUBEB_OK)); 1490 mGraphInterface->NotifySetRequestedInputProcessingParamsResult( 1491 this, generation, std::move(result)); 1492 }))); 1493 } 1494 1495 } // namespace mozilla 1496 1497 // avoid redefined macro in unified build 1498 #undef LOG