summaryrefslogtreecommitdiffstats
path: root/dom/media/gtest/TestAudioTrackGraph.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/gtest/TestAudioTrackGraph.cpp')
-rw-r--r--dom/media/gtest/TestAudioTrackGraph.cpp740
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