diff options
Diffstat (limited to 'dom/media/gtest/TestAudioTrackGraph.cpp')
-rw-r--r-- | dom/media/gtest/TestAudioTrackGraph.cpp | 740 |
1 files changed, 484 insertions, 256 deletions
diff --git a/dom/media/gtest/TestAudioTrackGraph.cpp b/dom/media/gtest/TestAudioTrackGraph.cpp index 7be1224ab9..b1202277ce 100644 --- a/dom/media/gtest/TestAudioTrackGraph.cpp +++ b/dom/media/gtest/TestAudioTrackGraph.cpp @@ -21,9 +21,13 @@ #include "WavDumper.h" using namespace mozilla; +using testing::Eq; +using testing::InSequence; +using testing::Return; +using testing::StrictMock; // Short-hand for InvokeAsync on the current thread. -#define Invoke(f) InvokeAsync(GetCurrentSerialEventTarget(), __func__, f) +#define InvokeAsync(f) InvokeAsync(GetCurrentSerialEventTarget(), __func__, f) // Short-hand for DispatchToCurrentThread with a function. #define DispatchFunction(f) \ @@ -33,6 +37,12 @@ using namespace mozilla; #define DispatchMethod(t, m, args...) \ NS_DispatchToCurrentThread(NewRunnableMethod(__func__, t, m, ##args)) +// Short-hand for draining the current threads event queue, i.e. processing +// those runnables dispatched per above. +#define ProcessEventQueue() \ + while (NS_ProcessNextEvent(nullptr, false)) { \ + } + namespace { #ifdef MOZ_WEBRTC /* @@ -111,6 +121,44 @@ struct StopNonNativeInput : public ControlMessage { void Run() override { mInputTrack->StopAudio(); } }; +// Helper for detecting when fallback driver has been switched away, for use +// with RunningMode::Manual. +class OnFallbackListener : public MediaTrackListener { + const RefPtr<MediaTrack> mTrack; + Atomic<bool> mOnFallback{true}; + + public: + explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {} + + void Reset() { mOnFallback = true; } + bool OnFallback() { return mOnFallback; } + + void NotifyOutput(MediaTrackGraph*, TrackTime) override { + if (auto* ad = + mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) { + mOnFallback = ad->OnFallback(); + } + } +}; + +class MockAudioDataListener : public AudioDataListener { + protected: + ~MockAudioDataListener() = default; + + public: + MockAudioDataListener() = default; + + MOCK_METHOD(uint32_t, RequestedInputChannelCount, (MediaTrackGraph*), + (const)); + MOCK_METHOD(cubeb_input_processing_params, RequestedInputProcessingParams, + (MediaTrackGraph*), (const)); + MOCK_METHOD(bool, IsVoiceInput, (MediaTrackGraph*), (const)); + MOCK_METHOD(void, DeviceChanged, (MediaTrackGraph*)); + MOCK_METHOD(void, Disconnect, (MediaTrackGraph*)); + MOCK_METHOD(void, NotifySetRequestedInputProcessingParamsResult, + (MediaTrackGraph*, cubeb_input_processing_params, + (const Result<cubeb_input_processing_params, int>&))); +}; } // namespace /* @@ -164,7 +212,7 @@ TEST(TestAudioTrackGraph, DifferentDeviceIDs) // graph from the global hash table and let it shutdown. using SourceTrackPromise = MozPromise<SourceMediaTrack*, nsresult, true>; - auto p = Invoke([g] { + auto p = InvokeAsync([g] { return SourceTrackPromise::CreateAndResolve( g->CreateSourceTrack(MediaSegment::AUDIO), __func__); }); @@ -256,7 +304,7 @@ TEST(TestAudioTrackGraph, NotifyDeviceStarted) nullptr, GetMainThreadSerialEventTarget()); RefPtr<SourceMediaTrack> dummySource; - Unused << WaitFor(Invoke([&] { + Unused << WaitFor(InvokeAsync([&] { // Dummy track to make graph rolling. Add it and remove it to remove the // graph from the global hash table and let it shutdown. dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO); @@ -522,7 +570,7 @@ TEST(TestAudioTrackGraph, NonNativeInputTrackErrorCallback) class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack { public: static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) { - MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); TestDeviceInputConsumerTrack* track = new TestDeviceInputConsumerTrack(aGraph->GraphRate()); aGraph->AddTrack(track); @@ -530,7 +578,7 @@ class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack { } void Destroy() { - MOZ_ASSERT(NS_IsMainThread()); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); DisconnectDeviceInput(); DeviceInputConsumerTrack::Destroy(); } @@ -543,7 +591,7 @@ class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack { if (mInputs.IsEmpty()) { GetData<AudioSegment>()->AppendNullData(aTo - aFrom); } else { - MOZ_ASSERT(mInputs.Length() == 1); + MOZ_RELEASE_ASSERT(mInputs.Length() == 1); AudioSegment data; DeviceInputConsumerTrack::GetInputSourceData(data, aFrom, aTo); GetData<AudioSegment>()->AppendFrom(&data); @@ -555,7 +603,7 @@ class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack { return 0; } DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack(); - MOZ_ASSERT(t); + MOZ_RELEASE_ASSERT(t); return t->NumberOfChannels(); } @@ -574,30 +622,23 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback) CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false), nullptr, GetMainThreadSerialEventTarget()); - class TestAudioDataListener : public AudioDataListener { + class TestAudioDataListener : public StrictMock<MockAudioDataListener> { public: - TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) - : mChannelCount(aChannelCount), - mIsVoice(aIsVoice), - mDeviceChangedCount(0) {} - - uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override { - return mChannelCount; - } - bool IsVoiceInput(MediaTrackGraph* aGraph) const override { - return mIsVoice; - }; - void DeviceChanged(MediaTrackGraph* aGraph) override { - ++mDeviceChangedCount; + TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) { + EXPECT_CALL(*this, RequestedInputChannelCount) + .WillRepeatedly(Return(aChannelCount)); + EXPECT_CALL(*this, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE)); + EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice)); + { + InSequence s; + EXPECT_CALL(*this, DeviceChanged); + EXPECT_CALL(*this, Disconnect); + } } - void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */}; - uint32_t DeviceChangedCount() { return mDeviceChangedCount; } private: ~TestAudioDataListener() = default; - const uint32_t mChannelCount; - const bool mIsVoice; - std::atomic<uint32_t> mDeviceChangedCount; }; // Create a full-duplex AudioCallbackDriver by creating a NativeInputTrack. @@ -610,7 +651,7 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback) EXPECT_TRUE(track1->ConnectedToNativeDevice()); EXPECT_FALSE(track1->ConnectedToNonNativeDevice()); auto started = - Invoke([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); }); + InvokeAsync([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); }); RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(stream1->mHasInput); EXPECT_TRUE(stream1->mHasOutput); @@ -648,9 +689,6 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback) WaitFor(cubeb->StreamDestroyEvent()); EXPECT_EQ(destroyedStream.get(), stream2.get()); - // Make sure we only have one device-changed event for the NativeInputTrack. - EXPECT_EQ(listener2->DeviceChangedCount(), 1U); - // Destroy the NativeInputTrack. DispatchFunction([&] { track1->DisconnectDeviceInput(); @@ -658,9 +696,6 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback) }); destroyedStream = WaitFor(cubeb->StreamDestroyEvent()); EXPECT_EQ(destroyedStream.get(), stream1.get()); - - // Make sure we only have one device-changed event for the NativeInputTrack. - EXPECT_EQ(listener1->DeviceChangedCount(), 1U); } // The native audio stream (a.k.a. GraphDriver) and the non-native audio stream @@ -692,57 +727,32 @@ TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) // A test-only AudioDataListener that simulates AudioInputProcessing's setter // and getter for the input channel count. - class TestAudioDataListener : public AudioDataListener { + class TestAudioDataListener : public StrictMock<MockAudioDataListener> { public: - TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) - : mChannelCount(aChannelCount), mIsVoice(aIsVoice) {} + TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) { + EXPECT_CALL(*this, RequestedInputChannelCount) + .WillRepeatedly(Return(aChannelCount)); + EXPECT_CALL(*this, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE)); + EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice)); + EXPECT_CALL(*this, Disconnect); + } // Main thread API void SetInputChannelCount(MediaTrackGraph* aGraph, CubebUtils::AudioDeviceID aDevice, uint32_t aChannelCount) { - MOZ_ASSERT(NS_IsMainThread()); - - struct Message : public ControlMessage { - MediaTrackGraph* mGraph; - TestAudioDataListener* mListener; - CubebUtils::AudioDeviceID mDevice; - uint32_t mChannelCount; - - Message(MediaTrackGraph* aGraph, TestAudioDataListener* aListener, - CubebUtils::AudioDeviceID aDevice, uint32_t aChannelCount) - : ControlMessage(nullptr), - mGraph(aGraph), - mListener(aListener), - mDevice(aDevice), - mChannelCount(aChannelCount) {} - void Run() override { - mListener->mChannelCount = mChannelCount; - mGraph->ReevaluateInputDevice(mDevice); - } - }; - - static_cast<MediaTrackGraphImpl*>(aGraph)->AppendMessage( - MakeUnique<Message>(aGraph, this, aDevice, aChannelCount)); + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + static_cast<MediaTrackGraphImpl*>(aGraph) + ->QueueControlMessageWithNoShutdown( + [this, self = RefPtr(this), aGraph, aDevice, aChannelCount] { + EXPECT_CALL(*this, RequestedInputChannelCount) + .WillRepeatedly(Return(aChannelCount)); + aGraph->ReevaluateInputDevice(aDevice); + }); } - // Graph thread APIs: AudioDataListenerInterface implementations. - uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override { - aGraph->AssertOnGraphThread(); - return mChannelCount; - } - bool IsVoiceInput(MediaTrackGraph* aGraph) const override { - return mIsVoice; - }; - void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */ - } - void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */}; private: ~TestAudioDataListener() = default; - - // Graph thread-only. - uint32_t mChannelCount; - // Any thread. - const bool mIsVoice; }; // Request a new input channel count and expect to have a new stream. @@ -841,8 +851,8 @@ TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) EXPECT_TRUE(track1->ConnectedToNativeDevice()); EXPECT_FALSE(track1->ConnectedToNonNativeDevice()); - auto started = - Invoke([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); }); + auto started = InvokeAsync( + [&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); }); nativeStream = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(nativeStream->mHasInput); EXPECT_TRUE(nativeStream->mHasOutput); @@ -947,30 +957,18 @@ TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) // AudioDataListener. However, it only tests when MOZ_WEBRTC is defined. TEST(TestAudioTrackGraph, SwitchNativeInputDevice) { - class TestAudioDataListener : public AudioDataListener { + class TestAudioDataListener : public StrictMock<MockAudioDataListener> { public: - TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) - : mChannelCount(aChannelCount), - mIsVoice(aIsVoice), - mDeviceChangedCount(0) {} - - uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override { - return mChannelCount; - } - bool IsVoiceInput(MediaTrackGraph* aGraph) const override { - return mIsVoice; - }; - void DeviceChanged(MediaTrackGraph* aGraph) override { - ++mDeviceChangedCount; + TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) { + EXPECT_CALL(*this, RequestedInputChannelCount) + .WillRepeatedly(Return(aChannelCount)); + EXPECT_CALL(*this, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE)); + EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice)); } - void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */}; - uint32_t DeviceChangedCount() { return mDeviceChangedCount; } private: ~TestAudioDataListener() = default; - const uint32_t mChannelCount; - const bool mIsVoice; - std::atomic<uint32_t> mDeviceChangedCount; }; MockCubeb* cubeb = new MockCubeb(); @@ -1049,11 +1047,12 @@ TEST(TestAudioTrackGraph, SwitchNativeInputDevice) RefPtr<TestDeviceInputConsumerTrack> track1 = TestDeviceInputConsumerTrack::Create(graph); RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false); + EXPECT_CALL(*listener1, Disconnect); track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE); EXPECT_EQ(track1->DeviceId().value(), device1); auto started = - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); + InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(stream1->mHasInput); @@ -1069,6 +1068,7 @@ TEST(TestAudioTrackGraph, SwitchNativeInputDevice) RefPtr<TestDeviceInputConsumerTrack> track2 = TestDeviceInputConsumerTrack::Create(graph); RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false); + EXPECT_CALL(*listener2, Disconnect).Times(2); track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE); EXPECT_EQ(track2->DeviceId().value(), device2); @@ -1085,6 +1085,7 @@ TEST(TestAudioTrackGraph, SwitchNativeInputDevice) RefPtr<TestDeviceInputConsumerTrack> track3 = TestDeviceInputConsumerTrack::Create(graph); RefPtr<TestAudioDataListener> listener3 = new TestAudioDataListener(1, false); + EXPECT_CALL(*listener3, Disconnect).Times(2); track3->ConnectDeviceInput(device3, listener3, PRINCIPAL_HANDLE_NONE); EXPECT_EQ(track3->DeviceId().value(), device3); @@ -1160,7 +1161,7 @@ TEST(TestAudioTrackGraph, ErrorCallback) // output from the graph. RefPtr<AudioProcessingTrack> processingTrack; RefPtr<AudioInputProcessing> listener; - auto started = Invoke([&] { + auto started = InvokeAsync([&] { processingTrack = AudioProcessingTrack::Create(graph); listener = new AudioInputProcessing(2); QueueExpectIsPassThrough(processingTrack, listener); @@ -1225,7 +1226,7 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack) RefPtr<ProcessedMediaTrack> outputTrack; RefPtr<MediaInputPort> port; RefPtr<AudioInputProcessing> listener; - auto p = Invoke([&] { + auto p = InvokeAsync([&] { processingTrack = AudioProcessingTrack::Create(graph); outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO); outputTrack->QueueSetAutoend(false); @@ -1282,7 +1283,7 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack) EXPECT_EQ(estimatedFreq, inputFrequency); std::cerr << "PreSilence: " << preSilenceSamples << std::endl; - // We buffer 128 frames. See DeviceInputTrack::ProcessInput. + // We buffer 128 frames. See NativeInputTrack::NotifyInputData. EXPECT_GE(preSilenceSamples, 128U); // If the fallback system clock driver is doing a graph iteration before the // first audio driver iteration comes in, that iteration is ignored and @@ -1297,13 +1298,17 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack) TEST(TestAudioTrackGraph, ReConnectDeviceInput) { - MockCubeb* cubeb = new MockCubeb(); + MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); // 48k is a native processing rate, and avoids a resampling pass compared // to 44.1k. The resampler may add take a few frames to stabilize, which show // as unexected discontinuities in the test. const TrackRate rate = 48000; + // Use a drift factor so that we don't dont produce perfect 10ms-chunks. + // This will exercise whatever buffers are in the audio processing pipeline, + // and the bookkeeping surrounding them. + const long step = 10 * rate * 1111 / 1000 / PR_MSEC_PER_SEC; MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance( MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, rate, nullptr, @@ -1315,7 +1320,8 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput) RefPtr<ProcessedMediaTrack> outputTrack; RefPtr<MediaInputPort> port; RefPtr<AudioInputProcessing> listener; - auto p = Invoke([&] { + RefPtr<OnFallbackListener> fallbackListener; + DispatchFunction([&] { processingTrack = AudioProcessingTrack::Create(graph); outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO); outputTrack->QueueSetAutoend(false); @@ -1337,54 +1343,65 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput) settings.mAgc2Forced = true; QueueApplySettings(processingTrack, listener, settings); - return graph->NotifyWhenDeviceStarted(nullptr); + fallbackListener = new OnFallbackListener(processingTrack); + processingTrack->AddListener(fallbackListener); }); RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(stream->mHasInput); - Unused << WaitFor(p); - // Set a drift factor so that we don't dont produce perfect 10ms-chunks. This - // will exercise whatever buffers are in the audio processing pipeline, and - // the bookkeeping surrounding them. - stream->SetDriftFactor(1.111); + while ( + stream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } - // Wait for a second worth of audio data. GoFaster is dispatched through a - // ControlMessage so that it is called in the first audio driver iteration. - // Otherwise the audio driver might be going very fast while the fallback - // system clock driver is still in an iteration. - DispatchFunction([&] { - processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); - }); - { - uint32_t totalFrames = 0; - WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) { - totalFrames += aFrames; - return totalFrames > static_cast<uint32_t>(graph->GraphRate()); - }); + // Wait for the AudioCallbackDriver to come into effect. + while (fallbackListener->OnFallback()) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Iterate for a second worth of audio data. + for (long frames = 0; frames < graph->GraphRate(); frames += step) { + stream->ManualDataCallback(step); } - cubeb->DontGoFaster(); // Close the input to see that no asserts go off due to bad state. DispatchFunction([&] { processingTrack->DisconnectDeviceInput(); }); - stream = WaitFor(cubeb->StreamInitEvent()); + // Dispatch the disconnect message. + ProcessEventQueue(); + // Run the disconnect message. + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + // Switch driver. + auto initPromise = TakeN(cubeb->StreamInitEvent(), 1); + EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No); + std::tie(stream) = WaitFor(initPromise).unwrap()[0]; EXPECT_FALSE(stream->mHasInput); - Unused << WaitFor( - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); })); - // Output-only. Wait for another second before unmuting. - DispatchFunction([&] { - processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); - }); - { - uint32_t totalFrames = 0; - WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) { - totalFrames += aFrames; - return totalFrames > static_cast<uint32_t>(graph->GraphRate()); - }); + while ( + stream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Wait for the new AudioCallbackDriver to come into effect. + fallbackListener->Reset(); + while (fallbackListener->OnFallback()) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Output-only. Iterate for another second before unmuting. + for (long frames = 0; frames < graph->GraphRate(); frames += step) { + stream->ManualDataCallback(step); } - cubeb->DontGoFaster(); // Re-open the input to again see that no asserts go off due to bad state. DispatchFunction([&] { @@ -1392,27 +1409,40 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput) processingTrack->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE); }); - - stream = WaitFor(cubeb->StreamInitEvent()); + // Dispatch the connect message. + ProcessEventQueue(); + // Run the connect message. + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + // Switch driver. + initPromise = TakeN(cubeb->StreamInitEvent(), 1); + EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No); + std::tie(stream) = WaitFor(initPromise).unwrap()[0]; EXPECT_TRUE(stream->mHasInput); - Unused << WaitFor( - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); })); - // Full-duplex. Wait for another second before finishing. - DispatchFunction([&] { - processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); - }); - { - uint32_t totalFrames = 0; - WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) { - totalFrames += aFrames; - return totalFrames > static_cast<uint32_t>(graph->GraphRate()); - }); + while ( + stream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Wait for the new AudioCallbackDriver to come into effect. + fallbackListener->Reset(); + while (fallbackListener->OnFallback()) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Full-duplex. Iterate for another second before finishing. + for (long frames = 0; frames < graph->GraphRate(); frames += step) { + stream->ManualDataCallback(step); } - cubeb->DontGoFaster(); // Clean up. DispatchFunction([&] { + processingTrack->RemoveListener(fallbackListener); outputTrack->RemoveAudioOutput((void*)1); outputTrack->Destroy(); port->Destroy(); @@ -1422,7 +1452,14 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput) processingTrack->Destroy(); }); - uint32_t inputRate = stream->SampleRate(); + // Dispatch the clean-up messages. + ProcessEventQueue(); + // Run the clean-up messages. + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + // Shut down driver. + EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No); + uint32_t inputFrequency = stream->InputFrequency(); uint64_t preSilenceSamples; uint32_t estimatedFreq; @@ -1431,17 +1468,12 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput) WaitFor(stream->OutputVerificationEvent()); EXPECT_EQ(estimatedFreq, inputFrequency); - std::cerr << "PreSilence: " << preSilenceSamples << std::endl; - // We buffer 10ms worth of frames in non-passthrough mode, plus up to 128 - // frames as we round up to the nearest block. See - // AudioInputProcessing::Process and DeviceInputTrack::PrcoessInput. - EXPECT_GE(preSilenceSamples, 128U + inputRate / 100); - // If the fallback system clock driver is doing a graph iteration before the - // first audio driver iteration comes in, that iteration is ignored and - // results in zeros. It takes one fallback driver iteration *after* the audio - // driver has started to complete the switch, *usually* resulting two - // 10ms-iterations of silence; sometimes only one. - EXPECT_LE(preSilenceSamples, 128U + 3 * inputRate / 100 /* 3*10ms */); + std::cerr << "PreSilence: " << preSilenceSamples << "\n"; + // We buffer 128 frames. See NativeInputTrack::NotifyInputData. + // When not in passthrough the AudioInputProcessing packetizer also buffers + // 10ms of silence, pulled in from NativeInputTrack when being run by the + // fallback SystemClockDriver. + EXPECT_EQ(preSilenceSamples, WEBAUDIO_BLOCK_SIZE + rate / 100); // The waveform from AudioGenerator starts at 0, but we don't control its // ending, so we expect a discontinuity there. Note that this check is only // for the waveform on the stream *after* re-opening the input. @@ -1467,7 +1499,7 @@ float rmsf32(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) { TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling) { - MockCubeb* cubeb = new MockCubeb(); + MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance( @@ -1481,7 +1513,8 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling) RefPtr<ProcessedMediaTrack> outputTrack; RefPtr<MediaInputPort> port; RefPtr<AudioInputProcessing> listener; - auto p = Invoke([&] { + RefPtr<OnFallbackListener> fallbackListener; + DispatchFunction([&] { processingTrack = AudioProcessingTrack::Create(graph); outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO); outputTrack->QueueSetAutoend(false); @@ -1495,32 +1528,36 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling) PRINCIPAL_HANDLE_NONE); processingTrack->GraphImpl()->AppendMessage( MakeUnique<StartInputProcessing>(processingTrack, listener)); - return graph->NotifyWhenDeviceStarted(nullptr); + fallbackListener = new OnFallbackListener(processingTrack); + processingTrack->AddListener(fallbackListener); }); + ProcessEventQueue(); + RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(stream->mHasInput); - Unused << WaitFor(p); + + while ( + stream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Wait for the AudioCallbackDriver to come into effect. + while (fallbackListener->OnFallback()) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } stream->SetOutputRecordingEnabled(true); // Wait for a second worth of audio data. - uint64_t targetPosition = graph->GraphRate(); - auto AdvanceToTargetPosition = [&] { - DispatchFunction([&] { - processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); - }); - WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) { - // Position() gives a more up-to-date indication than summing aFrames if - // multiple events are queued. - if (stream->Position() < targetPosition) { - return false; - } - cubeb->DontGoFaster(); - return true; - }); - }; - AdvanceToTargetPosition(); + const long step = graph->GraphRate() / 100; // 10ms + for (long frames = 0; frames < graph->GraphRate(); frames += step) { + stream->ManualDataCallback(step); + } const uint32_t ITERATION_COUNT = 5; uint32_t iterations = ITERATION_COUNT; @@ -1537,8 +1574,11 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling) } }); - targetPosition += graph->GraphRate(); - AdvanceToTargetPosition(); + ProcessEventQueue(); + + for (long frames = 0; frames < graph->GraphRate(); frames += step) { + stream->ManualDataCallback(step); + } } // Clean up. @@ -1548,10 +1588,18 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling) port->Destroy(); processingTrack->GraphImpl()->AppendMessage( MakeUnique<StopInputProcessing>(processingTrack, listener)); + processingTrack->RemoveListener(fallbackListener); processingTrack->DisconnectDeviceInput(); processingTrack->Destroy(); }); + ProcessEventQueue(); + + // Close the input and switch driver. + while (stream->ManualDataCallback(0) != MockCubebStream::KeepProcessing::No) { + std::cerr << "Waiting for switch...\n"; + } + uint64_t preSilenceSamples; uint32_t estimatedFreq; uint32_t nrDiscontinuities; @@ -1604,7 +1652,7 @@ TEST(TestAudioTrackGraph, SetRequestedInputChannelCount) EXPECT_EQ(track1->DeviceId().value(), device1); auto started = - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); + InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(stream1->mHasInput); @@ -1829,7 +1877,7 @@ TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged) EXPECT_EQ(track1->DeviceId().value(), nativeDevice); auto started = - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); + InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); nativeStream = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(nativeStream->mHasInput); @@ -1961,44 +2009,23 @@ TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver) const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1; RefPtr<AudioProcessingTrack> track; RefPtr<AudioInputProcessing> listener; - { - MozPromiseHolder<GenericPromise> h; - RefPtr<GenericPromise> p = h.Ensure(__func__); - - struct GuardMessage : public ControlMessage { - MozPromiseHolder<GenericPromise> mHolder; - - GuardMessage(MediaTrack* aTrack, - MozPromiseHolder<GenericPromise>&& aHolder) - : ControlMessage(aTrack), mHolder(std::move(aHolder)) {} - void Run() override { - mTrack->GraphImpl()->Dispatch(NS_NewRunnableFunction( - "TestAudioTrackGraph::SetInputChannel::Message::Resolver", - [holder = std::move(mHolder)]() mutable { - holder.Resolve(true, __func__); - })); - } - }; - - DispatchFunction([&] { - track = AudioProcessingTrack::Create(graph); - listener = new AudioInputProcessing(2); - QueueExpectIsPassThrough(track, listener); - track->SetInputProcessing(listener); - - MediaEnginePrefs settings; - settings.mChannels = 1; - QueueApplySettings(track, listener, settings); + DispatchFunction([&] { + track = AudioProcessingTrack::Create(graph); + listener = new AudioInputProcessing(2); + QueueExpectIsPassThrough(track, listener); + track->SetInputProcessing(listener); - track->GraphImpl()->AppendMessage( - MakeUnique<GuardMessage>(track, std::move(h))); - }); + MediaEnginePrefs settings; + settings.mChannels = 1; + QueueApplySettings(track, listener, settings); + }); - Unused << WaitFor(p); - } + // Wait for AudioCallbackDriver to init output-only stream. + RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); + EXPECT_FALSE(stream->mHasInput); + EXPECT_TRUE(stream->mHasOutput); // Open a full-duplex AudioCallbackDriver. - RefPtr<MediaInputPort> port; DispatchFunction([&] { track->GraphImpl()->AppendMessage( @@ -2006,22 +2033,13 @@ TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver) track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE); }); - // MediaTrackGraph will create a output-only AudioCallbackDriver in - // CheckDriver before we open an audio input above, since AudioProcessingTrack - // is a audio-type MediaTrack, so we need to wait here until the duplex - // AudioCallbackDriver is created. - RefPtr<SmartMockCubebStream> stream; - SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>( - "TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)"_ns, - [&] { - stream = WaitFor(cubeb->StreamInitEvent()); - EXPECT_TRUE(stream->mHasOutput); - return stream->mHasInput; - }); + stream = WaitFor(cubeb->StreamInitEvent()); + EXPECT_TRUE(stream->mHasInput); + EXPECT_TRUE(stream->mHasOutput); EXPECT_EQ(stream->InputChannels(), 1U); Unused << WaitFor( - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); })); + InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); })); // Clean up. DispatchFunction([&] { @@ -2257,7 +2275,7 @@ TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack) EXPECT_EQ(track1->DeviceId().value(), device1); auto started = - Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); + InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); }); RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent()); EXPECT_TRUE(stream1->mHasInput); @@ -2350,23 +2368,6 @@ TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack) std::cerr << "No native input now" << std::endl; } -class OnFallbackListener : public MediaTrackListener { - const RefPtr<MediaTrack> mTrack; - Atomic<bool> mOnFallback{true}; - - public: - explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {} - - bool OnFallback() { return mOnFallback; } - - void NotifyOutput(MediaTrackGraph*, TrackTime) override { - if (auto* ad = - mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) { - mOnFallback = ad->OnFallback(); - } - } -}; - void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate, float aDriftFactor, uint32_t aRunTimeSeconds = 10, uint32_t aNumExpectedUnderruns = 0) { @@ -2409,6 +2410,13 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate, RefPtr<SmartMockCubebStream> inputStream = WaitFor(cubeb->StreamInitEvent()); + while ( + inputStream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + // Wait for the primary AudioCallbackDriver to come into effect. while (primaryFallbackListener->OnFallback()) { EXPECT_EQ(inputStream->ManualDataCallback(0), @@ -2441,6 +2449,13 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate, RefPtr<SmartMockCubebStream> partnerStream = WaitFor(cubeb->StreamInitEvent()); + while ( + partnerStream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + // Process the CrossGraphTransmitter on the primary graph. EXPECT_EQ(inputStream->ManualDataCallback(0), MockCubebStream::KeepProcessing::Yes); @@ -2453,8 +2468,7 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate, } DispatchFunction([&] { receiver->RemoveListener(partnerFallbackListener); }); - while (NS_ProcessNextEvent(nullptr, false)) { - } + ProcessEventQueue(); nsIThread* currentThread = NS_GetCurrentThread(); cubeb_state inputState = CUBEB_STATE_STARTED; @@ -2499,8 +2513,7 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate, processingTrack->Destroy(); }); - while (NS_ProcessNextEvent(nullptr, false)) { - } + ProcessEventQueue(); EXPECT_EQ(inputStream->ManualDataCallback(0), MockCubebStream::KeepProcessing::Yes); @@ -2816,6 +2829,221 @@ TEST(TestAudioInputProcessing, ClockDriftExpectation) } #endif // MOZ_WEBRTC +TEST(TestAudioTrackGraph, PlatformProcessing) +{ + constexpr cubeb_input_processing_params allParams = + CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION | + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION | + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION; + MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual); + cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK); + CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext()); + + MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance( + MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, + CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false), + nullptr, GetMainThreadSerialEventTarget()); + + const CubebUtils::AudioDeviceID device = (CubebUtils::AudioDeviceID)1; + + // Set up mock listener. + RefPtr<MockAudioDataListener> listener = MakeRefPtr<MockAudioDataListener>(); + EXPECT_CALL(*listener, IsVoiceInput).WillRepeatedly(Return(true)); + EXPECT_CALL(*listener, RequestedInputChannelCount).WillRepeatedly(Return(1)); + EXPECT_CALL(*listener, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION)); + EXPECT_CALL(*listener, Disconnect); + + // Expectations. + const Result<cubeb_input_processing_params, int> notSupportedResult( + Err(CUBEB_ERROR_NOT_SUPPORTED)); + const Result<cubeb_input_processing_params, int> errorResult( + Err(CUBEB_ERROR)); + const Result<cubeb_input_processing_params, int> noneResult( + CUBEB_INPUT_PROCESSING_PARAM_NONE); + const Result<cubeb_input_processing_params, int> echoResult( + CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION); + const Result<cubeb_input_processing_params, int> noiseResult( + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION); + Atomic<int> numProcessingParamsResults(0); + { + InSequence s; + // On first driver start. + EXPECT_CALL(*listener, + NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, + Eq(std::ref(echoResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + // After requesting something else. + EXPECT_CALL(*listener, + NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION, + Eq(std::ref(noiseResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + // After error request. + EXPECT_CALL(*listener, + NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, + Eq(std::ref(errorResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + // After requesting None. + EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_NONE, + Eq(std::ref(noneResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + // After driver switch. + EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_NONE, + Eq(std::ref(noneResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + // After requesting something not supported. + EXPECT_CALL(*listener, + NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, + Eq(std::ref(noneResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + // After requesting something with backend not supporting processing params. + EXPECT_CALL(*listener, + NotifySetRequestedInputProcessingParamsResult( + graph, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION, + Eq(std::ref(notSupportedResult)))) + .WillOnce([&] { ++numProcessingParamsResults; }); + } + + // Open a device. + RefPtr<TestDeviceInputConsumerTrack> track; + RefPtr<OnFallbackListener> fallbackListener; + DispatchFunction([&] { + track = TestDeviceInputConsumerTrack::Create(graph); + track->ConnectDeviceInput(device, listener, PRINCIPAL_HANDLE_NONE); + fallbackListener = new OnFallbackListener(track); + track->AddListener(fallbackListener); + }); + + RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent()); + EXPECT_TRUE(stream->mHasInput); + EXPECT_TRUE(stream->mHasOutput); + EXPECT_EQ(stream->InputChannels(), 1U); + EXPECT_EQ(stream->GetInputDeviceID(), device); + + while ( + stream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + const auto waitForResult = [&](int aNumResult) { + while (numProcessingParamsResults < aNumResult) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + NS_ProcessNextEvent(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }; + + // Wait for the AudioCallbackDriver to come into effect. + while (fallbackListener->OnFallback()) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Wait for the first result after driver creation. + waitForResult(1); + + // Request new processing params. + EXPECT_CALL(*listener, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION)); + waitForResult(2); + + // Test with returning error on new request. + cubeb->SetInputProcessingApplyRv(CUBEB_ERROR); + EXPECT_CALL(*listener, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION)); + waitForResult(3); + + // Test unsetting all params. + cubeb->SetInputProcessingApplyRv(CUBEB_OK); + EXPECT_CALL(*listener, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE)); + waitForResult(4); + + // Switch driver. + EXPECT_CALL(*listener, RequestedInputChannelCount).WillRepeatedly(Return(2)); + DispatchFunction([&] { + track->QueueControlMessageWithNoShutdown( + [&] { graph->ReevaluateInputDevice(device); }); + }); + ProcessEventQueue(); + // Process the reevaluation message. + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + // Perform the switch. + auto initPromise = TakeN(cubeb->StreamInitEvent(), 1); + EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No); + std::tie(stream) = WaitFor(initPromise).unwrap()[0]; + EXPECT_TRUE(stream->mHasInput); + EXPECT_TRUE(stream->mHasOutput); + EXPECT_EQ(stream->InputChannels(), 2U); + EXPECT_EQ(stream->GetInputDeviceID(), device); + + while ( + stream->State() + .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; }) + .valueOr(true)) { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Wait for the new AudioCallbackDriver to come into effect. + fallbackListener->Reset(); + while (fallbackListener->OnFallback()) { + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + + // Wait for the first result after driver creation. + waitForResult(5); + + // Test requesting something not supported. + cubeb->SetSupportedInputProcessingParams( + CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION | + CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL | + CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION, + CUBEB_OK); + EXPECT_CALL(*listener, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION)); + waitForResult(6); + + // Test requesting something when unsupported by backend. + cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE, + CUBEB_ERROR_NOT_SUPPORTED); + EXPECT_CALL(*listener, RequestedInputProcessingParams) + .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION)); + waitForResult(7); + + // Clean up. + DispatchFunction([&] { + track->RemoveListener(fallbackListener); + track->Destroy(); + }); + ProcessEventQueue(); + // Process the destroy message. + EXPECT_EQ(stream->ManualDataCallback(0), + MockCubebStream::KeepProcessing::Yes); + // Shut down. + EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No); + RefPtr<SmartMockCubebStream> destroyedStream = + WaitFor(cubeb->StreamDestroyEvent()); + EXPECT_EQ(destroyedStream.get(), stream.get()); + { + NativeInputTrack* native = graph->GetNativeInputTrackMainThread(); + ASSERT_TRUE(!native); + } +} + #undef Invoke #undef DispatchFunction #undef DispatchMethod |