TestDeviceInputTrack.cpp (20326B)
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 5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 7 #include "AudioGenerator.h" 8 #include "DeviceInputTrack.h" 9 #include "MediaTrackGraphImpl.h" 10 #include "MockCubeb.h" 11 #include "gmock/gmock.h" 12 #include "gtest/gtest.h" 13 #include "mozilla/StaticPrefs_media.h" 14 #include "mozilla/gtest/WaitFor.h" 15 #include "nsContentUtils.h" 16 17 using namespace mozilla; 18 using testing::NiceMock; 19 using testing::Return; 20 21 namespace { 22 #define DispatchFunction(f) \ 23 NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f)) 24 } // namespace 25 26 class MockGraphImpl : public MediaTrackGraphImpl { 27 public: 28 explicit MockGraphImpl(TrackRate aRate) 29 : MediaTrackGraphImpl(0, aRate, nullptr, NS_GetCurrentThread()) { 30 ON_CALL(*this, OnGraphThread).WillByDefault(Return(true)); 31 } 32 33 void Init(uint32_t aChannels) { 34 MediaTrackGraphImpl::Init(OFFLINE_THREAD_DRIVER, DIRECT_DRIVER, aChannels); 35 // We have to call `Destroy()` manually in order to break the reference. 36 // The reason we don't assign a null driver is because we would add a track 37 // to the graph, then it would trigger graph's `EnsureNextIteration()` that 38 // requires a non-null driver. 39 SetCurrentDriver(new NiceMock<MockDriver>()); 40 } 41 42 MOCK_CONST_METHOD0(OnGraphThread, bool()); 43 MOCK_METHOD1(AppendMessage, void(UniquePtr<ControlMessageInterface>)); 44 45 protected: 46 ~MockGraphImpl() = default; 47 48 class MockDriver : public GraphDriver { 49 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockDriver, override); 50 51 MockDriver() : GraphDriver(nullptr, nullptr, 0) { 52 ON_CALL(*this, OnThread).WillByDefault(Return(true)); 53 ON_CALL(*this, ThreadRunning).WillByDefault(Return(true)); 54 } 55 56 MOCK_METHOD0(Start, void()); 57 MOCK_METHOD0(Shutdown, void()); 58 MOCK_METHOD0(IterationDuration, TimeDuration()); 59 MOCK_METHOD0(EnsureNextIteration, void()); 60 MOCK_CONST_METHOD0(OnThread, bool()); 61 MOCK_CONST_METHOD0(ThreadRunning, bool()); 62 63 protected: 64 ~MockDriver() = default; 65 }; 66 }; 67 68 class TestDeviceInputTrack : public testing::Test { 69 protected: 70 TestDeviceInputTrack() : mChannels(2), mRate(44100) {} 71 72 void SetUp() override { 73 mGraph = MakeRefPtr<NiceMock<MockGraphImpl>>(mRate); 74 mGraph->Init(mChannels); 75 } 76 77 void TearDown() override { mGraph->Destroy(); } 78 79 const uint32_t mChannels; 80 const TrackRate mRate; 81 RefPtr<MockGraphImpl> mGraph; 82 }; 83 84 TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) { 85 class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack { 86 public: 87 static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) { 88 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 89 TestDeviceInputConsumerTrack* track = 90 new TestDeviceInputConsumerTrack(aGraph->GraphRate()); 91 aGraph->AddTrack(track); 92 return track; 93 } 94 95 void Destroy() { 96 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 97 DisconnectDeviceInput(); 98 DeviceInputConsumerTrack::Destroy(); 99 } 100 101 void ProcessInput(GraphTime aFrom, GraphTime aTo, 102 uint32_t aFlags) override { /* Ignored */ }; 103 104 uint32_t NumberOfChannels() const override { 105 if (mInputs.IsEmpty()) { 106 return 0; 107 } 108 DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack(); 109 MOZ_RELEASE_ASSERT(t); 110 return t->NumberOfChannels(); 111 } 112 113 private: 114 explicit TestDeviceInputConsumerTrack(TrackRate aSampleRate) 115 : DeviceInputConsumerTrack(aSampleRate) {} 116 }; 117 118 class TestAudioDataListener : public AudioDataListener { 119 public: 120 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) 121 : mChannelCount(aChannelCount), mIsVoice(aIsVoice) {} 122 // Graph thread APIs: AudioDataListenerInterface implementations. 123 uint32_t RequestedInputChannelCount( 124 MediaTrackGraph* aGraph) const override { 125 aGraph->AssertOnGraphThread(); 126 return mChannelCount; 127 } 128 cubeb_input_processing_params RequestedInputProcessingParams( 129 MediaTrackGraph*) const override { 130 return CUBEB_INPUT_PROCESSING_PARAM_NONE; 131 } 132 bool IsVoiceInput(MediaTrackGraph* aGraph) const override { 133 return mIsVoice; 134 }; 135 void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */ } 136 void Disconnect(MediaTrackGraph* aGraph) override { /* Ignored */ }; 137 void NotifySetRequestedInputProcessingParams( 138 MediaTrackGraph* aGraph, int aGeneration, 139 cubeb_input_processing_params aRequestedParams) override { 140 /* Ignored */ 141 } 142 void NotifySetRequestedInputProcessingParamsResult( 143 MediaTrackGraph* aGraph, int aGeneration, 144 const Result<cubeb_input_processing_params, int>& aResult) override { 145 /* Ignored */ 146 } 147 148 private: 149 ~TestAudioDataListener() = default; 150 151 // Graph thread-only. 152 uint32_t mChannelCount; 153 // Any thread. 154 const bool mIsVoice; 155 }; 156 157 const PrincipalHandle testPrincipal = 158 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 159 160 const CubebUtils::AudioDeviceID device1 = (void*)1; 161 RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false); 162 RefPtr<TestDeviceInputConsumerTrack> track1 = 163 TestDeviceInputConsumerTrack::Create(mGraph); 164 track1->ConnectDeviceInput(device1, listener1.get(), testPrincipal); 165 EXPECT_TRUE(track1->ConnectedToNativeDevice()); 166 EXPECT_FALSE(track1->ConnectedToNonNativeDevice()); 167 168 const CubebUtils::AudioDeviceID device2 = (void*)2; 169 RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false); 170 RefPtr<TestDeviceInputConsumerTrack> track2 = 171 TestDeviceInputConsumerTrack::Create(mGraph); 172 track2->ConnectDeviceInput(device2, listener2.get(), testPrincipal); 173 EXPECT_FALSE(track2->ConnectedToNativeDevice()); 174 EXPECT_TRUE(track2->ConnectedToNonNativeDevice()); 175 176 track2->Destroy(); 177 mGraph->RemoveTrackGraphThread(track2); 178 179 track1->Destroy(); 180 mGraph->RemoveTrackGraphThread(track1); 181 } 182 183 TEST_F(TestDeviceInputTrack, NativeInputTrackData) { 184 const uint32_t flags = 0; 185 const CubebUtils::AudioDeviceID deviceId = (void*)1; 186 187 AudioGenerator<AudioDataValue> generator(mChannels, mRate); 188 const size_t nrFrames = 10; 189 const size_t bufferSize = nrFrames * mChannels; 190 nsTArray<AudioDataValue> buffer(bufferSize); 191 buffer.AppendElements(bufferSize); 192 193 const PrincipalHandle testPrincipal = 194 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 195 196 // Setup: Create a NativeInputTrack and add it to mGraph 197 RefPtr<NativeInputTrack> track = 198 new NativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal); 199 mGraph->AddTrack(track); 200 201 // Main test below: 202 203 generator.GenerateInterleaved(buffer.Elements(), nrFrames); 204 track->NotifyInputData(mGraph.get(), buffer.Elements(), nrFrames, mRate, 205 mChannels, 0); 206 207 track->ProcessInput(0, WEBAUDIO_BLOCK_SIZE + nrFrames, flags); 208 EXPECT_EQ(static_cast<size_t>(track->GetEnd()), 209 static_cast<size_t>(WEBAUDIO_BLOCK_SIZE) + nrFrames); 210 211 // Check pre-buffering: null data with PRINCIPAL_HANDLE_NONE principal 212 AudioSegment preBuffering; 213 preBuffering.AppendSlice(*track->GetData(), 0, WEBAUDIO_BLOCK_SIZE); 214 EXPECT_TRUE(preBuffering.IsNull()); 215 for (AudioSegment::ConstChunkIterator iter(preBuffering); !iter.IsEnded(); 216 iter.Next()) { 217 const AudioChunk& chunk = *iter; 218 EXPECT_EQ(chunk.mPrincipalHandle, PRINCIPAL_HANDLE_NONE); 219 } 220 221 // Check rest of the data 222 AudioSegment data; 223 data.AppendSlice(*track->GetData(), WEBAUDIO_BLOCK_SIZE, 224 WEBAUDIO_BLOCK_SIZE + nrFrames); 225 nsTArray<AudioDataValue> interleaved; 226 size_t sampleCount = data.WriteToInterleavedBuffer(interleaved, mChannels); 227 EXPECT_EQ(sampleCount, bufferSize); 228 EXPECT_EQ(interleaved, buffer); 229 230 // Check principal in data 231 for (AudioSegment::ConstChunkIterator iter(data); !iter.IsEnded(); 232 iter.Next()) { 233 const AudioChunk& chunk = *iter; 234 EXPECT_EQ(chunk.mPrincipalHandle, testPrincipal); 235 } 236 237 // Tear down: Destroy the NativeInputTrack and remove it from mGraph. 238 track->Destroy(); 239 mGraph->RemoveTrackGraphThread(track); 240 } 241 242 class MockEventListener : public AudioInputSource::EventListener { 243 public: 244 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockEventListener, override); 245 MOCK_METHOD1(AudioDeviceChanged, void(AudioInputSource::Id)); 246 MOCK_METHOD2(AudioStateCallback, 247 void(AudioInputSource::Id, 248 AudioInputSource::EventListener::State)); 249 250 private: 251 ~MockEventListener() = default; 252 }; 253 254 TEST_F(TestDeviceInputTrack, StartAndStop) { 255 MockCubeb* cubeb = new MockCubeb(); 256 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 257 258 // Non native input settings 259 const AudioInputSource::Id sourceId = 1; 260 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1; 261 const uint32_t channels = 2; 262 const PrincipalHandle testPrincipal = 263 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 264 const TrackRate rate = 48000; 265 266 // Setup: Create a NonNativeInputTrack and add it to mGraph. 267 RefPtr<NonNativeInputTrack> track = 268 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal); 269 mGraph->AddTrack(track); 270 271 // Main test below: 272 273 // Make sure the NonNativeInputTrack can start and stop its audio correctly. 274 { 275 auto listener = MakeRefPtr<MockEventListener>(); 276 EXPECT_CALL(*listener, 277 AudioStateCallback( 278 sourceId, AudioInputSource::EventListener::State::Started)); 279 EXPECT_CALL(*listener, 280 AudioStateCallback( 281 sourceId, AudioInputSource::EventListener::State::Stopped)) 282 .Times(2); 283 284 // No input channels and device preference before start. 285 EXPECT_EQ(track->NumberOfChannels(), 0U); 286 EXPECT_EQ(track->DevicePreference(), AudioInputType::Unknown); 287 288 DispatchFunction([&] { 289 track->StartAudio(MakeRefPtr<AudioInputSource>( 290 std::move(listener), sourceId, deviceId, channels, true /* voice */, 291 testPrincipal, rate, mGraph->GraphRate())); 292 }); 293 294 // Wait for stream creation. 295 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); 296 297 // Make sure the audio stream and the track's settings are correct. 298 EXPECT_TRUE(stream->mHasInput); 299 EXPECT_FALSE(stream->mHasOutput); 300 EXPECT_EQ(stream->GetInputDeviceID(), deviceId); 301 EXPECT_EQ(stream->InputChannels(), channels); 302 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate)); 303 EXPECT_EQ(track->NumberOfChannels(), channels); 304 EXPECT_EQ(track->DevicePreference(), AudioInputType::Voice); 305 306 // Wait for stream callbacks. 307 (void)WaitFor(stream->FramesProcessedEvent()); 308 309 DispatchFunction([&] { track->StopAudio(); }); 310 311 // Wait for stream destroy. 312 (void)WaitFor(cubeb->StreamDestroyEvent()); 313 314 // No input channels and device preference after stop. 315 EXPECT_EQ(track->NumberOfChannels(), 0U); 316 EXPECT_EQ(track->DevicePreference(), AudioInputType::Unknown); 317 } 318 319 // Make sure the NonNativeInputTrack can restart its audio correctly. 320 { 321 auto listener = MakeRefPtr<MockEventListener>(); 322 EXPECT_CALL(*listener, 323 AudioStateCallback( 324 sourceId, AudioInputSource::EventListener::State::Started)); 325 EXPECT_CALL(*listener, 326 AudioStateCallback( 327 sourceId, AudioInputSource::EventListener::State::Stopped)) 328 .Times(2); 329 330 DispatchFunction([&] { 331 track->StartAudio(MakeRefPtr<AudioInputSource>( 332 std::move(listener), sourceId, deviceId, channels, true, 333 testPrincipal, rate, mGraph->GraphRate())); 334 }); 335 336 // Wait for stream creation. 337 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); 338 EXPECT_TRUE(stream->mHasInput); 339 EXPECT_FALSE(stream->mHasOutput); 340 EXPECT_EQ(stream->GetInputDeviceID(), deviceId); 341 EXPECT_EQ(stream->InputChannels(), channels); 342 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate)); 343 344 // Wait for stream callbacks. 345 (void)WaitFor(stream->FramesProcessedEvent()); 346 347 DispatchFunction([&] { track->StopAudio(); }); 348 349 // Wait for stream destroy. 350 (void)WaitFor(cubeb->StreamDestroyEvent()); 351 } 352 353 // Tear down: Destroy the NativeInputTrack and remove it from mGraph. 354 track->Destroy(); 355 mGraph->RemoveTrackGraphThread(track); 356 } 357 358 TEST_F(TestDeviceInputTrack, NonNativeInputTrackData) { 359 MockCubeb* cubeb = new MockCubeb(); 360 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 361 362 // Graph settings 363 const uint32_t flags = 0; 364 const GraphTime frames = 440; 365 366 // Non native input settings 367 const AudioInputSource::Id sourceId = 1; 368 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1; 369 const uint32_t channels = 2; 370 const PrincipalHandle testPrincipal = 371 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 372 const TrackRate rate = 48000; 373 374 // Setup: Create a NonNativeInputTrack and add it to mGraph. 375 RefPtr<NonNativeInputTrack> track = 376 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal); 377 mGraph->AddTrack(track); 378 379 // Main test below: 380 381 // Make sure we get null data if the track is not started yet. 382 GraphTime current = 0; 383 GraphTime next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(frames); 384 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput. 385 386 track->ProcessInput(current, next, flags); 387 { 388 AudioSegment data; 389 data.AppendSegment(track->GetData<AudioSegment>()); 390 EXPECT_TRUE(data.IsNull()); 391 } 392 393 // Make sure we get the AudioInputSource's data once we start the track. 394 395 current = next; 396 next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(2 * frames); 397 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput. 398 399 auto listener = MakeRefPtr<MockEventListener>(); 400 EXPECT_CALL(*listener, 401 AudioStateCallback( 402 sourceId, AudioInputSource::EventListener::State::Started)); 403 EXPECT_CALL(*listener, 404 AudioStateCallback( 405 sourceId, AudioInputSource::EventListener::State::Stopped)) 406 .Times(2); 407 408 DispatchFunction([&] { 409 track->StartAudio(MakeRefPtr<AudioInputSource>( 410 std::move(listener), sourceId, deviceId, channels, true, testPrincipal, 411 rate, mGraph->GraphRate())); 412 }); 413 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); 414 EXPECT_TRUE(stream->mHasInput); 415 EXPECT_FALSE(stream->mHasOutput); 416 EXPECT_EQ(stream->GetInputDeviceID(), deviceId); 417 EXPECT_EQ(stream->InputChannels(), channels); 418 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate)); 419 420 // Check audio data. 421 (void)WaitFor(stream->FramesProcessedEvent()); 422 track->ProcessInput(current, next, flags); 423 { 424 AudioSegment data; 425 data.AppendSlice(*track->GetData<AudioSegment>(), current, next); 426 EXPECT_FALSE(data.IsNull()); 427 for (AudioSegment::ConstChunkIterator iter(data); !iter.IsEnded(); 428 iter.Next()) { 429 EXPECT_EQ(iter->mChannelData.Length(), channels); 430 EXPECT_EQ(iter->mPrincipalHandle, testPrincipal); 431 } 432 } 433 434 // Stop the track and make sure it produces null data again. 435 current = next; 436 next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(3 * frames); 437 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput. 438 439 DispatchFunction([&] { track->StopAudio(); }); 440 (void)WaitFor(cubeb->StreamDestroyEvent()); 441 442 track->ProcessInput(current, next, flags); 443 { 444 AudioSegment data; 445 data.AppendSlice(*track->GetData<AudioSegment>(), current, next); 446 EXPECT_TRUE(data.IsNull()); 447 } 448 449 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph. 450 track->Destroy(); 451 mGraph->RemoveTrackGraphThread(track); 452 } 453 454 TEST_F(TestDeviceInputTrack, NonNativeDeviceChangedCallback) { 455 MockCubeb* cubeb = new MockCubeb(); 456 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 457 458 // Non native input settings 459 const AudioInputSource::Id sourceId = 1; 460 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1; 461 const uint32_t channels = 2; 462 const PrincipalHandle testPrincipal = 463 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 464 const TrackRate rate = 48000; 465 466 // Setup: Create a NonNativeInputTrack and add it to mGraph. 467 RefPtr<NonNativeInputTrack> track = 468 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal); 469 mGraph->AddTrack(track); 470 471 // Main test below: 472 473 auto listener = MakeRefPtr<MockEventListener>(); 474 EXPECT_CALL(*listener, AudioDeviceChanged(sourceId)); 475 EXPECT_CALL(*listener, 476 AudioStateCallback( 477 sourceId, AudioInputSource::EventListener::State::Started)); 478 EXPECT_CALL(*listener, 479 AudioStateCallback( 480 sourceId, AudioInputSource::EventListener::State::Stopped)) 481 .Times(2); 482 483 // Launch and start an audio stream. 484 DispatchFunction([&] { 485 track->StartAudio(MakeRefPtr<AudioInputSource>( 486 std::move(listener), sourceId, deviceId, channels, true, testPrincipal, 487 rate, mGraph->GraphRate())); 488 }); 489 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); 490 EXPECT_TRUE(stream->mHasInput); 491 EXPECT_FALSE(stream->mHasOutput); 492 EXPECT_EQ(stream->GetInputDeviceID(), deviceId); 493 EXPECT_EQ(stream->InputChannels(), channels); 494 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate)); 495 496 // Make sure the stream is running. 497 (void)WaitFor(stream->FramesProcessedEvent()); 498 499 // Fire a device-changed callback. 500 DispatchFunction([&] { stream->ForceDeviceChanged(); }); 501 WaitFor(stream->DeviceChangeForcedEvent()); 502 503 // Stop and destroy the stream. 504 DispatchFunction([&] { track->StopAudio(); }); 505 (void)WaitFor(cubeb->StreamDestroyEvent()); 506 507 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph. 508 track->Destroy(); 509 mGraph->RemoveTrackGraphThread(track); 510 } 511 512 TEST_F(TestDeviceInputTrack, NonNativeErrorCallback) { 513 MockCubeb* cubeb = new MockCubeb(); 514 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); 515 516 // Non native input settings 517 const AudioInputSource::Id sourceId = 1; 518 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1; 519 const uint32_t channels = 2; 520 const PrincipalHandle testPrincipal = 521 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal()); 522 const TrackRate rate = 48000; 523 524 // Setup: Create a NonNativeInputTrack and add it to mGraph. 525 RefPtr<NonNativeInputTrack> track = 526 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal); 527 mGraph->AddTrack(track); 528 529 // Main test below: 530 531 auto listener = MakeRefPtr<MockEventListener>(); 532 EXPECT_CALL(*listener, 533 AudioStateCallback( 534 sourceId, AudioInputSource::EventListener::State::Started)); 535 EXPECT_CALL(*listener, 536 AudioStateCallback( 537 sourceId, AudioInputSource::EventListener::State::Error)); 538 EXPECT_CALL(*listener, 539 AudioStateCallback( 540 sourceId, AudioInputSource::EventListener::State::Stopped)) 541 .Times(2); 542 543 // Launch and start an audio stream. 544 DispatchFunction([&] { 545 track->StartAudio(MakeRefPtr<AudioInputSource>( 546 std::move(listener), sourceId, deviceId, channels, true, testPrincipal, 547 rate, mGraph->GraphRate())); 548 }); 549 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); 550 EXPECT_TRUE(stream->mHasInput); 551 EXPECT_FALSE(stream->mHasOutput); 552 EXPECT_EQ(stream->GetInputDeviceID(), deviceId); 553 EXPECT_EQ(stream->InputChannels(), channels); 554 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate)); 555 556 // Make sure the stream is running. 557 (void)WaitFor(stream->FramesProcessedEvent()); 558 559 // Force an error in the MockCubeb. 560 DispatchFunction([&] { stream->ForceError(); }); 561 WaitFor(stream->ErrorForcedEvent()); 562 563 // Stop and destroy the stream. 564 DispatchFunction([&] { track->StopAudio(); }); 565 (void)WaitFor(cubeb->StreamDestroyEvent()); 566 567 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph. 568 track->Destroy(); 569 mGraph->RemoveTrackGraphThread(track); 570 }