TestAudioCallbackDriver.cpp (29328B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=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 "CubebUtils.h" 8 #include "MediaTrackGraphImpl.h" 9 #include "MockCubeb.h" 10 #include "MockGraphInterface.h" 11 #include "gtest/gtest.h" 12 #include "mozilla/Attributes.h" 13 #include "mozilla/SyncRunnable.h" 14 #include "mozilla/gtest/WaitFor.h" 15 #include "nsTArray.h" 16 17 namespace mozilla { 18 19 using IterationResult = GraphInterface::IterationResult; 20 using ::testing::_; 21 using ::testing::AnyNumber; 22 using ::testing::AtMost; 23 using ::testing::Eq; 24 using ::testing::InSequence; 25 using ::testing::NiceMock; 26 27 NS_IMPL_ISUPPORTS0(MockGraphInterface) 28 29 TEST(TestAudioCallbackDriver, StartStop) 30 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 31 const TrackRate rate = 44100; 32 MockCubeb* cubeb = new MockCubeb(); 33 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 34 35 RefPtr<AudioCallbackDriver> driver; 36 auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate); 37 EXPECT_CALL(*graph, NotifyInputStopped).Times(0); 38 39 driver = MakeRefPtr<AudioCallbackDriver>( 40 graph, nullptr, rate, 2, 0, nullptr, nullptr, AudioInputType::Unknown, 41 Some<AudioInputProcessingParamsRequest>( 42 {0, CUBEB_INPUT_PROCESSING_PARAM_NONE})); 43 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 44 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 45 46 graph->SetCurrentDriver(driver); 47 driver->Start(); 48 // Allow some time to "play" audio. 49 std::this_thread::sleep_for(std::chrono::milliseconds(200)); 50 EXPECT_TRUE(driver->ThreadRunning()) << "Verify thread is running"; 51 EXPECT_TRUE(driver->IsStarted()) << "Verify thread is started"; 52 53 // This will block untill all events have been executed. 54 MOZ_KnownLive(driver)->Shutdown(); 55 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 56 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 57 } 58 59 void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_BOUNDARY { 60 std::cerr << "TestSlowStart with rate " << aRate << std::endl; 61 62 MockCubeb* cubeb = new MockCubeb(); 63 cubeb->SetStreamStartFreezeEnabled(true); 64 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap(); 65 (void)unforcer; 66 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 67 68 RefPtr<AudioCallbackDriver> driver; 69 auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(aRate); 70 EXPECT_CALL(*graph, NotifyInputStopped).Times(0); 71 72 nsIThread* mainThread = NS_GetCurrentThread(); 73 Maybe<int64_t> audioStart; 74 Maybe<uint32_t> firstAlreadyBuffered; 75 int64_t inputFrameCount = 0; 76 int64_t processedFrameCount = -1; 77 ON_CALL(*graph, NotifyInputData) 78 .WillByDefault([&](const AudioDataValue*, size_t aFrames, TrackRate, 79 uint32_t, uint32_t aAlreadyBuffered) { 80 if (!audioStart) { 81 // GraphDrivers advance state computed time only in block size 82 // increments and AudioCallbackDriver first advances state computed 83 // time in the same iteration as NotifyInputData() is first called, 84 // so the frame after firstAlreadyBuffered.value() aligns with a 85 // block boundary in state computed time. 86 audioStart = Some(graph->StateComputedTime()); 87 firstAlreadyBuffered = Some(aAlreadyBuffered); 88 mainThread->Dispatch(NS_NewRunnableFunction(__func__, [&] { 89 // Start processedFrameCount now, ignoring frames processed while 90 // waiting for the fallback driver to stop. 91 processedFrameCount = 0; 92 })); 93 } 94 EXPECT_EQ( 95 PR_ROUNDUP( 96 inputFrameCount + aAlreadyBuffered - *firstAlreadyBuffered, 97 WEBAUDIO_BLOCK_SIZE), 98 static_cast<int64_t>(graph->StateComputedTime() - *audioStart)) 99 << "Input should be behind state time, due to the delayed start. " 100 << "inputFrameCount=" << inputFrameCount 101 << ", firstAlreadyBuffered=" << *firstAlreadyBuffered 102 << ", aAlreadyBuffered=" << aAlreadyBuffered 103 << ", stateComputedTime=" << graph->StateComputedTime() 104 << ", audioStartTime=" << *audioStart; 105 inputFrameCount += aFrames; 106 }); 107 108 driver = MakeRefPtr<AudioCallbackDriver>( 109 graph, nullptr, aRate, 2, 2, nullptr, (void*)1, AudioInputType::Voice, 110 Some<AudioInputProcessingParamsRequest>( 111 {0, CUBEB_INPUT_PROCESSING_PARAM_NONE})); 112 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 113 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 114 115 graph->SetCurrentDriver(driver); 116 graph->SetEnsureNextIteration(true); 117 118 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1); 119 driver->Start(); 120 auto [stream] = WaitFor(initPromise).unwrap()[0]; 121 cubeb->SetStreamStartFreezeEnabled(false); 122 123 const size_t fallbackIterations = 3; 124 WaitUntil(graph->FramesIteratedEvent(), [&](uint32_t aFrames) { 125 const GraphTime tenMillis = aRate / 100; 126 // An iteration is always rounded upwards to the next full block. 127 const GraphTime tenMillisIteration = 128 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(tenMillis); 129 // The iteration may be smaller because up to an extra block may have been 130 // processed and buffered. 131 const GraphTime tenMillisMinIteration = 132 tenMillisIteration - WEBAUDIO_BLOCK_SIZE; 133 // An iteration must be at least one audio block. 134 const GraphTime minIteration = 135 std::max<GraphTime>(WEBAUDIO_BLOCK_SIZE, tenMillisMinIteration); 136 EXPECT_GE(aFrames, minIteration) 137 << "Fallback driver iteration >= 10ms, modulo an audio block"; 138 EXPECT_LT(aFrames, static_cast<size_t>(aRate)) 139 << "Fallback driver iteration <1s (sanity)"; 140 return graph->IterationCount() >= fallbackIterations; 141 }); 142 143 MediaEventListener processedListener = 144 stream->FramesProcessedEvent().Connect(mainThread, [&](uint32_t aFrames) { 145 if (processedFrameCount >= 0) { 146 processedFrameCount += aFrames; 147 } 148 }); 149 stream->Thaw(); 150 151 SpinEventLoopUntil( 152 "processed at least 100ms of audio data from stream callback"_ns, 153 [&] { return processedFrameCount >= aRate / 10; }); 154 155 // This will block until all events have been queued. 156 MOZ_KnownLive(driver)->Shutdown(); 157 // Process processListener events. 158 NS_ProcessPendingEvents(mainThread); 159 processedListener.Disconnect(); 160 161 EXPECT_EQ(inputFrameCount, processedFrameCount); 162 EXPECT_EQ( 163 graph->StateComputedTime() - *audioStart, 164 PR_ROUNDUP(inputFrameCount - *firstAlreadyBuffered, WEBAUDIO_BLOCK_SIZE)) 165 << "Graph progresses while audio driver runs. " 166 << "stateComputedTime=" << graph->StateComputedTime() 167 << ", inputFrameCount=" << inputFrameCount 168 << ", firstAlreadyBuffered=" << *firstAlreadyBuffered; 169 } 170 171 TEST(TestAudioCallbackDriver, SlowStart) 172 { 173 TestSlowStart(1000); // 10ms = 10 <<< 128 samples 174 TestSlowStart(8000); // 10ms = 80 < 128 samples 175 TestSlowStart(44100); // 10ms = 441 > 128 samples 176 } 177 178 #ifdef DEBUG 179 template <typename T> 180 class MOZ_STACK_CLASS AutoSetter { 181 std::atomic<T>& mVal; 182 T mNew; 183 T mOld; 184 185 public: 186 explicit AutoSetter(std::atomic<T>& aVal, T aNew) 187 : mVal(aVal), mNew(aNew), mOld(mVal.exchange(aNew)) {} 188 ~AutoSetter() { 189 DebugOnly<T> oldNew = mVal.exchange(mOld); 190 MOZ_RELEASE_ASSERT(oldNew == mNew); 191 } 192 }; 193 #endif 194 195 TEST(TestAudioCallbackDriver, SlowDeviceChange) 196 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 197 constexpr TrackRate rate = 48000; 198 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); 199 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 200 201 int generation = 99; 202 auto graph = MakeRefPtr<MockGraphInterface>(rate); 203 auto driver = MakeRefPtr<AudioCallbackDriver>( 204 graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice, 205 Some<AudioInputProcessingParamsRequest>( 206 {generation, CUBEB_INPUT_PROCESSING_PARAM_NONE})); 207 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 208 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 209 210 #ifdef DEBUG 211 std::atomic<std::thread::id> threadInDriverIteration{std::thread::id()}; 212 EXPECT_CALL(*graph, InDriverIteration(driver.get())).WillRepeatedly([&] { 213 return std::this_thread::get_id() == threadInDriverIteration; 214 }); 215 #endif 216 constexpr size_t ignoredFrameCount = 1337; 217 EXPECT_CALL(*graph, NotifyInputData(_, 0, rate, 1, _)).Times(AnyNumber()); 218 EXPECT_CALL(*graph, NotifyInputData(_, ignoredFrameCount, _, _, _)).Times(0); 219 EXPECT_CALL(*graph, DeviceChanged); 220 Result<cubeb_input_processing_params, int> expected = 221 Err(CUBEB_ERROR_NOT_SUPPORTED); 222 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 223 driver.get(), generation, Eq(std::ref(expected)))); 224 225 graph->SetCurrentDriver(driver); 226 graph->SetEnsureNextIteration(true); 227 // This starts the fallback driver. 228 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1); 229 driver->Start(); 230 auto [stream] = WaitFor(initPromise).unwrap()[0]; 231 232 // Wait for the audio driver to have started the stream before running data 233 // callbacks. driver->Start() does a dispatch to the cubeb operation thread 234 // and starts the stream there. 235 nsCOMPtr<nsIEventTarget> cubebOpThread = 236 CubebUtils::GetCubebOperationThread(); 237 MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( 238 cubebOpThread, NS_NewRunnableFunction(__func__, [] {}))); 239 240 // This makes the fallback driver stop on its next callback. 241 EXPECT_EQ(stream->ManualDataCallback(0), 242 MockCubebStream::KeepProcessing::Yes); 243 { 244 #ifdef DEBUG 245 AutoSetter as(threadInDriverIteration, std::this_thread::get_id()); 246 #endif 247 while (driver->OnFallback()) { 248 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 249 } 250 } 251 252 const TimeStamp wallClockStart = TimeStamp::Now(); 253 const GraphTime graphClockStart = graph->StateComputedTime(); 254 const size_t iterationCountStart = graph->IterationCount(); 255 256 // Flag that the stream should force a devicechange event. 257 stream->NotifyDeviceChangedNow(); 258 259 // The audio driver should now have switched on the fallback driver again. 260 { 261 #ifdef DEBUG 262 AutoSetter as(threadInDriverIteration, std::this_thread::get_id()); 263 #endif 264 EXPECT_TRUE(driver->OnFallback()); 265 } 266 267 // Make sure that the audio driver can handle (and ignore) data callbacks for 268 // a little while after the devicechange callback. Cubeb does not provide 269 // ordering guarantees here. 270 auto start = TimeStamp::Now(); 271 while (start + TimeDuration::FromMilliseconds(5) > TimeStamp::Now()) { 272 EXPECT_EQ(stream->ManualDataCallback(ignoredFrameCount), 273 MockCubebStream::KeepProcessing::Yes); 274 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 275 } 276 277 // Let the fallback driver start and spin for one second. 278 std::this_thread::sleep_for(std::chrono::seconds(1)); 279 280 // Tell the fallback driver to hand over to the audio driver which has 281 // finished changing devices. 282 EXPECT_EQ(stream->ManualDataCallback(0), 283 MockCubebStream::KeepProcessing::Yes); 284 285 // Wait for the fallback to stop. 286 { 287 #ifdef DEBUG 288 AutoSetter as(threadInDriverIteration, std::this_thread::get_id()); 289 #endif 290 while (driver->OnFallback()) { 291 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 292 } 293 } 294 295 TimeStamp wallClockEnd = TimeStamp::Now(); 296 GraphTime graphClockEnd = graph->StateComputedTime(); 297 size_t iterationCountEnd = graph->IterationCount(); 298 299 auto wallClockDuration = 300 media::TimeUnit::FromTimeDuration(wallClockEnd - wallClockStart); 301 auto graphClockDuration = 302 media::TimeUnit(CheckedInt64(graphClockEnd) - graphClockStart, rate); 303 304 // Check that the time while we switched devices was accounted for by the 305 // fallback driver. 306 EXPECT_NEAR( 307 wallClockDuration.ToSeconds(), graphClockDuration.ToSeconds(), 308 #ifdef XP_MACOSX 309 // SystemClockDriver on macOS in CI is underrunning, i.e. the driver 310 // thread when waiting for the next iteration waits too long. Therefore 311 // the graph clock is unable to keep up with wall clock. 312 wallClockDuration.ToSeconds() * 0.8 313 #else 314 0.1 315 #endif 316 ); 317 // Check that each fallback driver was of reasonable cadence. It's a thread 318 // that tries to run a task every 10ms. Check that the average callback 319 // interval i falls in 8ms ≤ i ≤ 40ms. 320 auto fallbackCadence = 321 graphClockDuration / 322 static_cast<int64_t>(iterationCountEnd - iterationCountStart); 323 EXPECT_LE(8, fallbackCadence.ToMilliseconds()); 324 EXPECT_LE(fallbackCadence.ToMilliseconds(), 40.0); 325 326 // This will block until all events have been queued. 327 MOZ_KnownLive(driver)->Shutdown(); 328 // Drain the event queue. 329 NS_ProcessPendingEvents(nullptr); 330 } 331 332 TEST(TestAudioCallbackDriver, DeviceChangeAfterStop) 333 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 334 constexpr TrackRate rate = 48000; 335 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); 336 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 337 338 auto graph = MakeRefPtr<MockGraphInterface>(rate); 339 auto driver = MakeRefPtr<AudioCallbackDriver>( 340 graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice, 341 Some<AudioInputProcessingParamsRequest>( 342 {99, CUBEB_INPUT_PROCESSING_PARAM_NONE})); 343 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 344 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 345 346 auto newDriver = MakeRefPtr<AudioCallbackDriver>( 347 graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice, 348 Some<AudioInputProcessingParamsRequest>( 349 {99, CUBEB_INPUT_PROCESSING_PARAM_NONE})); 350 EXPECT_FALSE(newDriver->ThreadRunning()) << "Verify thread is not running"; 351 EXPECT_FALSE(newDriver->IsStarted()) << "Verify thread is not started"; 352 353 #ifdef DEBUG 354 std::atomic<std::thread::id> threadInDriverIteration{ 355 std::this_thread::get_id()}; 356 EXPECT_CALL(*graph, InDriverIteration(_)).WillRepeatedly([&] { 357 return std::this_thread::get_id() == threadInDriverIteration; 358 }); 359 #endif 360 EXPECT_CALL(*graph, NotifyInputData(_, 0, rate, 1, _)).Times(AnyNumber()); 361 // This only happens if the first fallback driver is stopped by the audio 362 // driver handover rather than the driver switch. It happens when the 363 // subsequent audio callback performs the switch. 364 EXPECT_CALL(*graph, NotifyInputStopped()).Times(AtMost(1)); 365 Result<cubeb_input_processing_params, int> expected = 366 Err(CUBEB_ERROR_NOT_SUPPORTED); 367 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 368 driver.get(), 99, Eq(std::ref(expected)))); 369 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 370 newDriver.get(), 99, Eq(std::ref(expected)))); 371 372 graph->SetCurrentDriver(driver); 373 graph->SetEnsureNextIteration(true); 374 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1); 375 // This starts the fallback driver. 376 driver->Start(); 377 RefPtr<SmartMockCubebStream> stream; 378 std::tie(stream) = WaitFor(initPromise).unwrap()[0]; 379 380 // Wait for the audio driver to have started or the DeviceChanged event will 381 // be ignored. driver->Start() does a dispatch to the cubeb operation thread 382 // and starts the stream there. 383 nsCOMPtr<nsIEventTarget> cubebOpThread = 384 CubebUtils::GetCubebOperationThread(); 385 MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( 386 cubebOpThread, NS_NewRunnableFunction(__func__, [] {}))); 387 388 initPromise = TakeN(cubeb->StreamInitEvent(), 1); 389 Monitor mon(__func__); 390 bool canContinueToStartNextDriver = false; 391 bool continued = false; 392 393 // This marks the audio driver as running. 394 EXPECT_EQ(stream->ManualDataCallback(0), 395 MockCubebStream::KeepProcessing::Yes); 396 397 // To satisfy TSAN's lock-order-inversion checking we avoid locking stream's 398 // mMutex (by calling ManualDataCallback) under mon. The SwitchTo runnable 399 // below already locks mon under stream's mMutex. 400 MonitorAutoLock lock(mon); 401 402 // If a fallback driver callback happens between the audio callback 403 // above, and the SwitchTo below, the driver will enter 404 // `FallbackDriverState::None`, relying on the audio driver to 405 // iterate the graph, including performing the driver switch. This 406 // test may therefore intermittently take different code paths. 407 // Note however that the fallback driver runs every ~10ms while the 408 // time from the manual callback above to telling the mock graph to 409 // switch drivers below is much much shorter. The vast majority of 410 // test runs will exercise the intended code path. 411 412 // Make the fallback driver enter FallbackDriverState::Stopped by 413 // switching audio driver in the graph. 414 graph->SwitchTo(newDriver, NS_NewRunnableFunction(__func__, [&] { 415 MonitorAutoLock lock(mon); 416 // Block the fallback driver on its thread until 417 // the test on main thread has finished testing 418 // what it needs. 419 while (!canContinueToStartNextDriver) { 420 lock.Wait(); 421 } 422 // Notify the test that it can take these 423 // variables off the stack now. 424 continued = true; 425 lock.Notify(); 426 })); 427 428 // Wait for the fallback driver to stop running. 429 while (driver->OnFallback()) { 430 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 431 } 432 433 if (driver->HasFallback()) { 434 // Driver entered FallbackDriverState::Stopped as desired. 435 // Proceed with a DeviceChangedCallback. 436 437 EXPECT_CALL(*graph, DeviceChanged); 438 439 { 440 #ifdef DEBUG 441 AutoSetter as(threadInDriverIteration, std::thread::id()); 442 #endif 443 // After stopping the fallback driver, but before newDriver has 444 // stopped the old audio driver, fire a DeviceChanged event to 445 // ensure it is handled properly. 446 AudioCallbackDriver::DeviceChangedCallback_s(driver); 447 } 448 449 EXPECT_FALSE(driver->OnFallback()) 450 << "DeviceChangedCallback after stopping must not start the " 451 "fallback driver again"; 452 } 453 454 // Iterate the audio driver on a background thread in case the fallback 455 // driver completed the handover to the audio driver before the switch 456 // above. Doing the switch would deadlock as the switch runnable waits on 457 // mon. 458 NS_DispatchBackgroundTask(NS_NewRunnableFunction( 459 "DeviceChangeAfterStop::postSwitchManualAudioCallback", [stream] { 460 // An audio callback after switching must tell the stream to stop. 461 EXPECT_EQ(stream->ManualDataCallback(0), 462 MockCubebStream::KeepProcessing::No); 463 })); 464 465 // Unblock the fallback driver. 466 canContinueToStartNextDriver = true; 467 lock.Notify(); 468 469 // Wait for the fallback driver to continue, so we can clear the 470 // stack. 471 while (!continued) { 472 lock.Wait(); 473 } 474 475 // Wait for newDriver's cubeb stream to init. 476 std::tie(stream) = WaitFor(initPromise).unwrap()[0]; 477 478 graph->StopIterating(); 479 newDriver->EnsureNextIteration(); 480 while (newDriver->OnFallback()) { 481 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 482 } 483 484 { 485 #ifdef DEBUG 486 AutoSetter as(threadInDriverIteration, std::thread::id()); 487 #endif 488 EXPECT_EQ(stream->ManualDataCallback(0), 489 MockCubebStream::KeepProcessing::No); 490 } 491 492 // Drain the event queue. 493 NS_ProcessPendingEvents(nullptr); 494 } 495 496 void TestInputProcessingOnStart( 497 MockCubeb* aCubeb, int aGeneration, 498 cubeb_input_processing_params aRequested, 499 const Result<cubeb_input_processing_params, int>& aExpected) 500 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 501 const TrackRate rate = 44100; 502 503 auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate); 504 auto driver = MakeRefPtr<AudioCallbackDriver>( 505 graph, nullptr, rate, 2, 1, nullptr, nullptr, AudioInputType::Voice, 506 Some<AudioInputProcessingParamsRequest>({aGeneration, aRequested})); 507 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 508 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 509 510 #ifdef DEBUG 511 std::atomic_bool inGraphIteration{false}; 512 ON_CALL(*graph, InDriverIteration(_)).WillByDefault([&] { 513 return inGraphIteration.load() && NS_IsMainThread(); 514 }); 515 #endif 516 bool notified = false; 517 EXPECT_CALL(*graph, NotifyInputStopped).Times(0); 518 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 519 driver.get(), aGeneration, Eq(std::ref(aExpected)))) 520 .WillOnce([&] { notified = true; }); 521 522 graph->SetCurrentDriver(driver); 523 auto initPromise = TakeN(aCubeb->StreamInitEvent(), 1); 524 driver->Start(); 525 auto [stream] = WaitFor(initPromise).unwrap()[0]; 526 527 // Wait for the audio driver to have started the stream before running data 528 // callbacks. driver->Start() does a dispatch to the cubeb operation thread 529 // and starts the stream there. 530 nsCOMPtr<nsIEventTarget> cubebOpThread = 531 CubebUtils::GetCubebOperationThread(); 532 MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( 533 cubebOpThread, NS_NewRunnableFunction(__func__, [] {}))); 534 535 // This makes the fallback driver stop on its next callback. 536 { 537 #ifdef DEBUG 538 AutoSetter as(inGraphIteration, true); 539 #endif 540 while (driver->OnFallback()) { 541 stream->ManualDataCallback(0); 542 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 543 } 544 } 545 546 while (!notified) { 547 NS_ProcessNextEvent(); 548 } 549 550 // This will block untill all events have been executed. 551 MOZ_KnownLive(driver)->Shutdown(); 552 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 553 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 554 } 555 556 TEST(TestAudioCallbackDriver, InputProcessingOnStart) 557 { 558 constexpr cubeb_input_processing_params allParams = 559 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | 560 CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | 561 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION | 562 CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION; 563 564 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); 565 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 566 567 // Not supported by backend. 568 cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE, 569 CUBEB_ERROR_NOT_SUPPORTED); 570 TestInputProcessingOnStart(cubeb, 1, 571 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, 572 Err(CUBEB_ERROR_NOT_SUPPORTED)); 573 574 // Not supported by params. 575 cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE, 576 CUBEB_OK); 577 TestInputProcessingOnStart(cubeb, 2, 578 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, 579 CUBEB_INPUT_PROCESSING_PARAM_NONE); 580 581 // Successful all. 582 cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK); 583 TestInputProcessingOnStart(cubeb, 3, allParams, allParams); 584 585 // Successful partial. 586 TestInputProcessingOnStart(cubeb, 4, 587 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, 588 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION); 589 590 // Not supported by stream. 591 cubeb->SetInputProcessingApplyRv(CUBEB_ERROR); 592 TestInputProcessingOnStart(cubeb, 5, 593 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, 594 Err(CUBEB_ERROR)); 595 } 596 597 TEST(TestAudioCallbackDriver, InputProcessingWhileRunning) 598 MOZ_CAN_RUN_SCRIPT_BOUNDARY { 599 constexpr TrackRate rate = 44100; 600 constexpr cubeb_input_processing_params allParams = 601 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | 602 CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | 603 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION | 604 CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION; 605 constexpr int applyError = 99; 606 607 int currentGeneration = 0; 608 const auto signal = [&](auto aDriver, auto aGeneration, 609 auto&& aResult) mutable { 610 MOZ_ASSERT(NS_IsMainThread()); 611 currentGeneration = aGeneration; 612 }; 613 const auto waitForSignal = [&](int aGeneration) { 614 while (currentGeneration != aGeneration) { 615 NS_ProcessNextEvent(); 616 } 617 }; 618 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); 619 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 620 621 auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate); 622 auto driver = MakeRefPtr<AudioCallbackDriver>( 623 graph, nullptr, rate, 2, 1, nullptr, nullptr, AudioInputType::Voice, 624 Some<AudioInputProcessingParamsRequest>( 625 {100, CUBEB_INPUT_PROCESSING_PARAM_NONE})); 626 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 627 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 628 629 EXPECT_CALL(*graph, NotifyInputStopped).Times(0); 630 // Expectations 631 const Result<cubeb_input_processing_params, int> noneResult = 632 CUBEB_INPUT_PROCESSING_PARAM_NONE; 633 const Result<cubeb_input_processing_params, int> aecResult = 634 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION; 635 const Result<cubeb_input_processing_params, int> allResult = allParams; 636 const Result<cubeb_input_processing_params, int> notSupportedResult = 637 Err(CUBEB_ERROR_NOT_SUPPORTED); 638 const Result<cubeb_input_processing_params, int> applyErrorResult = 639 Err(applyError); 640 { 641 InSequence s; 642 643 // Notified on start. 644 EXPECT_CALL(*graph, 645 NotifySetRequestedInputProcessingParamsResult( 646 driver.get(), 100, Eq(std::ref(notSupportedResult)))) 647 .WillOnce(signal); 648 // Not supported by backend. 649 EXPECT_CALL(*graph, 650 NotifySetRequestedInputProcessingParamsResult( 651 driver.get(), 101, Eq(std::ref(notSupportedResult)))) 652 .WillOnce(signal); 653 // Not supported by params. 654 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 655 driver.get(), 102, Eq(std::ref(noneResult)))) 656 .WillOnce(signal); 657 // Successful all. 658 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 659 driver.get(), 103, Eq(std::ref(allResult)))) 660 .WillOnce(signal); 661 // Successful partial. 662 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 663 driver.get(), 104, Eq(std::ref(aecResult)))) 664 .WillOnce(signal); 665 // Not supported by stream. 666 EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult( 667 driver.get(), 105, Eq(std::ref(applyErrorResult)))) 668 .WillOnce(signal); 669 } 670 671 #ifdef DEBUG 672 std::atomic_bool inGraphIteration{false}; 673 ON_CALL(*graph, InDriverIteration(_)).WillByDefault([&] { 674 return inGraphIteration.load() && NS_IsMainThread(); 675 }); 676 #endif 677 678 const auto setParams = [&](int aGen, cubeb_input_processing_params aParams) { 679 { 680 #ifdef DEBUG 681 AutoSetter as(inGraphIteration, true); 682 #endif 683 driver->RequestInputProcessingParams({aGen, aParams}); 684 } 685 }; 686 687 graph->SetCurrentDriver(driver); 688 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1); 689 driver->Start(); 690 auto [stream] = WaitFor(initPromise).unwrap()[0]; 691 692 // Wait for the audio driver to have started the stream before running data 693 // callbacks. driver->Start() does a dispatch to the cubeb operation thread 694 // and starts the stream there. 695 nsCOMPtr<nsIEventTarget> cubebOpThread = 696 CubebUtils::GetCubebOperationThread(); 697 MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread( 698 cubebOpThread, NS_NewRunnableFunction(__func__, [] {}))); 699 700 // This makes the fallback driver stop on its next callback. 701 702 { 703 #ifdef DEBUG 704 AutoSetter as(inGraphIteration, true); 705 #endif 706 while (driver->OnFallback()) { 707 stream->ManualDataCallback(0); 708 std::this_thread::sleep_for(std::chrono::milliseconds(1)); 709 } 710 } 711 waitForSignal(100); 712 713 // Not supported by backend. 714 cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE, 715 CUBEB_ERROR_NOT_SUPPORTED); 716 setParams(101, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION); 717 waitForSignal(101); 718 719 // Not supported by params. 720 cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE, 721 CUBEB_OK); 722 setParams(102, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION); 723 waitForSignal(102); 724 725 // Successful all. 726 cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK); 727 setParams(103, allParams); 728 waitForSignal(103); 729 730 // Successful partial. 731 setParams(104, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION); 732 waitForSignal(104); 733 734 // Not supported by stream. 735 cubeb->SetInputProcessingApplyRv(applyError); 736 setParams(105, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION); 737 waitForSignal(105); 738 739 // This will block untill all events have been executed. 740 MOZ_KnownLive(driver)->Shutdown(); 741 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running"; 742 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started"; 743 } 744 745 } // namespace mozilla