Backed out changeset 2bbc01486e2f (bug 1910796) for causing multiple failures. CLOSED...
[gecko.git] / dom / media / gtest / TestAudioTrackGraph.cpp
blobbf018d9673f4d77531d29c60728c92e814e77a74
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "MediaTrackGraphImpl.h"
8 #include "gmock/gmock.h"
9 #include "gtest/gtest-printers.h"
10 #include "gtest/gtest.h"
12 #include "CrossGraphPort.h"
13 #include "DeviceInputTrack.h"
14 #ifdef MOZ_WEBRTC
15 # include "MediaEngineWebRTCAudio.h"
16 #endif // MOZ_WEBRTC
17 #include "MockCubeb.h"
18 #include "mozilla/gtest/WaitFor.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/SpinEventLoopUntil.h"
21 #include "WavDumper.h"
23 using namespace mozilla;
24 using testing::AtLeast;
25 using testing::Eq;
26 using testing::InSequence;
27 using testing::MockFunction;
28 using testing::Return;
29 using testing::StrEq;
30 using testing::StrictMock;
32 // Short-hand for InvokeAsync on the current thread.
33 #define InvokeAsync(f) InvokeAsync(GetCurrentSerialEventTarget(), __func__, f)
35 // Short-hand for DispatchToCurrentThread with a function.
36 #define DispatchFunction(f) \
37 NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
39 // Short-hand for DispatchToCurrentThread with a method with arguments
40 #define DispatchMethod(t, m, args...) \
41 NS_DispatchToCurrentThread(NewRunnableMethod(__func__, t, m, ##args))
43 // Short-hand for draining the current threads event queue, i.e. processing
44 // those runnables dispatched per above.
45 #define ProcessEventQueue() \
46 while (NS_ProcessNextEvent(nullptr, false)) { \
49 namespace {
50 #ifdef MOZ_WEBRTC
52 * Common ControlMessages
54 struct StartInputProcessing : public ControlMessage {
55 const RefPtr<AudioProcessingTrack> mProcessingTrack;
56 const RefPtr<AudioInputProcessing> mInputProcessing;
58 StartInputProcessing(AudioProcessingTrack* aTrack,
59 AudioInputProcessing* aInputProcessing)
60 : ControlMessage(aTrack),
61 mProcessingTrack(aTrack),
62 mInputProcessing(aInputProcessing) {}
63 void Run() override { mInputProcessing->Start(mTrack->Graph()); }
66 struct StopInputProcessing : public ControlMessage {
67 const RefPtr<AudioInputProcessing> mInputProcessing;
69 explicit StopInputProcessing(AudioProcessingTrack* aTrack,
70 AudioInputProcessing* aInputProcessing)
71 : ControlMessage(aTrack), mInputProcessing(aInputProcessing) {}
72 void Run() override { mInputProcessing->Stop(mTrack->Graph()); }
75 void QueueApplySettings(AudioProcessingTrack* aTrack,
76 AudioInputProcessing* aInputProcessing,
77 const MediaEnginePrefs& aSettings) {
78 aTrack->QueueControlMessageWithNoShutdown(
79 [inputProcessing = RefPtr{aInputProcessing}, aSettings,
80 // If the track is not connected to a device then the particular
81 // AudioDeviceID (nullptr) passed to ReevaluateInputDevice() is not
82 // important.
83 deviceId = aTrack->DeviceId().valueOr(nullptr),
84 graph = aTrack->Graph()] {
85 inputProcessing->ApplySettings(graph, deviceId, aSettings);
86 });
89 void QueueExpectIsPassThrough(AudioProcessingTrack* aTrack,
90 AudioInputProcessing* aInputProcessing) {
91 aTrack->QueueControlMessageWithNoShutdown(
92 [inputProcessing = RefPtr{aInputProcessing}, graph = aTrack->Graph()] {
93 EXPECT_EQ(inputProcessing->IsPassThrough(graph), true);
94 });
96 #endif // MOZ_WEBRTC
98 class GoFaster : public ControlMessage {
99 MockCubeb* mCubeb;
101 public:
102 explicit GoFaster(MockCubeb* aCubeb)
103 : ControlMessage(nullptr), mCubeb(aCubeb) {}
104 void Run() override { mCubeb->GoFaster(); }
107 struct StartNonNativeInput : public ControlMessage {
108 const RefPtr<NonNativeInputTrack> mInputTrack;
109 RefPtr<AudioInputSource> mInputSource;
111 StartNonNativeInput(NonNativeInputTrack* aInputTrack,
112 RefPtr<AudioInputSource>&& aInputSource)
113 : ControlMessage(aInputTrack),
114 mInputTrack(aInputTrack),
115 mInputSource(std::move(aInputSource)) {}
116 void Run() override { mInputTrack->StartAudio(std::move(mInputSource)); }
119 struct StopNonNativeInput : public ControlMessage {
120 const RefPtr<NonNativeInputTrack> mInputTrack;
122 explicit StopNonNativeInput(NonNativeInputTrack* aInputTrack)
123 : ControlMessage(aInputTrack), mInputTrack(aInputTrack) {}
124 void Run() override { mInputTrack->StopAudio(); }
127 // Helper for detecting when fallback driver has been switched away, for use
128 // with RunningMode::Manual.
129 class OnFallbackListener : public MediaTrackListener {
130 const RefPtr<MediaTrack> mTrack;
131 Atomic<bool> mOnFallback{true};
133 public:
134 explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {}
136 void Reset() { mOnFallback = true; }
137 bool OnFallback() { return mOnFallback; }
139 void NotifyOutput(MediaTrackGraph*, TrackTime) override {
140 if (auto* ad =
141 mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) {
142 mOnFallback = ad->OnFallback();
147 class MockAudioDataListener : public AudioDataListener {
148 protected:
149 ~MockAudioDataListener() = default;
151 public:
152 MockAudioDataListener() = default;
154 MOCK_METHOD(uint32_t, RequestedInputChannelCount, (MediaTrackGraph*),
155 (const));
156 MOCK_METHOD(cubeb_input_processing_params, RequestedInputProcessingParams,
157 (MediaTrackGraph*), (const));
158 MOCK_METHOD(bool, IsVoiceInput, (MediaTrackGraph*), (const));
159 MOCK_METHOD(void, DeviceChanged, (MediaTrackGraph*));
160 MOCK_METHOD(void, Disconnect, (MediaTrackGraph*));
161 MOCK_METHOD(void, NotifySetRequestedInputProcessingParams,
162 (MediaTrackGraph*, int, cubeb_input_processing_params));
163 MOCK_METHOD(void, NotifySetRequestedInputProcessingParamsResult,
164 (MediaTrackGraph*, int,
165 (const Result<cubeb_input_processing_params, int>&)));
167 } // namespace
170 * The set of tests here are a bit special. In part because they're async and
171 * depends on the graph thread to function. In part because they depend on main
172 * thread stable state to send messages to the graph.
174 * Any message sent from the main thread to the graph through the graph's
175 * various APIs are scheduled to run in stable state. Stable state occurs after
176 * a task in the main thread eventloop has run to completion.
178 * Since gtests are generally sync and on main thread, calling into the graph
179 * may schedule a stable state runnable but with no task in the eventloop to
180 * trigger stable state. Therefore care must be taken to always call into the
181 * graph from a task, typically via InvokeAsync or a dispatch to main thread.
184 TEST(TestAudioTrackGraph, DifferentDeviceIDs)
186 MockCubeb* cubeb = new MockCubeb();
187 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
189 MediaTrackGraph* g1 = MediaTrackGraphImpl::GetInstance(
190 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
191 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
192 /*OutputDeviceID*/ nullptr, GetMainThreadSerialEventTarget());
194 MediaTrackGraph* g2 = MediaTrackGraphImpl::GetInstance(
195 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
196 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
197 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
198 GetMainThreadSerialEventTarget());
200 MediaTrackGraph* g1_2 = MediaTrackGraphImpl::GetInstance(
201 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
202 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
203 /*OutputDeviceID*/ nullptr, GetMainThreadSerialEventTarget());
205 MediaTrackGraph* g2_2 = MediaTrackGraphImpl::GetInstance(
206 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
207 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
208 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
209 GetMainThreadSerialEventTarget());
211 EXPECT_NE(g1, g2) << "Different graphs due to different device ids";
212 EXPECT_EQ(g1, g1_2) << "Same graphs for same device ids";
213 EXPECT_EQ(g2, g2_2) << "Same graphs for same device ids";
215 for (MediaTrackGraph* g : {g1, g2}) {
216 // Dummy track to make graph rolling. Add it and remove it to remove the
217 // graph from the global hash table and let it shutdown.
219 using SourceTrackPromise = MozPromise<SourceMediaTrack*, nsresult, true>;
220 auto p = InvokeAsync([g] {
221 return SourceTrackPromise::CreateAndResolve(
222 g->CreateSourceTrack(MediaSegment::AUDIO), __func__);
225 WaitFor(cubeb->StreamInitEvent());
226 RefPtr<SourceMediaTrack> dummySource = WaitFor(p).unwrap();
228 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
230 WaitFor(cubeb->StreamDestroyEvent());
234 TEST(TestAudioTrackGraph, SetOutputDeviceID)
236 MockCubeb* cubeb = new MockCubeb();
237 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
239 // Set the output device id in GetInstance method confirm that it is the one
240 // used in cubeb_stream_init.
241 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
242 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
243 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
244 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(2),
245 GetMainThreadSerialEventTarget());
247 // Dummy track to make graph rolling. Add it and remove it to remove the
248 // graph from the global hash table and let it shutdown.
249 RefPtr<SourceMediaTrack> dummySource;
250 DispatchFunction(
251 [&] { dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO); });
253 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
255 EXPECT_EQ(stream->GetOutputDeviceID(), reinterpret_cast<cubeb_devid>(2))
256 << "After init confirm the expected output device id";
258 // Test has finished, destroy the track to shutdown the MTG.
259 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
260 WaitFor(cubeb->StreamDestroyEvent());
263 TEST(TestAudioTrackGraph, StreamName)
265 MockCubeb* cubeb = new MockCubeb();
266 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
268 // Initialize a graph with a system thread driver to check that the stream
269 // name survives the driver switch.
270 MediaTrackGraphImpl* graph = MediaTrackGraphImpl::GetInstance(
271 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
272 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
273 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
274 GetMainThreadSerialEventTarget());
275 nsLiteralCString name1("name1");
276 graph->CurrentDriver()->SetStreamName(name1);
278 // Dummy track to start the graph rolling and switch to an
279 // AudioCallbackDriver.
280 RefPtr<SourceMediaTrack> dummySource;
281 DispatchFunction(
282 [&] { dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO); });
284 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
285 EXPECT_STREQ(stream->StreamName(), name1.get());
287 // Test a name change on an existing stream.
288 nsLiteralCString name2("name2");
289 DispatchFunction([&] {
290 graph->QueueControlMessageWithNoShutdown(
291 [&] { graph->CurrentDriver()->SetStreamName(name2); });
293 nsCString name = WaitFor(stream->NameSetEvent());
294 EXPECT_EQ(name, name2);
296 // Test has finished. Destroy the track to shutdown the MTG.
297 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
298 WaitFor(cubeb->StreamDestroyEvent());
301 TEST(TestAudioTrackGraph, NotifyDeviceStarted)
303 MockCubeb* cubeb = new MockCubeb();
304 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
306 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
307 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
308 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
309 nullptr, GetMainThreadSerialEventTarget());
311 RefPtr<SourceMediaTrack> dummySource;
312 Unused << WaitFor(InvokeAsync([&] {
313 // Dummy track to make graph rolling. Add it and remove it to remove the
314 // graph from the global hash table and let it shutdown.
315 dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO);
317 return graph->NotifyWhenDeviceStarted(nullptr);
318 }));
321 MediaTrackGraphImpl* graph = dummySource->GraphImpl();
322 MonitorAutoLock lock(graph->GetMonitor());
323 EXPECT_TRUE(graph->CurrentDriver()->AsAudioCallbackDriver());
324 EXPECT_TRUE(graph->CurrentDriver()->ThreadRunning());
327 // Test has finished, destroy the track to shutdown the MTG.
328 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
329 WaitFor(cubeb->StreamDestroyEvent());
332 TEST(TestAudioTrackGraph, NonNativeInputTrackStartAndStop)
334 MockCubeb* cubeb = new MockCubeb();
335 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
337 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
338 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
339 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
340 nullptr, GetMainThreadSerialEventTarget());
342 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
344 // Add a NonNativeInputTrack to graph, making graph create an output-only
345 // AudioCallbackDriver since NonNativeInputTrack is an audio-type MediaTrack.
346 RefPtr<NonNativeInputTrack> track;
347 DispatchFunction([&] {
348 track = new NonNativeInputTrack(graph->GraphRate(), deviceId,
349 PRINCIPAL_HANDLE_NONE);
350 graph->AddTrack(track);
353 RefPtr<SmartMockCubebStream> driverStream = WaitFor(cubeb->StreamInitEvent());
354 EXPECT_FALSE(driverStream->mHasInput);
355 EXPECT_TRUE(driverStream->mHasOutput);
357 // Main test below:
359 const AudioInputSource::Id sourceId = 1;
360 const uint32_t channels = 2;
361 const TrackRate rate = 48000;
363 // Start and stop the audio in NonNativeInputTrack.
365 struct DeviceInfo {
366 uint32_t mChannelCount;
367 AudioInputType mType;
369 using DeviceQueryPromise =
370 MozPromise<DeviceInfo, nsresult, /* IsExclusive = */ true>;
372 struct DeviceQueryMessage : public ControlMessage {
373 const NonNativeInputTrack* mInputTrack;
374 MozPromiseHolder<DeviceQueryPromise> mHolder;
376 DeviceQueryMessage(NonNativeInputTrack* aInputTrack,
377 MozPromiseHolder<DeviceQueryPromise>&& aHolder)
378 : ControlMessage(aInputTrack),
379 mInputTrack(aInputTrack),
380 mHolder(std::move(aHolder)) {}
381 void Run() override {
382 DeviceInfo info = {mInputTrack->NumberOfChannels(),
383 mInputTrack->DevicePreference()};
384 // mHolder.Resolve(info, __func__);
385 mTrack->GraphImpl()->Dispatch(NS_NewRunnableFunction(
386 "TestAudioTrackGraph::DeviceQueryMessage",
387 [holder = std::move(mHolder), devInfo = info]() mutable {
388 holder.Resolve(devInfo, __func__);
389 }));
393 // No input channels and device preference before start.
395 MozPromiseHolder<DeviceQueryPromise> h;
396 RefPtr<DeviceQueryPromise> p = h.Ensure(__func__);
397 DispatchFunction([&] {
398 track->GraphImpl()->AppendMessage(
399 MakeUnique<DeviceQueryMessage>(track.get(), std::move(h)));
401 Result<DeviceInfo, nsresult> r = WaitFor(p);
402 ASSERT_TRUE(r.isOk());
403 DeviceInfo info = r.unwrap();
405 EXPECT_EQ(info.mChannelCount, 0U);
406 EXPECT_EQ(info.mType, AudioInputType::Unknown);
409 DispatchFunction([&] {
410 track->GraphImpl()->AppendMessage(MakeUnique<StartNonNativeInput>(
411 track.get(), MakeRefPtr<AudioInputSource>(
412 MakeRefPtr<AudioInputSourceListener>(track.get()),
413 sourceId, deviceId, channels, true /* voice */,
414 PRINCIPAL_HANDLE_NONE, rate, graph->GraphRate())));
416 RefPtr<SmartMockCubebStream> nonNativeStream =
417 WaitFor(cubeb->StreamInitEvent());
418 EXPECT_TRUE(nonNativeStream->mHasInput);
419 EXPECT_FALSE(nonNativeStream->mHasOutput);
420 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), deviceId);
421 EXPECT_EQ(nonNativeStream->InputChannels(), channels);
422 EXPECT_EQ(nonNativeStream->SampleRate(), static_cast<uint32_t>(rate));
424 // Input channels and device preference should be set after start.
426 MozPromiseHolder<DeviceQueryPromise> h;
427 RefPtr<DeviceQueryPromise> p = h.Ensure(__func__);
428 DispatchFunction([&] {
429 track->GraphImpl()->AppendMessage(
430 MakeUnique<DeviceQueryMessage>(track.get(), std::move(h)));
432 Result<DeviceInfo, nsresult> r = WaitFor(p);
433 ASSERT_TRUE(r.isOk());
434 DeviceInfo info = r.unwrap();
436 EXPECT_EQ(info.mChannelCount, channels);
437 EXPECT_EQ(info.mType, AudioInputType::Voice);
440 Unused << WaitFor(nonNativeStream->FramesProcessedEvent());
442 DispatchFunction([&] {
443 track->GraphImpl()->AppendMessage(
444 MakeUnique<StopNonNativeInput>(track.get()));
446 RefPtr<SmartMockCubebStream> destroyedStream =
447 WaitFor(cubeb->StreamDestroyEvent());
448 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
450 // No input channels and device preference after stop.
452 MozPromiseHolder<DeviceQueryPromise> h;
453 RefPtr<DeviceQueryPromise> p = h.Ensure(__func__);
454 DispatchFunction([&] {
455 track->GraphImpl()->AppendMessage(
456 MakeUnique<DeviceQueryMessage>(track.get(), std::move(h)));
458 Result<DeviceInfo, nsresult> r = WaitFor(p);
459 ASSERT_TRUE(r.isOk());
460 DeviceInfo info = r.unwrap();
462 EXPECT_EQ(info.mChannelCount, 0U);
463 EXPECT_EQ(info.mType, AudioInputType::Unknown);
467 // Make sure the NonNativeInputTrack can restart and stop its audio.
469 DispatchFunction([&] {
470 track->GraphImpl()->AppendMessage(MakeUnique<StartNonNativeInput>(
471 track.get(), MakeRefPtr<AudioInputSource>(
472 MakeRefPtr<AudioInputSourceListener>(track.get()),
473 sourceId, deviceId, channels, true,
474 PRINCIPAL_HANDLE_NONE, rate, graph->GraphRate())));
476 RefPtr<SmartMockCubebStream> nonNativeStream =
477 WaitFor(cubeb->StreamInitEvent());
478 EXPECT_TRUE(nonNativeStream->mHasInput);
479 EXPECT_FALSE(nonNativeStream->mHasOutput);
480 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), deviceId);
481 EXPECT_EQ(nonNativeStream->InputChannels(), channels);
482 EXPECT_EQ(nonNativeStream->SampleRate(), static_cast<uint32_t>(rate));
484 Unused << WaitFor(nonNativeStream->FramesProcessedEvent());
486 DispatchFunction([&] {
487 track->GraphImpl()->AppendMessage(
488 MakeUnique<StopNonNativeInput>(track.get()));
490 RefPtr<SmartMockCubebStream> destroyedStream =
491 WaitFor(cubeb->StreamDestroyEvent());
492 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
496 // Clean up.
497 DispatchFunction([&] { track->Destroy(); });
498 RefPtr<SmartMockCubebStream> destroyedStream =
499 WaitFor(cubeb->StreamDestroyEvent());
500 EXPECT_EQ(destroyedStream.get(), driverStream.get());
503 TEST(TestAudioTrackGraph, NonNativeInputTrackErrorCallback)
505 MockCubeb* cubeb = new MockCubeb();
506 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
508 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
509 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
510 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
511 nullptr, GetMainThreadSerialEventTarget());
513 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
515 // Add a NonNativeInputTrack to graph, making graph create an output-only
516 // AudioCallbackDriver since NonNativeInputTrack is an audio-type MediaTrack.
517 RefPtr<NonNativeInputTrack> track;
518 DispatchFunction([&] {
519 track = new NonNativeInputTrack(graph->GraphRate(), deviceId,
520 PRINCIPAL_HANDLE_NONE);
521 graph->AddTrack(track);
524 RefPtr<SmartMockCubebStream> driverStream = WaitFor(cubeb->StreamInitEvent());
525 EXPECT_FALSE(driverStream->mHasInput);
526 EXPECT_TRUE(driverStream->mHasOutput);
528 // Main test below:
530 const AudioInputSource::Id sourceId = 1;
531 const uint32_t channels = 2;
532 const TrackRate rate = 48000;
534 // Launch and start the non-native audio stream.
535 DispatchFunction([&] {
536 track->GraphImpl()->AppendMessage(MakeUnique<StartNonNativeInput>(
537 track.get(), MakeRefPtr<AudioInputSource>(
538 MakeRefPtr<AudioInputSourceListener>(track.get()),
539 sourceId, deviceId, channels, true,
540 PRINCIPAL_HANDLE_NONE, rate, graph->GraphRate())));
542 RefPtr<SmartMockCubebStream> nonNativeStream =
543 WaitFor(cubeb->StreamInitEvent());
544 EXPECT_TRUE(nonNativeStream->mHasInput);
545 EXPECT_FALSE(nonNativeStream->mHasOutput);
546 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), deviceId);
547 EXPECT_EQ(nonNativeStream->InputChannels(), channels);
548 EXPECT_EQ(nonNativeStream->SampleRate(), static_cast<uint32_t>(rate));
550 // Make sure the audio stream is running.
551 Unused << WaitFor(nonNativeStream->FramesProcessedEvent());
553 // Force an error. This results in the audio stream destroying.
554 DispatchFunction([&] { nonNativeStream->ForceError(); });
555 WaitFor(nonNativeStream->ErrorForcedEvent());
557 RefPtr<SmartMockCubebStream> destroyedStream =
558 WaitFor(cubeb->StreamDestroyEvent());
559 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
562 // Make sure it's ok to call audio stop again.
563 DispatchFunction([&] {
564 track->GraphImpl()->AppendMessage(
565 MakeUnique<StopNonNativeInput>(track.get()));
568 // Clean up.
569 DispatchFunction([&] { track->Destroy(); });
570 RefPtr<SmartMockCubebStream> destroyedStream =
571 WaitFor(cubeb->StreamDestroyEvent());
572 EXPECT_EQ(destroyedStream.get(), driverStream.get());
575 class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
576 public:
577 static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
578 MOZ_RELEASE_ASSERT(NS_IsMainThread());
579 TestDeviceInputConsumerTrack* track =
580 new TestDeviceInputConsumerTrack(aGraph->GraphRate());
581 aGraph->AddTrack(track);
582 return track;
585 void Destroy() {
586 MOZ_RELEASE_ASSERT(NS_IsMainThread());
587 DisconnectDeviceInput();
588 DeviceInputConsumerTrack::Destroy();
591 void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override {
592 MOZ_RELEASE_ASSERT(aFrom < aTo);
594 if (mInputs.IsEmpty()) {
595 GetData<AudioSegment>()->AppendNullData(aTo - aFrom);
596 } else {
597 MOZ_RELEASE_ASSERT(mInputs.Length() == 1);
598 AudioSegment data;
599 DeviceInputConsumerTrack::GetInputSourceData(data, aFrom, aTo);
600 GetData<AudioSegment>()->AppendFrom(&data);
604 uint32_t NumberOfChannels() const override {
605 if (mInputs.IsEmpty()) {
606 return 0;
608 DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
609 MOZ_RELEASE_ASSERT(t);
610 return t->NumberOfChannels();
613 private:
614 explicit TestDeviceInputConsumerTrack(TrackRate aSampleRate)
615 : DeviceInputConsumerTrack(aSampleRate) {}
618 TEST(TestAudioTrackGraph, DeviceChangedCallback)
620 MockCubeb* cubeb = new MockCubeb();
621 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
623 MediaTrackGraph* graphImpl = MediaTrackGraphImpl::GetInstance(
624 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
625 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
626 nullptr, GetMainThreadSerialEventTarget());
628 class TestAudioDataListener : public StrictMock<MockAudioDataListener> {
629 public:
630 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) {
631 EXPECT_CALL(*this, RequestedInputChannelCount)
632 .WillRepeatedly(Return(aChannelCount));
633 EXPECT_CALL(*this, RequestedInputProcessingParams)
634 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
635 EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice));
637 InSequence s;
638 EXPECT_CALL(*this, DeviceChanged);
639 EXPECT_CALL(*this, Disconnect);
643 private:
644 ~TestAudioDataListener() = default;
647 // Create a full-duplex AudioCallbackDriver by creating a NativeInputTrack.
648 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
649 RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
650 RefPtr<TestDeviceInputConsumerTrack> track1 =
651 TestDeviceInputConsumerTrack::Create(graphImpl);
652 track1->ConnectDeviceInput(device1, listener1.get(), PRINCIPAL_HANDLE_NONE);
654 EXPECT_TRUE(track1->ConnectedToNativeDevice());
655 EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
656 auto started =
657 InvokeAsync([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
658 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
659 EXPECT_TRUE(stream1->mHasInput);
660 EXPECT_TRUE(stream1->mHasOutput);
661 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
662 Unused << WaitFor(started);
664 // Create a NonNativeInputTrack, and make sure its DeviceChangeCallback works.
665 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
666 RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, true);
667 RefPtr<TestDeviceInputConsumerTrack> track2 =
668 TestDeviceInputConsumerTrack::Create(graphImpl);
669 track2->ConnectDeviceInput(device2, listener2.get(), PRINCIPAL_HANDLE_NONE);
671 EXPECT_FALSE(track2->ConnectedToNativeDevice());
672 EXPECT_TRUE(track2->ConnectedToNonNativeDevice());
673 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
674 EXPECT_TRUE(stream2->mHasInput);
675 EXPECT_FALSE(stream2->mHasOutput);
676 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
678 // Produce a device-changed event for the NonNativeInputTrack.
679 DispatchFunction([&] { stream2->ForceDeviceChanged(); });
680 WaitFor(stream2->DeviceChangeForcedEvent());
682 // Produce a device-changed event for the NativeInputTrack.
683 DispatchFunction([&] { stream1->ForceDeviceChanged(); });
684 WaitFor(stream1->DeviceChangeForcedEvent());
686 // Destroy the NonNativeInputTrack.
687 DispatchFunction([&] {
688 track2->DisconnectDeviceInput();
689 track2->Destroy();
691 RefPtr<SmartMockCubebStream> destroyedStream =
692 WaitFor(cubeb->StreamDestroyEvent());
693 EXPECT_EQ(destroyedStream.get(), stream2.get());
695 // Destroy the NativeInputTrack.
696 DispatchFunction([&] {
697 track1->DisconnectDeviceInput();
698 track1->Destroy();
700 destroyedStream = WaitFor(cubeb->StreamDestroyEvent());
701 EXPECT_EQ(destroyedStream.get(), stream1.get());
704 // The native audio stream (a.k.a. GraphDriver) and the non-native audio stream
705 // should always be the same as the max requested input channel of its paired
706 // DeviceInputTracks. This test checks if the audio stream paired with the
707 // DeviceInputTrack will follow the max requested input channel or not.
709 // The main focus for this test is to make sure DeviceInputTrack::OpenAudio and
710 // ::CloseAudio works as what we expect. Besides, This test also confirms
711 // MediaTrackGraph::ReevaluateInputDevice works correctly by using a
712 // test-only AudioDataListener.
714 // This test is pretty similar to RestartAudioIfProcessingMaxChannelCountChanged
715 // below, which tests the same thing but using AudioProcessingTrack.
716 // AudioProcessingTrack is the consumer of the DeviceInputTrack used in wild.
717 // It has its own customized AudioDataListener. However, it only tests when
718 // MOZ_WEBRTC is defined.
719 TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged)
721 MockCubeb* cubeb = new MockCubeb();
722 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
723 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
724 Unused << unforcer;
726 MediaTrackGraph* graphImpl = MediaTrackGraphImpl::GetInstance(
727 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
728 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
729 nullptr, GetMainThreadSerialEventTarget());
731 // A test-only AudioDataListener that simulates AudioInputProcessing's setter
732 // and getter for the input channel count.
733 class TestAudioDataListener : public StrictMock<MockAudioDataListener> {
734 public:
735 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) {
736 EXPECT_CALL(*this, RequestedInputChannelCount)
737 .WillRepeatedly(Return(aChannelCount));
738 EXPECT_CALL(*this, RequestedInputProcessingParams)
739 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
740 EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice));
741 EXPECT_CALL(*this, Disconnect);
743 // Main thread API
744 void SetInputChannelCount(MediaTrackGraph* aGraph,
745 CubebUtils::AudioDeviceID aDevice,
746 uint32_t aChannelCount) {
747 MOZ_RELEASE_ASSERT(NS_IsMainThread());
748 static_cast<MediaTrackGraphImpl*>(aGraph)
749 ->QueueControlMessageWithNoShutdown(
750 [this, self = RefPtr(this), aGraph, aDevice, aChannelCount] {
751 EXPECT_CALL(*this, RequestedInputChannelCount)
752 .WillRepeatedly(Return(aChannelCount));
753 aGraph->ReevaluateInputDevice(aDevice);
757 private:
758 ~TestAudioDataListener() = default;
761 // Request a new input channel count and expect to have a new stream.
762 auto setNewChannelCount = [&](const RefPtr<TestAudioDataListener>& aListener,
763 RefPtr<SmartMockCubebStream>& aStream,
764 uint32_t aChannelCount) {
765 ASSERT_TRUE(!!aListener);
766 ASSERT_TRUE(!!aStream);
767 ASSERT_TRUE(aStream->mHasInput);
768 ASSERT_NE(aChannelCount, 0U);
770 const CubebUtils::AudioDeviceID device = aStream->GetInputDeviceID();
772 bool destroyed = false;
773 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
774 AbstractThread::GetCurrent(),
775 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
776 destroyed = aDestroyed.get() == aStream.get();
779 RefPtr<SmartMockCubebStream> newStream;
780 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
781 AbstractThread::GetCurrent(),
782 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
783 newStream = aCreated;
786 DispatchFunction([&] {
787 aListener->SetInputChannelCount(graphImpl, device, aChannelCount);
790 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
791 "TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) #1"_ns,
792 [&] { return destroyed && newStream; });
794 destroyListener.Disconnect();
795 restartListener.Disconnect();
797 aStream = newStream;
800 // Open a new track and expect to have a new stream.
801 auto openTrack = [&](RefPtr<SmartMockCubebStream>& aCurrentStream,
802 RefPtr<TestDeviceInputConsumerTrack>& aTrack,
803 const RefPtr<TestAudioDataListener>& aListener,
804 CubebUtils::AudioDeviceID aDevice) {
805 ASSERT_TRUE(!!aCurrentStream);
806 ASSERT_TRUE(aCurrentStream->mHasInput);
807 ASSERT_TRUE(!aTrack);
808 ASSERT_TRUE(!!aListener);
810 bool destroyed = false;
811 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
812 AbstractThread::GetCurrent(),
813 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
814 destroyed = aDestroyed.get() == aCurrentStream.get();
817 RefPtr<SmartMockCubebStream> newStream;
818 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
819 AbstractThread::GetCurrent(),
820 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
821 newStream = aCreated;
824 aTrack = TestDeviceInputConsumerTrack::Create(graphImpl);
825 aTrack->ConnectDeviceInput(aDevice, aListener.get(), PRINCIPAL_HANDLE_NONE);
827 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
828 "TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) #2"_ns,
829 [&] { return destroyed && newStream; });
831 destroyListener.Disconnect();
832 restartListener.Disconnect();
834 aCurrentStream = newStream;
837 // Test for the native input device first then non-native device. The
838 // non-native device will be destroyed before the native device in case of
839 // causing a driver switching.
841 // Test for the native device.
842 const CubebUtils::AudioDeviceID nativeDevice = (CubebUtils::AudioDeviceID)1;
843 RefPtr<TestDeviceInputConsumerTrack> track1;
844 RefPtr<TestAudioDataListener> listener1;
845 RefPtr<SmartMockCubebStream> nativeStream;
846 RefPtr<TestDeviceInputConsumerTrack> track2;
847 RefPtr<TestAudioDataListener> listener2;
849 // Open a 1-channel NativeInputTrack.
850 listener1 = new TestAudioDataListener(1, false);
851 track1 = TestDeviceInputConsumerTrack::Create(graphImpl);
852 track1->ConnectDeviceInput(nativeDevice, listener1.get(),
853 PRINCIPAL_HANDLE_NONE);
855 EXPECT_TRUE(track1->ConnectedToNativeDevice());
856 EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
857 auto started = InvokeAsync(
858 [&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
859 nativeStream = WaitFor(cubeb->StreamInitEvent());
860 EXPECT_TRUE(nativeStream->mHasInput);
861 EXPECT_TRUE(nativeStream->mHasOutput);
862 EXPECT_EQ(nativeStream->GetInputDeviceID(), nativeDevice);
863 Unused << WaitFor(started);
865 // Open a 2-channel NativeInputTrack and wait for a new driver since the
866 // max-channel for the native device becomes 2 now.
867 listener2 = new TestAudioDataListener(2, false);
868 openTrack(nativeStream, track2, listener2, nativeDevice);
869 EXPECT_EQ(nativeStream->InputChannels(), 2U);
871 // Set the second NativeInputTrack to 1-channel and wait for a new driver
872 // since the max-channel for the native device becomes 1 now.
873 setNewChannelCount(listener2, nativeStream, 1);
874 EXPECT_EQ(nativeStream->InputChannels(), 1U);
876 // Set the first NativeInputTrack to 2-channel and wait for a new driver
877 // since the max input channel for the native device becomes 2 now.
878 setNewChannelCount(listener1, nativeStream, 2);
879 EXPECT_EQ(nativeStream->InputChannels(), 2U);
882 // Test for the non-native device.
884 const CubebUtils::AudioDeviceID nonNativeDevice =
885 (CubebUtils::AudioDeviceID)2;
887 // Open a 1-channel NonNativeInputTrack.
888 RefPtr<TestAudioDataListener> listener3 =
889 new TestAudioDataListener(1, false);
890 RefPtr<TestDeviceInputConsumerTrack> track3 =
891 TestDeviceInputConsumerTrack::Create(graphImpl);
892 track3->ConnectDeviceInput(nonNativeDevice, listener3.get(),
893 PRINCIPAL_HANDLE_NONE);
894 EXPECT_FALSE(track3->ConnectedToNativeDevice());
895 EXPECT_TRUE(track3->ConnectedToNonNativeDevice());
897 RefPtr<SmartMockCubebStream> nonNativeStream =
898 WaitFor(cubeb->StreamInitEvent());
899 EXPECT_TRUE(nonNativeStream->mHasInput);
900 EXPECT_FALSE(nonNativeStream->mHasOutput);
901 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
902 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
904 // Open a 2-channel NonNativeInputTrack and wait for a new stream since
905 // the max-channel for the non-native device becomes 2 now.
906 RefPtr<TestAudioDataListener> listener4 =
907 new TestAudioDataListener(2, false);
908 RefPtr<TestDeviceInputConsumerTrack> track4;
909 openTrack(nonNativeStream, track4, listener4, nonNativeDevice);
910 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
911 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
913 // Set the second NonNativeInputTrack to 1-channel and wait for a new
914 // driver since the max-channel for the non-native device becomes 1 now.
915 setNewChannelCount(listener4, nonNativeStream, 1);
916 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
918 // Set the first NonNativeInputTrack to 2-channel and wait for a new
919 // driver since the max input channel for the non-native device becomes 2
920 // now.
921 setNewChannelCount(listener3, nonNativeStream, 2);
922 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
924 // Close the second NonNativeInputTrack (1-channel) then the first one
925 // (2-channel) so we won't result in another stream creation.
926 DispatchFunction([&] {
927 track4->DisconnectDeviceInput();
928 track4->Destroy();
930 DispatchFunction([&] {
931 track3->DisconnectDeviceInput();
932 track3->Destroy();
934 RefPtr<SmartMockCubebStream> destroyedStream =
935 WaitFor(cubeb->StreamDestroyEvent());
936 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
939 // Tear down for the native device.
941 // Close the second NativeInputTrack (1-channel) then the first one
942 // (2-channel) so we won't have driver switching.
943 DispatchFunction([&] {
944 track2->DisconnectDeviceInput();
945 track2->Destroy();
947 DispatchFunction([&] {
948 track1->DisconnectDeviceInput();
949 track1->Destroy();
951 RefPtr<SmartMockCubebStream> destroyedStream =
952 WaitFor(cubeb->StreamDestroyEvent());
953 EXPECT_EQ(destroyedStream.get(), nativeStream.get());
957 // This test is pretty similar to SwitchNativeAudioProcessingTrack below, which
958 // tests the same thing but using AudioProcessingTrack. AudioProcessingTrack is
959 // the consumer of the DeviceInputTrack used in wild. It has its own customized
960 // AudioDataListener. However, it only tests when MOZ_WEBRTC is defined.
961 TEST(TestAudioTrackGraph, SwitchNativeInputDevice)
963 class TestAudioDataListener : public StrictMock<MockAudioDataListener> {
964 public:
965 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) {
966 EXPECT_CALL(*this, RequestedInputChannelCount)
967 .WillRepeatedly(Return(aChannelCount));
968 EXPECT_CALL(*this, RequestedInputProcessingParams)
969 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
970 EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice));
973 private:
974 ~TestAudioDataListener() = default;
977 MockCubeb* cubeb = new MockCubeb();
978 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
980 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
981 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
982 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
983 nullptr, GetMainThreadSerialEventTarget());
985 auto switchNativeDevice =
986 [&](RefPtr<SmartMockCubebStream>&& aCurrentNativeStream,
987 RefPtr<TestDeviceInputConsumerTrack>& aCurrentNativeTrack,
988 RefPtr<SmartMockCubebStream>& aNextNativeStream,
989 RefPtr<TestDeviceInputConsumerTrack>& aNextNativeTrack) {
990 ASSERT_TRUE(aCurrentNativeStream->mHasInput);
991 ASSERT_TRUE(aCurrentNativeStream->mHasOutput);
992 ASSERT_TRUE(aNextNativeStream->mHasInput);
993 ASSERT_FALSE(aNextNativeStream->mHasOutput);
995 std::cerr << "Switching native input from device "
996 << aCurrentNativeStream->GetInputDeviceID() << " to "
997 << aNextNativeStream->GetInputDeviceID() << std::endl;
999 uint32_t destroyed = 0;
1000 MediaEventListener destroyListener =
1001 cubeb->StreamDestroyEvent().Connect(
1002 AbstractThread::GetCurrent(),
1003 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1004 if (aDestroyed.get() == aCurrentNativeStream.get() ||
1005 aDestroyed.get() == aNextNativeStream.get()) {
1006 std::cerr << "cubeb stream " << aDestroyed.get()
1007 << " (device " << aDestroyed->GetInputDeviceID()
1008 << ") has been destroyed" << std::endl;
1009 destroyed += 1;
1013 RefPtr<SmartMockCubebStream> newStream;
1014 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1015 AbstractThread::GetCurrent(),
1016 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1017 // Make sure new stream has input, to prevent from getting a
1018 // temporary output-only AudioCallbackDriver after closing current
1019 // native device but before setting a new native input.
1020 if (aCreated->mHasInput) {
1021 ASSERT_TRUE(aCreated->mHasOutput);
1022 newStream = aCreated;
1026 std::cerr << "Close device " << aCurrentNativeStream->GetInputDeviceID()
1027 << std::endl;
1028 DispatchFunction([&] {
1029 aCurrentNativeTrack->DisconnectDeviceInput();
1030 aCurrentNativeTrack->Destroy();
1033 std::cerr << "Wait for the switching" << std::endl;
1034 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1035 "TEST(TestAudioTrackGraph, SwitchNativeInputDevice)"_ns,
1036 [&] { return destroyed >= 2 && newStream; });
1038 destroyListener.Disconnect();
1039 restartListener.Disconnect();
1041 aCurrentNativeStream = nullptr;
1042 aNextNativeStream = newStream;
1044 std::cerr << "Now the native input is device "
1045 << aNextNativeStream->GetInputDeviceID() << std::endl;
1048 // Open a DeviceInputConsumerTrack for device 1.
1049 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
1050 RefPtr<TestDeviceInputConsumerTrack> track1 =
1051 TestDeviceInputConsumerTrack::Create(graph);
1052 RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
1053 EXPECT_CALL(*listener1, Disconnect);
1054 track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
1055 EXPECT_EQ(track1->DeviceId().value(), device1);
1057 auto started =
1058 InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
1060 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
1061 EXPECT_TRUE(stream1->mHasInput);
1062 EXPECT_TRUE(stream1->mHasOutput);
1063 EXPECT_EQ(stream1->InputChannels(), 1U);
1064 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
1065 Unused << WaitFor(started);
1066 std::cerr << "Device " << device1 << " is opened (stream " << stream1.get()
1067 << ")" << std::endl;
1069 // Open a DeviceInputConsumerTrack for device 2.
1070 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
1071 RefPtr<TestDeviceInputConsumerTrack> track2 =
1072 TestDeviceInputConsumerTrack::Create(graph);
1073 RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false);
1074 EXPECT_CALL(*listener2, Disconnect).Times(2);
1075 track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
1076 EXPECT_EQ(track2->DeviceId().value(), device2);
1078 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
1079 EXPECT_TRUE(stream2->mHasInput);
1080 EXPECT_FALSE(stream2->mHasOutput);
1081 EXPECT_EQ(stream2->InputChannels(), 2U);
1082 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1083 std::cerr << "Device " << device2 << " is opened (stream " << stream2.get()
1084 << ")" << std::endl;
1086 // Open a DeviceInputConsumerTrack for device 3.
1087 const CubebUtils::AudioDeviceID device3 = (CubebUtils::AudioDeviceID)3;
1088 RefPtr<TestDeviceInputConsumerTrack> track3 =
1089 TestDeviceInputConsumerTrack::Create(graph);
1090 RefPtr<TestAudioDataListener> listener3 = new TestAudioDataListener(1, false);
1091 EXPECT_CALL(*listener3, Disconnect).Times(2);
1092 track3->ConnectDeviceInput(device3, listener3, PRINCIPAL_HANDLE_NONE);
1093 EXPECT_EQ(track3->DeviceId().value(), device3);
1095 RefPtr<SmartMockCubebStream> stream3 = WaitFor(cubeb->StreamInitEvent());
1096 EXPECT_TRUE(stream3->mHasInput);
1097 EXPECT_FALSE(stream3->mHasOutput);
1098 EXPECT_EQ(stream3->InputChannels(), 1U);
1099 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
1100 std::cerr << "Device " << device3 << " is opened (stream " << stream3.get()
1101 << ")" << std::endl;
1103 // Close device 1, so the native input device is switched from device 1 to
1104 // device 2.
1105 switchNativeDevice(std::move(stream1), track1, stream2, track2);
1106 EXPECT_TRUE(stream2->mHasInput);
1107 EXPECT_TRUE(stream2->mHasOutput);
1108 EXPECT_EQ(stream2->InputChannels(), 2U);
1109 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1111 NativeInputTrack* native = track2->Graph()->GetNativeInputTrackMainThread();
1112 ASSERT_TRUE(!!native);
1113 EXPECT_EQ(native->mDeviceId, device2);
1116 // Close device 2, so the native input device is switched from device 2 to
1117 // device 3.
1118 switchNativeDevice(std::move(stream2), track2, stream3, track3);
1119 EXPECT_TRUE(stream3->mHasInput);
1120 EXPECT_TRUE(stream3->mHasOutput);
1121 EXPECT_EQ(stream3->InputChannels(), 1U);
1122 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
1124 NativeInputTrack* native = track3->Graph()->GetNativeInputTrackMainThread();
1125 ASSERT_TRUE(!!native);
1126 EXPECT_EQ(native->mDeviceId, device3);
1129 // Clean up.
1130 std::cerr << "Close device " << device3 << std::endl;
1131 DispatchFunction([&] {
1132 track3->DisconnectDeviceInput();
1133 track3->Destroy();
1135 RefPtr<SmartMockCubebStream> destroyedStream =
1136 WaitFor(cubeb->StreamDestroyEvent());
1137 EXPECT_EQ(destroyedStream.get(), stream3.get());
1139 NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
1140 ASSERT_TRUE(!native);
1142 std::cerr << "No native input now" << std::endl;
1145 #ifdef MOZ_WEBRTC
1146 TEST(TestAudioTrackGraph, ErrorCallback)
1148 MockCubeb* cubeb = new MockCubeb();
1149 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1151 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1152 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1153 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1154 nullptr, GetMainThreadSerialEventTarget());
1156 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1158 // Dummy track to make graph rolling. Add it and remove it to remove the
1159 // graph from the global hash table and let it shutdown.
1161 // We open an input through this track so that there's something triggering
1162 // EnsureNextIteration on the fallback driver after the callback driver has
1163 // gotten the error, and to check that a replacement cubeb_stream receives
1164 // output from the graph.
1165 RefPtr<AudioProcessingTrack> processingTrack;
1166 RefPtr<AudioInputProcessing> listener;
1167 auto started = InvokeAsync([&] {
1168 processingTrack = AudioProcessingTrack::Create(graph);
1169 listener = new AudioInputProcessing(2);
1170 QueueExpectIsPassThrough(processingTrack, listener);
1171 processingTrack->SetInputProcessing(listener);
1172 processingTrack->GraphImpl()->AppendMessage(
1173 MakeUnique<StartInputProcessing>(processingTrack, listener));
1174 processingTrack->ConnectDeviceInput(deviceId, listener,
1175 PRINCIPAL_HANDLE_NONE);
1176 EXPECT_EQ(processingTrack->DeviceId().value(), deviceId);
1177 processingTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1178 return graph->NotifyWhenDeviceStarted(nullptr);
1181 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1182 Result<bool, nsresult> rv = WaitFor(started);
1183 EXPECT_TRUE(rv.unwrapOr(false));
1185 // Force a cubeb state_callback error and see that we don't crash.
1186 DispatchFunction([&] { stream->ForceError(); });
1188 // Wait for the error to take effect, and the driver to restart and receive
1189 // output.
1190 bool errored = false;
1191 MediaEventListener errorListener = stream->ErrorForcedEvent().Connect(
1192 AbstractThread::GetCurrent(), [&] { errored = true; });
1193 stream = WaitFor(cubeb->StreamInitEvent());
1194 WaitFor(stream->FramesVerifiedEvent());
1195 // The error event is notified after CUBEB_STATE_ERROR triggers other
1196 // threads to init a new cubeb_stream, so there is a theoretical chance that
1197 // `errored` might not be set when `stream` is set.
1198 errorListener.Disconnect();
1199 EXPECT_TRUE(errored);
1201 // Clean up.
1202 DispatchFunction([&] {
1203 processingTrack->GraphImpl()->AppendMessage(
1204 MakeUnique<StopInputProcessing>(processingTrack, listener));
1205 processingTrack->DisconnectDeviceInput();
1206 processingTrack->Destroy();
1208 WaitFor(cubeb->StreamDestroyEvent());
1211 TEST(TestAudioTrackGraph, AudioProcessingTrack)
1213 MockCubeb* cubeb = new MockCubeb();
1214 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1215 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
1216 Unused << unforcer;
1218 // Start on a system clock driver, then switch to full-duplex in one go. If we
1219 // did output-then-full-duplex we'd risk a second NotifyWhenDeviceStarted
1220 // resolving early after checking the first audio driver only.
1221 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1222 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1223 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1224 nullptr, GetMainThreadSerialEventTarget());
1226 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1228 RefPtr<AudioProcessingTrack> processingTrack;
1229 RefPtr<ProcessedMediaTrack> outputTrack;
1230 RefPtr<MediaInputPort> port;
1231 RefPtr<AudioInputProcessing> listener;
1232 auto p = InvokeAsync([&] {
1233 processingTrack = AudioProcessingTrack::Create(graph);
1234 outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
1235 outputTrack->QueueSetAutoend(false);
1236 outputTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1237 port = outputTrack->AllocateInputPort(processingTrack);
1238 /* Primary graph: Open Audio Input through SourceMediaTrack */
1239 listener = new AudioInputProcessing(2);
1240 QueueExpectIsPassThrough(processingTrack, listener);
1241 processingTrack->SetInputProcessing(listener);
1242 processingTrack->GraphImpl()->AppendMessage(
1243 MakeUnique<StartInputProcessing>(processingTrack, listener));
1244 // Device id does not matter. Ignore.
1245 processingTrack->ConnectDeviceInput(deviceId, listener,
1246 PRINCIPAL_HANDLE_NONE);
1247 return graph->NotifyWhenDeviceStarted(nullptr);
1250 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1251 EXPECT_TRUE(stream->mHasInput);
1252 Unused << WaitFor(p);
1254 // Wait for a second worth of audio data. GoFaster is dispatched through a
1255 // ControlMessage so that it is called in the first audio driver iteration.
1256 // Otherwise the audio driver might be going very fast while the fallback
1257 // system clock driver is still in an iteration.
1258 DispatchFunction([&] {
1259 processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
1261 uint32_t totalFrames = 0;
1262 WaitUntil(stream->FramesVerifiedEvent(), [&](uint32_t aFrames) {
1263 totalFrames += aFrames;
1264 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
1266 cubeb->DontGoFaster();
1268 // Clean up.
1269 DispatchFunction([&] {
1270 outputTrack->RemoveAudioOutput((void*)1);
1271 outputTrack->Destroy();
1272 port->Destroy();
1273 processingTrack->GraphImpl()->AppendMessage(
1274 MakeUnique<StopInputProcessing>(processingTrack, listener));
1275 processingTrack->DisconnectDeviceInput();
1276 processingTrack->Destroy();
1279 uint32_t inputRate = stream->SampleRate();
1280 uint32_t inputFrequency = stream->InputFrequency();
1281 uint64_t preSilenceSamples;
1282 uint32_t estimatedFreq;
1283 uint32_t nrDiscontinuities;
1284 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
1285 WaitFor(stream->OutputVerificationEvent());
1287 EXPECT_EQ(estimatedFreq, inputFrequency);
1288 std::cerr << "PreSilence: " << preSilenceSamples << std::endl;
1289 // We buffer 128 frames. See NativeInputTrack::NotifyInputData.
1290 EXPECT_GE(preSilenceSamples, 128U);
1291 // If the fallback system clock driver is doing a graph iteration before the
1292 // first audio driver iteration comes in, that iteration is ignored and
1293 // results in zeros. It takes one fallback driver iteration *after* the audio
1294 // driver has started to complete the switch, *usually* resulting two
1295 // 10ms-iterations of silence; sometimes only one.
1296 EXPECT_LE(preSilenceSamples, 128U + 2 * inputRate / 100 /* 2*10ms */);
1297 // The waveform from AudioGenerator starts at 0, but we don't control its
1298 // ending, so we expect a discontinuity there.
1299 EXPECT_LE(nrDiscontinuities, 1U);
1302 TEST(TestAudioTrackGraph, ReConnectDeviceInput)
1304 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
1305 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1307 // 48k is a native processing rate, and avoids a resampling pass compared
1308 // to 44.1k. The resampler may add take a few frames to stabilize, which show
1309 // as unexected discontinuities in the test.
1310 const TrackRate rate = 48000;
1311 // Use a drift factor so that we don't dont produce perfect 10ms-chunks.
1312 // This will exercise whatever buffers are in the audio processing pipeline,
1313 // and the bookkeeping surrounding them.
1314 const long step = 10 * rate * 1111 / 1000 / PR_MSEC_PER_SEC;
1316 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1317 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, rate, nullptr,
1318 GetMainThreadSerialEventTarget());
1320 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1322 RefPtr<AudioProcessingTrack> processingTrack;
1323 RefPtr<ProcessedMediaTrack> outputTrack;
1324 RefPtr<MediaInputPort> port;
1325 RefPtr<AudioInputProcessing> listener;
1326 RefPtr<OnFallbackListener> fallbackListener;
1327 DispatchFunction([&] {
1328 processingTrack = AudioProcessingTrack::Create(graph);
1329 outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
1330 outputTrack->QueueSetAutoend(false);
1331 outputTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1332 port = outputTrack->AllocateInputPort(processingTrack);
1334 const int32_t channelCount = 2;
1335 listener = new AudioInputProcessing(channelCount);
1336 processingTrack->SetInputProcessing(listener);
1337 processingTrack->GraphImpl()->AppendMessage(
1338 MakeUnique<StartInputProcessing>(processingTrack, listener));
1339 processingTrack->ConnectDeviceInput(deviceId, listener,
1340 PRINCIPAL_HANDLE_NONE);
1341 MediaEnginePrefs settings;
1342 settings.mChannels = channelCount;
1343 settings.mAgcOn = true; // Turn off pass-through.
1344 // AGC1 Mode 0 interferes with AudioVerifier's frequency estimation
1345 // through zero-crossing counts.
1346 settings.mAgc2Forced = true;
1347 QueueApplySettings(processingTrack, listener, settings);
1349 fallbackListener = new OnFallbackListener(processingTrack);
1350 processingTrack->AddListener(fallbackListener);
1353 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1354 EXPECT_TRUE(stream->mHasInput);
1356 while (
1357 stream->State()
1358 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
1359 .valueOr(true)) {
1360 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1363 // Wait for the AudioCallbackDriver to come into effect.
1364 while (fallbackListener->OnFallback()) {
1365 EXPECT_EQ(stream->ManualDataCallback(0),
1366 MockCubebStream::KeepProcessing::Yes);
1367 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1370 // Iterate for a second worth of audio data.
1371 for (long frames = 0; frames < graph->GraphRate(); frames += step) {
1372 stream->ManualDataCallback(step);
1375 // Close the input to see that no asserts go off due to bad state.
1376 DispatchFunction([&] { processingTrack->DisconnectDeviceInput(); });
1378 // Dispatch the disconnect message.
1379 ProcessEventQueue();
1380 // Run the disconnect message.
1381 EXPECT_EQ(stream->ManualDataCallback(0),
1382 MockCubebStream::KeepProcessing::Yes);
1383 // Switch driver.
1384 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
1385 EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
1386 std::tie(stream) = WaitFor(initPromise).unwrap()[0];
1387 EXPECT_FALSE(stream->mHasInput);
1389 while (
1390 stream->State()
1391 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
1392 .valueOr(true)) {
1393 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1396 // Wait for the new AudioCallbackDriver to come into effect.
1397 fallbackListener->Reset();
1398 while (fallbackListener->OnFallback()) {
1399 EXPECT_EQ(stream->ManualDataCallback(0),
1400 MockCubebStream::KeepProcessing::Yes);
1401 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1404 // Output-only. Iterate for another second before unmuting.
1405 for (long frames = 0; frames < graph->GraphRate(); frames += step) {
1406 stream->ManualDataCallback(step);
1409 // Re-open the input to again see that no asserts go off due to bad state.
1410 DispatchFunction([&] {
1411 // Device id does not matter. Ignore.
1412 processingTrack->ConnectDeviceInput(deviceId, listener,
1413 PRINCIPAL_HANDLE_NONE);
1415 // Dispatch the connect message.
1416 ProcessEventQueue();
1417 // Run the connect message.
1418 EXPECT_EQ(stream->ManualDataCallback(0),
1419 MockCubebStream::KeepProcessing::Yes);
1420 // Switch driver.
1421 initPromise = TakeN(cubeb->StreamInitEvent(), 1);
1422 EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
1423 std::tie(stream) = WaitFor(initPromise).unwrap()[0];
1424 EXPECT_TRUE(stream->mHasInput);
1426 while (
1427 stream->State()
1428 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
1429 .valueOr(true)) {
1430 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1433 // Wait for the new AudioCallbackDriver to come into effect.
1434 fallbackListener->Reset();
1435 while (fallbackListener->OnFallback()) {
1436 EXPECT_EQ(stream->ManualDataCallback(0),
1437 MockCubebStream::KeepProcessing::Yes);
1438 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1441 // Full-duplex. Iterate for another second before finishing.
1442 for (long frames = 0; frames < graph->GraphRate(); frames += step) {
1443 stream->ManualDataCallback(step);
1446 // Clean up.
1447 DispatchFunction([&] {
1448 processingTrack->RemoveListener(fallbackListener);
1449 outputTrack->RemoveAudioOutput((void*)1);
1450 outputTrack->Destroy();
1451 port->Destroy();
1452 processingTrack->GraphImpl()->AppendMessage(
1453 MakeUnique<StopInputProcessing>(processingTrack, listener));
1454 processingTrack->DisconnectDeviceInput();
1455 processingTrack->Destroy();
1458 // Dispatch the clean-up messages.
1459 ProcessEventQueue();
1460 // Run the clean-up messages.
1461 EXPECT_EQ(stream->ManualDataCallback(0),
1462 MockCubebStream::KeepProcessing::Yes);
1463 // Shut down driver.
1464 EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
1466 uint32_t inputFrequency = stream->InputFrequency();
1467 uint64_t preSilenceSamples;
1468 uint32_t estimatedFreq;
1469 uint32_t nrDiscontinuities;
1470 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
1471 WaitFor(stream->OutputVerificationEvent());
1473 EXPECT_EQ(estimatedFreq, inputFrequency);
1474 std::cerr << "PreSilence: " << preSilenceSamples << "\n";
1475 // We buffer 128 frames. See NativeInputTrack::NotifyInputData.
1476 // When not in passthrough the AudioInputProcessing packetizer also buffers
1477 // 10ms of silence, pulled in from NativeInputTrack when being run by the
1478 // fallback SystemClockDriver.
1479 EXPECT_EQ(preSilenceSamples, WEBAUDIO_BLOCK_SIZE + rate / 100);
1480 // The waveform from AudioGenerator starts at 0, but we don't control its
1481 // ending, so we expect a discontinuity there. Note that this check is only
1482 // for the waveform on the stream *after* re-opening the input.
1483 EXPECT_LE(nrDiscontinuities, 1U);
1486 // Sum the signal to mono and compute the root mean square, in float32,
1487 // regardless of the input format.
1488 float rmsf32(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) {
1489 float downmixed;
1490 float rms = 0.;
1491 uint32_t readIdx = 0;
1492 for (uint32_t i = 0; i < aFrames; i++) {
1493 downmixed = 0.;
1494 for (uint32_t j = 0; j < aChannels; j++) {
1495 downmixed += ConvertAudioSample<float>(aSamples[readIdx++]);
1497 rms += downmixed * downmixed;
1499 rms = rms / aFrames;
1500 return sqrt(rms);
1503 TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
1505 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
1506 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1508 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1509 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1510 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1511 nullptr, GetMainThreadSerialEventTarget());
1513 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1515 RefPtr<AudioProcessingTrack> processingTrack;
1516 RefPtr<ProcessedMediaTrack> outputTrack;
1517 RefPtr<MediaInputPort> port;
1518 RefPtr<AudioInputProcessing> listener;
1519 RefPtr<OnFallbackListener> fallbackListener;
1520 DispatchFunction([&] {
1521 processingTrack = AudioProcessingTrack::Create(graph);
1522 outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
1523 outputTrack->QueueSetAutoend(false);
1524 outputTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1525 port = outputTrack->AllocateInputPort(processingTrack);
1526 /* Primary graph: Open Audio Input through SourceMediaTrack */
1527 listener = new AudioInputProcessing(2);
1528 QueueExpectIsPassThrough(processingTrack, listener);
1529 processingTrack->SetInputProcessing(listener);
1530 processingTrack->ConnectDeviceInput(deviceId, listener,
1531 PRINCIPAL_HANDLE_NONE);
1532 processingTrack->GraphImpl()->AppendMessage(
1533 MakeUnique<StartInputProcessing>(processingTrack, listener));
1534 fallbackListener = new OnFallbackListener(processingTrack);
1535 processingTrack->AddListener(fallbackListener);
1538 ProcessEventQueue();
1540 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1541 EXPECT_TRUE(stream->mHasInput);
1543 while (
1544 stream->State()
1545 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
1546 .valueOr(true)) {
1547 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1550 // Wait for the AudioCallbackDriver to come into effect.
1551 while (fallbackListener->OnFallback()) {
1552 EXPECT_EQ(stream->ManualDataCallback(0),
1553 MockCubebStream::KeepProcessing::Yes);
1554 std::this_thread::sleep_for(std::chrono::milliseconds(1));
1557 stream->SetOutputRecordingEnabled(true);
1559 // Wait for a second worth of audio data.
1560 const long step = graph->GraphRate() / 100; // 10ms
1561 for (long frames = 0; frames < graph->GraphRate(); frames += step) {
1562 stream->ManualDataCallback(step);
1565 const uint32_t ITERATION_COUNT = 5;
1566 uint32_t iterations = ITERATION_COUNT;
1567 DisabledTrackMode nextMode = DisabledTrackMode::SILENCE_BLACK;
1568 while (iterations--) {
1569 // toggle the track enabled mode, wait a second, do this ITERATION_COUNT
1570 // times
1571 DispatchFunction([&] {
1572 processingTrack->SetDisabledTrackMode(nextMode);
1573 if (nextMode == DisabledTrackMode::SILENCE_BLACK) {
1574 nextMode = DisabledTrackMode::ENABLED;
1575 } else {
1576 nextMode = DisabledTrackMode::SILENCE_BLACK;
1580 ProcessEventQueue();
1582 for (long frames = 0; frames < graph->GraphRate(); frames += step) {
1583 stream->ManualDataCallback(step);
1587 // Clean up.
1588 DispatchFunction([&] {
1589 outputTrack->RemoveAudioOutput((void*)1);
1590 outputTrack->Destroy();
1591 port->Destroy();
1592 processingTrack->GraphImpl()->AppendMessage(
1593 MakeUnique<StopInputProcessing>(processingTrack, listener));
1594 processingTrack->RemoveListener(fallbackListener);
1595 processingTrack->DisconnectDeviceInput();
1596 processingTrack->Destroy();
1599 ProcessEventQueue();
1601 // Close the input and switch driver.
1602 while (stream->ManualDataCallback(0) != MockCubebStream::KeepProcessing::No) {
1603 std::cerr << "Waiting for switch...\n";
1606 uint64_t preSilenceSamples;
1607 uint32_t estimatedFreq;
1608 uint32_t nrDiscontinuities;
1609 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
1610 WaitFor(stream->OutputVerificationEvent());
1612 auto data = stream->TakeRecordedOutput();
1614 // check that there is non-silence and silence at the expected time in the
1615 // stereo recording, while allowing for a bit of scheduling uncertainty, by
1616 // checking half a second after the theoretical muting/unmuting.
1617 // non-silence starts around: 0s, 2s, 4s
1618 // silence start around: 1s, 3s, 5s
1619 // To detect silence or non-silence, we compute the RMS of the signal for
1620 // 100ms.
1621 float noisyTime_s[] = {0.5, 2.5, 4.5};
1622 float silenceTime_s[] = {1.5, 3.5, 5.5};
1624 uint32_t rate = graph->GraphRate();
1625 for (float& time : noisyTime_s) {
1626 uint32_t startIdx = time * rate * 2 /* stereo */;
1627 EXPECT_NE(rmsf32(&(data[startIdx]), 2, rate / 10), 0.0);
1630 for (float& time : silenceTime_s) {
1631 uint32_t startIdx = time * rate * 2 /* stereo */;
1632 EXPECT_EQ(rmsf32(&(data[startIdx]), 2, rate / 10), 0.0);
1636 TEST(TestAudioTrackGraph, SetRequestedInputChannelCount)
1638 MockCubeb* cubeb = new MockCubeb();
1639 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1641 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1642 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1643 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1644 nullptr, GetMainThreadSerialEventTarget());
1646 // Open a 2-channel native input stream.
1647 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
1648 RefPtr<AudioProcessingTrack> track1 = AudioProcessingTrack::Create(graph);
1649 RefPtr<AudioInputProcessing> listener1 = new AudioInputProcessing(2);
1650 track1->SetInputProcessing(listener1);
1651 QueueExpectIsPassThrough(track1, listener1);
1652 track1->GraphImpl()->AppendMessage(
1653 MakeUnique<StartInputProcessing>(track1, listener1));
1654 track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
1655 EXPECT_EQ(track1->DeviceId().value(), device1);
1657 auto started =
1658 InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
1660 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
1661 EXPECT_TRUE(stream1->mHasInput);
1662 EXPECT_TRUE(stream1->mHasOutput);
1663 EXPECT_EQ(stream1->InputChannels(), 2U);
1664 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
1665 Unused << WaitFor(started);
1667 // Open a 1-channel non-native input stream.
1668 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
1669 RefPtr<AudioProcessingTrack> track2 = AudioProcessingTrack::Create(graph);
1670 RefPtr<AudioInputProcessing> listener2 = new AudioInputProcessing(1);
1671 track2->SetInputProcessing(listener2);
1672 QueueExpectIsPassThrough(track2, listener2);
1673 track2->GraphImpl()->AppendMessage(
1674 MakeUnique<StartInputProcessing>(track2, listener2));
1675 track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
1676 EXPECT_EQ(track2->DeviceId().value(), device2);
1678 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
1679 EXPECT_TRUE(stream2->mHasInput);
1680 EXPECT_FALSE(stream2->mHasOutput);
1681 EXPECT_EQ(stream2->InputChannels(), 1U);
1682 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1684 // Request a new input channel count. This should re-create new input stream
1685 // accordingly.
1686 auto setNewChannelCount = [&](const RefPtr<AudioProcessingTrack> aTrack,
1687 const RefPtr<AudioInputProcessing>& aListener,
1688 RefPtr<SmartMockCubebStream>& aStream,
1689 int32_t aChannelCount) {
1690 bool destroyed = false;
1691 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
1692 AbstractThread::GetCurrent(),
1693 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1694 destroyed = aDestroyed.get() == aStream.get();
1697 RefPtr<SmartMockCubebStream> newStream;
1698 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1699 AbstractThread::GetCurrent(),
1700 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1701 newStream = aCreated;
1704 MediaEnginePrefs settings;
1705 settings.mChannels = aChannelCount;
1706 QueueApplySettings(aTrack, aListener, settings);
1708 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1709 "TEST(TestAudioTrackGraph, SetRequestedInputChannelCount)"_ns,
1710 [&] { return destroyed && newStream; });
1712 destroyListener.Disconnect();
1713 restartListener.Disconnect();
1715 aStream = newStream;
1718 // Set the native input stream's input channel count to 1.
1719 setNewChannelCount(track1, listener1, stream1, 1);
1720 EXPECT_TRUE(stream1->mHasInput);
1721 EXPECT_TRUE(stream1->mHasOutput);
1722 EXPECT_EQ(stream1->InputChannels(), 1U);
1723 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
1725 // Set the non-native input stream's input channel count to 2.
1726 setNewChannelCount(track2, listener2, stream2, 2);
1727 EXPECT_TRUE(stream2->mHasInput);
1728 EXPECT_FALSE(stream2->mHasOutput);
1729 EXPECT_EQ(stream2->InputChannels(), 2U);
1730 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1732 // Close the non-native input stream.
1733 DispatchFunction([&] {
1734 track2->GraphImpl()->AppendMessage(
1735 MakeUnique<StopInputProcessing>(track2, listener2));
1736 track2->DisconnectDeviceInput();
1737 track2->Destroy();
1739 RefPtr<SmartMockCubebStream> destroyed = WaitFor(cubeb->StreamDestroyEvent());
1740 EXPECT_EQ(destroyed.get(), stream2.get());
1742 // Close the native input stream.
1743 DispatchFunction([&] {
1744 track1->GraphImpl()->AppendMessage(
1745 MakeUnique<StopInputProcessing>(track1, listener1));
1746 track1->DisconnectDeviceInput();
1747 track1->Destroy();
1749 destroyed = WaitFor(cubeb->StreamDestroyEvent());
1750 EXPECT_EQ(destroyed.get(), stream1.get());
1753 // The native audio stream (a.k.a. GraphDriver) and the non-native audio stream
1754 // should always be the same as the max requested input channel of its paired
1755 // AudioProcessingTracks. This test checks if the audio stream paired with the
1756 // AudioProcessingTrack will follow the max requested input channel or not.
1758 // This test is pretty similar to RestartAudioIfMaxChannelCountChanged above,
1759 // which makes sure the related DeviceInputTrack operations for the test here
1760 // works correctly. Instead of using a test-only AudioDataListener, we use
1761 // AudioInputProcessing here to simulate the real world use case.
1762 TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged)
1764 MockCubeb* cubeb = new MockCubeb();
1765 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1766 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
1767 Unused << unforcer;
1769 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1770 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1771 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1772 nullptr, GetMainThreadSerialEventTarget());
1774 // Request a new input channel count and expect to have a new stream.
1775 auto setNewChannelCount = [&](const RefPtr<AudioProcessingTrack>& aTrack,
1776 const RefPtr<AudioInputProcessing>& aListener,
1777 RefPtr<SmartMockCubebStream>& aStream,
1778 int32_t aChannelCount) {
1779 ASSERT_TRUE(!!aTrack);
1780 ASSERT_TRUE(!!aListener);
1781 ASSERT_TRUE(!!aStream);
1782 ASSERT_TRUE(aStream->mHasInput);
1783 ASSERT_NE(aChannelCount, 0);
1785 bool destroyed = false;
1786 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
1787 AbstractThread::GetCurrent(),
1788 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1789 destroyed = aDestroyed.get() == aStream.get();
1792 RefPtr<SmartMockCubebStream> newStream;
1793 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1794 AbstractThread::GetCurrent(),
1795 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1796 newStream = aCreated;
1799 MediaEnginePrefs settings;
1800 settings.mChannels = aChannelCount;
1801 QueueApplySettings(aTrack, aListener, settings);
1803 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1804 "TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged) #1"_ns,
1805 [&] { return destroyed && newStream; });
1807 destroyListener.Disconnect();
1808 restartListener.Disconnect();
1810 aStream = newStream;
1813 // Open a new track and expect to have a new stream.
1814 auto openTrack = [&](RefPtr<SmartMockCubebStream>& aCurrentStream,
1815 RefPtr<AudioProcessingTrack>& aTrack,
1816 RefPtr<AudioInputProcessing>& aListener,
1817 CubebUtils::AudioDeviceID aDevice,
1818 uint32_t aChannelCount) {
1819 ASSERT_TRUE(!!aCurrentStream);
1820 ASSERT_TRUE(aCurrentStream->mHasInput);
1821 ASSERT_TRUE(aChannelCount > aCurrentStream->InputChannels());
1822 ASSERT_TRUE(!aTrack);
1823 ASSERT_TRUE(!aListener);
1825 bool destroyed = false;
1826 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
1827 AbstractThread::GetCurrent(),
1828 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1829 destroyed = aDestroyed.get() == aCurrentStream.get();
1832 RefPtr<SmartMockCubebStream> newStream;
1833 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1834 AbstractThread::GetCurrent(),
1835 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1836 newStream = aCreated;
1839 aTrack = AudioProcessingTrack::Create(graph);
1840 aListener = new AudioInputProcessing(aChannelCount);
1841 aTrack->SetInputProcessing(aListener);
1842 QueueExpectIsPassThrough(aTrack, aListener);
1843 aTrack->GraphImpl()->AppendMessage(
1844 MakeUnique<StartInputProcessing>(aTrack, aListener));
1846 DispatchFunction([&] {
1847 aTrack->ConnectDeviceInput(aDevice, aListener, PRINCIPAL_HANDLE_NONE);
1850 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1851 "TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged) #2"_ns,
1852 [&] { return destroyed && newStream; });
1854 destroyListener.Disconnect();
1855 restartListener.Disconnect();
1857 aCurrentStream = newStream;
1860 // Test for the native input device first then non-native device. The
1861 // non-native device will be destroyed before the native device in case of
1862 // causing a native-device-switching.
1864 // Test for the native device.
1865 const CubebUtils::AudioDeviceID nativeDevice = (CubebUtils::AudioDeviceID)1;
1866 RefPtr<AudioProcessingTrack> track1;
1867 RefPtr<AudioInputProcessing> listener1;
1868 RefPtr<SmartMockCubebStream> nativeStream;
1869 RefPtr<AudioProcessingTrack> track2;
1870 RefPtr<AudioInputProcessing> listener2;
1872 // Open a 1-channel AudioProcessingTrack for the native device.
1873 track1 = AudioProcessingTrack::Create(graph);
1874 listener1 = new AudioInputProcessing(1);
1875 track1->SetInputProcessing(listener1);
1876 QueueExpectIsPassThrough(track1, listener1);
1877 track1->GraphImpl()->AppendMessage(
1878 MakeUnique<StartInputProcessing>(track1, listener1));
1879 track1->ConnectDeviceInput(nativeDevice, listener1, PRINCIPAL_HANDLE_NONE);
1880 EXPECT_EQ(track1->DeviceId().value(), nativeDevice);
1882 auto started =
1883 InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
1885 nativeStream = WaitFor(cubeb->StreamInitEvent());
1886 EXPECT_TRUE(nativeStream->mHasInput);
1887 EXPECT_TRUE(nativeStream->mHasOutput);
1888 EXPECT_EQ(nativeStream->InputChannels(), 1U);
1889 EXPECT_EQ(nativeStream->GetInputDeviceID(), nativeDevice);
1890 Unused << WaitFor(started);
1892 // Open a 2-channel AudioProcessingTrack for the native device and wait for
1893 // a new driver since the max-channel for the native device becomes 2 now.
1894 openTrack(nativeStream, track2, listener2, nativeDevice, 2);
1895 EXPECT_EQ(nativeStream->InputChannels(), 2U);
1897 // Set the second AudioProcessingTrack for the native device to 1-channel
1898 // and wait for a new driver since the max-channel for the native device
1899 // becomes 1 now.
1900 setNewChannelCount(track2, listener2, nativeStream, 1);
1901 EXPECT_EQ(nativeStream->InputChannels(), 1U);
1903 // Set the first AudioProcessingTrack for the native device to 2-channel and
1904 // wait for a new driver since the max input channel for the native device
1905 // becomes 2 now.
1906 setNewChannelCount(track1, listener1, nativeStream, 2);
1907 EXPECT_EQ(nativeStream->InputChannels(), 2U);
1910 // Test for the non-native device.
1912 const CubebUtils::AudioDeviceID nonNativeDevice =
1913 (CubebUtils::AudioDeviceID)2;
1915 // Open a 1-channel AudioProcessingTrack for the non-native device.
1916 RefPtr<AudioProcessingTrack> track3 = AudioProcessingTrack::Create(graph);
1917 RefPtr<AudioInputProcessing> listener3 = new AudioInputProcessing(1);
1918 track3->SetInputProcessing(listener3);
1919 QueueExpectIsPassThrough(track3, listener3);
1920 track3->GraphImpl()->AppendMessage(
1921 MakeUnique<StartInputProcessing>(track3, listener3));
1922 track3->ConnectDeviceInput(nonNativeDevice, listener3,
1923 PRINCIPAL_HANDLE_NONE);
1924 EXPECT_EQ(track3->DeviceId().value(), nonNativeDevice);
1926 RefPtr<SmartMockCubebStream> nonNativeStream =
1927 WaitFor(cubeb->StreamInitEvent());
1928 EXPECT_TRUE(nonNativeStream->mHasInput);
1929 EXPECT_FALSE(nonNativeStream->mHasOutput);
1930 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
1931 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1933 // Open a 2-channel AudioProcessingTrack for the non-native device and wait
1934 // for a new stream since the max-channel for the non-native device becomes
1935 // 2 now.
1936 RefPtr<AudioProcessingTrack> track4;
1937 RefPtr<AudioInputProcessing> listener4;
1938 openTrack(nonNativeStream, track4, listener4, nonNativeDevice, 2);
1939 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
1940 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1942 // Set the second AudioProcessingTrack for the non-native to 1-channel and
1943 // wait for a new driver since the max-channel for the non-native device
1944 // becomes 1 now.
1945 setNewChannelCount(track4, listener4, nonNativeStream, 1);
1946 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
1947 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1949 // Set the first AudioProcessingTrack for the non-native device to 2-channel
1950 // and wait for a new driver since the max input channel for the non-native
1951 // device becomes 2 now.
1952 setNewChannelCount(track3, listener3, nonNativeStream, 2);
1953 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
1954 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1956 // Close the second AudioProcessingTrack (1-channel) for the non-native
1957 // device then the first one (2-channel) so we won't result in another
1958 // stream creation.
1959 DispatchFunction([&] {
1960 track4->GraphImpl()->AppendMessage(
1961 MakeUnique<StopInputProcessing>(track4, listener4));
1962 track4->DisconnectDeviceInput();
1963 track4->Destroy();
1965 DispatchFunction([&] {
1966 track3->GraphImpl()->AppendMessage(
1967 MakeUnique<StopInputProcessing>(track3, listener3));
1968 track3->DisconnectDeviceInput();
1969 track3->Destroy();
1971 RefPtr<SmartMockCubebStream> destroyedStream =
1972 WaitFor(cubeb->StreamDestroyEvent());
1973 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
1976 // Tear down for the native device.
1978 // Close the second AudioProcessingTrack (1-channel) for the native device
1979 // then the first one (2-channel) so we won't have driver switching.
1980 DispatchFunction([&] {
1981 track2->GraphImpl()->AppendMessage(
1982 MakeUnique<StopInputProcessing>(track2, listener2));
1983 track2->DisconnectDeviceInput();
1984 track2->Destroy();
1986 DispatchFunction([&] {
1987 track1->GraphImpl()->AppendMessage(
1988 MakeUnique<StopInputProcessing>(track1, listener1));
1989 track1->DisconnectDeviceInput();
1990 track1->Destroy();
1992 RefPtr<SmartMockCubebStream> destroyedStream =
1993 WaitFor(cubeb->StreamDestroyEvent());
1994 EXPECT_EQ(destroyedStream.get(), nativeStream.get());
1998 TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)
2000 MockCubeb* cubeb = new MockCubeb();
2001 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2003 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2004 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2005 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2006 nullptr, GetMainThreadSerialEventTarget());
2008 // Set the input channel count of AudioInputProcessing, which will force
2009 // MediaTrackGraph to re-evaluate input device, when the MediaTrackGraph is
2010 // driven by the SystemClockDriver.
2012 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
2013 RefPtr<AudioProcessingTrack> track;
2014 RefPtr<AudioInputProcessing> listener;
2015 DispatchFunction([&] {
2016 track = AudioProcessingTrack::Create(graph);
2017 listener = new AudioInputProcessing(2);
2018 QueueExpectIsPassThrough(track, listener);
2019 track->SetInputProcessing(listener);
2021 MediaEnginePrefs settings;
2022 settings.mChannels = 1;
2023 QueueApplySettings(track, listener, settings);
2026 // Wait for AudioCallbackDriver to init output-only stream.
2027 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
2028 EXPECT_FALSE(stream->mHasInput);
2029 EXPECT_TRUE(stream->mHasOutput);
2031 // Open a full-duplex AudioCallbackDriver.
2032 RefPtr<MediaInputPort> port;
2033 DispatchFunction([&] {
2034 track->GraphImpl()->AppendMessage(
2035 MakeUnique<StartInputProcessing>(track, listener));
2036 track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
2039 stream = WaitFor(cubeb->StreamInitEvent());
2040 EXPECT_TRUE(stream->mHasInput);
2041 EXPECT_TRUE(stream->mHasOutput);
2042 EXPECT_EQ(stream->InputChannels(), 1U);
2044 Unused << WaitFor(
2045 InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
2047 // Clean up.
2048 DispatchFunction([&] {
2049 track->GraphImpl()->AppendMessage(
2050 MakeUnique<StopInputProcessing>(track, listener));
2051 track->DisconnectDeviceInput();
2052 track->Destroy();
2054 Unused << WaitFor(cubeb->StreamDestroyEvent());
2057 TEST(TestAudioTrackGraph, StartAudioDeviceBeforeStartingAudioProcessing)
2059 MockCubeb* cubeb = new MockCubeb();
2060 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2062 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2063 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2064 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2065 nullptr, GetMainThreadSerialEventTarget());
2067 // Create a duplex AudioCallbackDriver
2068 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
2069 RefPtr<AudioProcessingTrack> track;
2070 RefPtr<AudioInputProcessing> listener;
2071 DispatchFunction([&] {
2072 track = AudioProcessingTrack::Create(graph);
2073 listener = new AudioInputProcessing(2);
2074 QueueExpectIsPassThrough(track, listener);
2075 track->SetInputProcessing(listener);
2076 // Start audio device without starting audio processing.
2077 track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
2080 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
2081 EXPECT_TRUE(stream->mHasInput);
2082 EXPECT_TRUE(stream->mHasOutput);
2084 // Wait for a second to make sure audio output callback has been fired.
2085 DispatchFunction(
2086 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2088 uint32_t totalFrames = 0;
2089 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2090 totalFrames += aFrames;
2091 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2094 cubeb->DontGoFaster();
2096 // Start the audio processing.
2097 DispatchFunction([&] {
2098 track->GraphImpl()->AppendMessage(
2099 MakeUnique<StartInputProcessing>(track, listener));
2102 // Wait for a second to make sure audio output callback has been fired.
2103 DispatchFunction(
2104 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2106 uint32_t totalFrames = 0;
2107 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2108 totalFrames += aFrames;
2109 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2112 cubeb->DontGoFaster();
2114 // Clean up.
2115 DispatchFunction([&] {
2116 track->DisconnectDeviceInput();
2117 track->Destroy();
2119 Unused << WaitFor(cubeb->StreamDestroyEvent());
2122 TEST(TestAudioTrackGraph, StopAudioProcessingBeforeStoppingAudioDevice)
2124 MockCubeb* cubeb = new MockCubeb();
2125 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2127 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2128 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2129 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2130 nullptr, GetMainThreadSerialEventTarget());
2132 // Create a duplex AudioCallbackDriver
2133 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
2134 RefPtr<AudioProcessingTrack> track;
2135 RefPtr<AudioInputProcessing> listener;
2136 DispatchFunction([&] {
2137 track = AudioProcessingTrack::Create(graph);
2138 listener = new AudioInputProcessing(2);
2139 QueueExpectIsPassThrough(track, listener);
2140 track->SetInputProcessing(listener);
2141 track->GraphImpl()->AppendMessage(
2142 MakeUnique<StartInputProcessing>(track, listener));
2143 track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
2146 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
2147 EXPECT_TRUE(stream->mHasInput);
2148 EXPECT_TRUE(stream->mHasOutput);
2150 // Wait for a second to make sure audio output callback has been fired.
2151 DispatchFunction(
2152 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2154 uint32_t totalFrames = 0;
2155 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2156 totalFrames += aFrames;
2157 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2160 cubeb->DontGoFaster();
2162 // Stop the audio processing
2163 DispatchFunction([&] {
2164 track->GraphImpl()->AppendMessage(
2165 MakeUnique<StopInputProcessing>(track, listener));
2168 // Wait for a second to make sure audio output callback has been fired.
2169 DispatchFunction(
2170 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2172 uint32_t totalFrames = 0;
2173 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2174 totalFrames += aFrames;
2175 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2178 cubeb->DontGoFaster();
2180 // Clean up.
2181 DispatchFunction([&] {
2182 track->DisconnectDeviceInput();
2183 track->Destroy();
2185 Unused << WaitFor(cubeb->StreamDestroyEvent());
2188 // This test is pretty similar to SwitchNativeInputDevice above, which makes
2189 // sure the related DeviceInputTrack operations for the test here works
2190 // correctly. Instead of using a test-only DeviceInputTrack consumer, we use
2191 // AudioProcessingTrack here to simulate the real world use case.
2192 TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack)
2194 MockCubeb* cubeb = new MockCubeb();
2195 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2197 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2198 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2199 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2200 nullptr, GetMainThreadSerialEventTarget());
2202 auto switchNativeDevice =
2203 [&](RefPtr<SmartMockCubebStream>&& aCurrentNativeStream,
2204 RefPtr<AudioProcessingTrack>& aCurrentNativeTrack,
2205 RefPtr<AudioInputProcessing>& aCurrentNativeListener,
2206 RefPtr<SmartMockCubebStream>& aNextNativeStream,
2207 RefPtr<AudioProcessingTrack>& aNextNativeTrack) {
2208 ASSERT_TRUE(aCurrentNativeStream->mHasInput);
2209 ASSERT_TRUE(aCurrentNativeStream->mHasOutput);
2210 ASSERT_TRUE(aNextNativeStream->mHasInput);
2211 ASSERT_FALSE(aNextNativeStream->mHasOutput);
2213 std::cerr << "Switching native input from device "
2214 << aCurrentNativeStream->GetInputDeviceID() << " to "
2215 << aNextNativeStream->GetInputDeviceID() << std::endl;
2217 uint32_t destroyed = 0;
2218 MediaEventListener destroyListener =
2219 cubeb->StreamDestroyEvent().Connect(
2220 AbstractThread::GetCurrent(),
2221 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
2222 if (aDestroyed.get() == aCurrentNativeStream.get() ||
2223 aDestroyed.get() == aNextNativeStream.get()) {
2224 std::cerr << "cubeb stream " << aDestroyed.get()
2225 << " (device " << aDestroyed->GetInputDeviceID()
2226 << ") has been destroyed" << std::endl;
2227 destroyed += 1;
2231 RefPtr<SmartMockCubebStream> newStream;
2232 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
2233 AbstractThread::GetCurrent(),
2234 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
2235 // Make sure new stream has input, to prevent from getting a
2236 // temporary output-only AudioCallbackDriver after closing current
2237 // native device but before setting a new native input.
2238 if (aCreated->mHasInput) {
2239 ASSERT_TRUE(aCreated->mHasOutput);
2240 newStream = aCreated;
2244 std::cerr << "Close device " << aCurrentNativeStream->GetInputDeviceID()
2245 << std::endl;
2246 DispatchFunction([&] {
2247 aCurrentNativeTrack->GraphImpl()->AppendMessage(
2248 MakeUnique<StopInputProcessing>(aCurrentNativeTrack,
2249 aCurrentNativeListener));
2250 aCurrentNativeTrack->DisconnectDeviceInput();
2251 aCurrentNativeTrack->Destroy();
2254 std::cerr << "Wait for the switching" << std::endl;
2255 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
2256 "TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack)"_ns,
2257 [&] { return destroyed >= 2 && newStream; });
2259 destroyListener.Disconnect();
2260 restartListener.Disconnect();
2262 aCurrentNativeStream = nullptr;
2263 aNextNativeStream = newStream;
2265 std::cerr << "Now the native input is device "
2266 << aNextNativeStream->GetInputDeviceID() << std::endl;
2269 // Open a AudioProcessingTrack for device 1.
2270 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
2271 RefPtr<AudioProcessingTrack> track1 = AudioProcessingTrack::Create(graph);
2272 RefPtr<AudioInputProcessing> listener1 = new AudioInputProcessing(1);
2273 track1->SetInputProcessing(listener1);
2274 QueueExpectIsPassThrough(track1, listener1);
2275 track1->GraphImpl()->AppendMessage(
2276 MakeUnique<StartInputProcessing>(track1, listener1));
2277 track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
2278 EXPECT_EQ(track1->DeviceId().value(), device1);
2280 auto started =
2281 InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
2283 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
2284 EXPECT_TRUE(stream1->mHasInput);
2285 EXPECT_TRUE(stream1->mHasOutput);
2286 EXPECT_EQ(stream1->InputChannels(), 1U);
2287 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
2288 Unused << WaitFor(started);
2289 std::cerr << "Device " << device1 << " is opened (stream " << stream1.get()
2290 << ")" << std::endl;
2292 // Open a AudioProcessingTrack for device 2.
2293 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
2294 RefPtr<AudioProcessingTrack> track2 = AudioProcessingTrack::Create(graph);
2295 RefPtr<AudioInputProcessing> listener2 = new AudioInputProcessing(2);
2296 track2->SetInputProcessing(listener2);
2297 QueueExpectIsPassThrough(track2, listener2);
2298 track2->GraphImpl()->AppendMessage(
2299 MakeUnique<StartInputProcessing>(track2, listener2));
2300 track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
2301 EXPECT_EQ(track2->DeviceId().value(), device2);
2303 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
2304 EXPECT_TRUE(stream2->mHasInput);
2305 EXPECT_FALSE(stream2->mHasOutput);
2306 EXPECT_EQ(stream2->InputChannels(), 2U);
2307 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
2308 std::cerr << "Device " << device2 << " is opened (stream " << stream2.get()
2309 << ")" << std::endl;
2311 // Open a AudioProcessingTrack for device 3.
2312 const CubebUtils::AudioDeviceID device3 = (CubebUtils::AudioDeviceID)3;
2313 RefPtr<AudioProcessingTrack> track3 = AudioProcessingTrack::Create(graph);
2314 RefPtr<AudioInputProcessing> listener3 = new AudioInputProcessing(1);
2315 track3->SetInputProcessing(listener3);
2316 QueueExpectIsPassThrough(track3, listener3);
2317 track3->GraphImpl()->AppendMessage(
2318 MakeUnique<StartInputProcessing>(track3, listener3));
2319 track3->ConnectDeviceInput(device3, listener3, PRINCIPAL_HANDLE_NONE);
2320 EXPECT_EQ(track3->DeviceId().value(), device3);
2322 RefPtr<SmartMockCubebStream> stream3 = WaitFor(cubeb->StreamInitEvent());
2323 EXPECT_TRUE(stream3->mHasInput);
2324 EXPECT_FALSE(stream3->mHasOutput);
2325 EXPECT_EQ(stream3->InputChannels(), 1U);
2326 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
2327 std::cerr << "Device " << device3 << " is opened (stream " << stream3.get()
2328 << ")" << std::endl;
2330 // Close device 1, so the native input device is switched from device 1 to
2331 // device 2.
2332 switchNativeDevice(std::move(stream1), track1, listener1, stream2, track2);
2333 EXPECT_TRUE(stream2->mHasInput);
2334 EXPECT_TRUE(stream2->mHasOutput);
2335 EXPECT_EQ(stream2->InputChannels(), 2U);
2336 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
2338 NativeInputTrack* native = track2->Graph()->GetNativeInputTrackMainThread();
2339 ASSERT_TRUE(!!native);
2340 EXPECT_EQ(native->mDeviceId, device2);
2343 // Close device 2, so the native input device is switched from device 2 to
2344 // device 3.
2345 switchNativeDevice(std::move(stream2), track2, listener2, stream3, track3);
2346 EXPECT_TRUE(stream3->mHasInput);
2347 EXPECT_TRUE(stream3->mHasOutput);
2348 EXPECT_EQ(stream3->InputChannels(), 1U);
2349 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
2351 NativeInputTrack* native = track3->Graph()->GetNativeInputTrackMainThread();
2352 ASSERT_TRUE(!!native);
2353 EXPECT_EQ(native->mDeviceId, device3);
2356 // Clean up.
2357 std::cerr << "Close device " << device3 << std::endl;
2358 DispatchFunction([&] {
2359 track3->GraphImpl()->AppendMessage(
2360 MakeUnique<StopInputProcessing>(track3, listener3));
2361 track3->DisconnectDeviceInput();
2362 track3->Destroy();
2364 RefPtr<SmartMockCubebStream> destroyedStream =
2365 WaitFor(cubeb->StreamDestroyEvent());
2366 EXPECT_EQ(destroyedStream.get(), stream3.get());
2368 NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
2369 ASSERT_TRUE(!native);
2371 std::cerr << "No native input now" << std::endl;
2374 void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
2375 float aDriftFactor, uint32_t aRunTimeSeconds = 10,
2376 uint32_t aNumExpectedUnderruns = 0) {
2377 std::cerr << "TestCrossGraphPort input: " << aInputRate
2378 << ", output: " << aOutputRate << ", driftFactor: " << aDriftFactor
2379 << std::endl;
2381 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
2382 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2384 /* Primary graph: Create the graph. */
2385 MediaTrackGraph* primary = MediaTrackGraphImpl::GetInstance(
2386 MediaTrackGraph::SYSTEM_THREAD_DRIVER,
2387 /*Window ID*/ 1, aInputRate, nullptr, GetMainThreadSerialEventTarget());
2389 /* Partner graph: Create the graph. */
2390 MediaTrackGraph* partner = MediaTrackGraphImpl::GetInstance(
2391 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, aOutputRate,
2392 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
2393 GetMainThreadSerialEventTarget());
2395 const CubebUtils::AudioDeviceID inputDeviceId = (CubebUtils::AudioDeviceID)1;
2397 RefPtr<AudioProcessingTrack> processingTrack;
2398 RefPtr<AudioInputProcessing> listener;
2399 RefPtr<OnFallbackListener> primaryFallbackListener;
2400 DispatchFunction([&] {
2401 /* Primary graph: Create input track and open it */
2402 processingTrack = AudioProcessingTrack::Create(primary);
2403 listener = new AudioInputProcessing(2);
2404 QueueExpectIsPassThrough(processingTrack, listener);
2405 processingTrack->SetInputProcessing(listener);
2406 processingTrack->GraphImpl()->AppendMessage(
2407 MakeUnique<StartInputProcessing>(processingTrack, listener));
2408 processingTrack->ConnectDeviceInput(inputDeviceId, listener,
2409 PRINCIPAL_HANDLE_NONE);
2410 primaryFallbackListener = new OnFallbackListener(processingTrack);
2411 processingTrack->AddListener(primaryFallbackListener);
2414 RefPtr<SmartMockCubebStream> inputStream = WaitFor(cubeb->StreamInitEvent());
2416 while (
2417 inputStream->State()
2418 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
2419 .valueOr(true)) {
2420 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2423 // Wait for the primary AudioCallbackDriver to come into effect.
2424 while (primaryFallbackListener->OnFallback()) {
2425 EXPECT_EQ(inputStream->ManualDataCallback(0),
2426 MockCubebStream::KeepProcessing::Yes);
2427 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2430 RefPtr<CrossGraphTransmitter> transmitter;
2431 RefPtr<MediaInputPort> port;
2432 RefPtr<CrossGraphReceiver> receiver;
2433 RefPtr<OnFallbackListener> partnerFallbackListener;
2434 DispatchFunction([&] {
2435 processingTrack->RemoveListener(primaryFallbackListener);
2437 /* Partner graph: Create CrossGraphReceiver */
2438 receiver = partner->CreateCrossGraphReceiver(primary->GraphRate());
2440 /* Primary graph: Create CrossGraphTransmitter */
2441 transmitter = primary->CreateCrossGraphTransmitter(receiver);
2443 /* How the input track connects to another ProcessedMediaTrack.
2444 * Check in MediaManager how it is connected to AudioStreamTrack. */
2445 port = transmitter->AllocateInputPort(processingTrack);
2446 receiver->AddAudioOutput((void*)1, partner->PrimaryOutputDeviceID(), 0);
2448 partnerFallbackListener = new OnFallbackListener(receiver);
2449 receiver->AddListener(partnerFallbackListener);
2452 RefPtr<SmartMockCubebStream> partnerStream =
2453 WaitFor(cubeb->StreamInitEvent());
2455 while (
2456 partnerStream->State()
2457 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
2458 .valueOr(true)) {
2459 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2462 // Process the CrossGraphTransmitter on the primary graph.
2463 EXPECT_EQ(inputStream->ManualDataCallback(0),
2464 MockCubebStream::KeepProcessing::Yes);
2466 // Wait for the partner AudioCallbackDriver to come into effect.
2467 while (partnerFallbackListener->OnFallback()) {
2468 EXPECT_EQ(partnerStream->ManualDataCallback(0),
2469 MockCubebStream::KeepProcessing::Yes);
2470 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2473 DispatchFunction([&] { receiver->RemoveListener(partnerFallbackListener); });
2474 ProcessEventQueue();
2476 nsIThread* currentThread = NS_GetCurrentThread();
2477 cubeb_state inputState = CUBEB_STATE_STARTED;
2478 MediaEventListener inputStateListener = inputStream->StateEvent().Connect(
2479 currentThread, [&](cubeb_state aState) { inputState = aState; });
2480 cubeb_state partnerState = CUBEB_STATE_STARTED;
2481 MediaEventListener partnerStateListener = partnerStream->StateEvent().Connect(
2482 currentThread, [&](cubeb_state aState) { partnerState = aState; });
2484 const media::TimeUnit runtime = media::TimeUnit::FromSeconds(aRunTimeSeconds);
2485 // 10ms per iteration.
2486 const media::TimeUnit step = media::TimeUnit::FromSeconds(0.01);
2488 media::TimeUnit pos = media::TimeUnit::Zero();
2489 long inputFrames = 0;
2490 long outputFrames = 0;
2491 while (pos < runtime) {
2492 pos += step;
2493 const long newInputFrames = pos.ToTicksAtRate(aInputRate);
2494 const long newOutputFrames =
2495 (pos.MultDouble(aDriftFactor)).ToTicksAtRate(aOutputRate);
2496 EXPECT_EQ(inputStream->ManualDataCallback(newInputFrames - inputFrames),
2497 MockCubebStream::KeepProcessing::Yes);
2498 EXPECT_EQ(
2499 partnerStream->ManualDataCallback(newOutputFrames - outputFrames),
2500 MockCubebStream::KeepProcessing::Yes);
2502 inputFrames = newInputFrames;
2503 outputFrames = newOutputFrames;
2507 DispatchFunction([&] {
2508 // Clean up on MainThread
2509 receiver->RemoveAudioOutput((void*)1);
2510 receiver->Destroy();
2511 transmitter->Destroy();
2512 port->Destroy();
2513 processingTrack->GraphImpl()->AppendMessage(
2514 MakeUnique<StopInputProcessing>(processingTrack, listener));
2515 processingTrack->DisconnectDeviceInput();
2516 processingTrack->Destroy();
2519 ProcessEventQueue();
2521 EXPECT_EQ(inputStream->ManualDataCallback(0),
2522 MockCubebStream::KeepProcessing::Yes);
2523 EXPECT_EQ(partnerStream->ManualDataCallback(0),
2524 MockCubebStream::KeepProcessing::Yes);
2526 EXPECT_EQ(inputStream->ManualDataCallback(128),
2527 MockCubebStream::KeepProcessing::No);
2528 EXPECT_EQ(partnerStream->ManualDataCallback(128),
2529 MockCubebStream::KeepProcessing::No);
2531 uint32_t inputFrequency = inputStream->InputFrequency();
2533 uint64_t preSilenceSamples;
2534 float estimatedFreq;
2535 uint32_t nrDiscontinuities;
2536 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
2537 WaitFor(partnerStream->OutputVerificationEvent());
2539 EXPECT_NEAR(estimatedFreq, inputFrequency / aDriftFactor, 5);
2540 // Note that pre-silence is in the output rate. The buffering is on the input
2541 // side. There is one block buffered in NativeInputTrack. Then
2542 // AudioDriftCorrection sets its pre-buffering so that *after* the first
2543 // resample of real input data, the buffer contains enough data to match the
2544 // desired level, which is initially 50ms. I.e. silence = buffering -
2545 // inputStep + outputStep. Note that the steps here are rounded up to block
2546 // size.
2547 const media::TimeUnit inputBuffering(WEBAUDIO_BLOCK_SIZE, aInputRate);
2548 const media::TimeUnit buffering =
2549 media::TimeUnit::FromSeconds(0.05).ToBase(aInputRate);
2550 const media::TimeUnit inputStepSize(
2551 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(
2552 step.ToTicksAtRate(aInputRate)),
2553 aInputRate);
2554 const media::TimeUnit outputStepSize =
2555 media::TimeUnit(MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(
2556 step.ToBase(aOutputRate)
2557 .MultDouble(aDriftFactor)
2558 .ToTicksAtRate(aOutputRate)),
2559 aOutputRate)
2560 .ToBase(aInputRate);
2561 const uint32_t expectedPreSilence =
2562 (outputStepSize + inputBuffering + buffering - inputStepSize)
2563 .ToBase(aInputRate)
2564 .ToBase<media::TimeUnit::CeilingPolicy>(aOutputRate)
2565 .ToTicksAtRate(aOutputRate);
2566 // Use a margin of 0.1% of the expected pre-silence, since the resampler is
2567 // adapting to drift and will process the pre-silence frames. Because of
2568 // rounding errors, we don't use a margin lower than 1.
2569 const uint32_t margin = std::max(1U, expectedPreSilence / 1000);
2570 EXPECT_NEAR(preSilenceSamples, expectedPreSilence, margin);
2571 // The waveform from AudioGenerator starts at 0, but we don't control its
2572 // ending, so we expect a discontinuity there. For each expected underrun
2573 // there could be an additional 2 discontinuities (start and end of the silent
2574 // period).
2575 EXPECT_LE(nrDiscontinuities, 1U + 2 * aNumExpectedUnderruns);
2577 SpinEventLoopUntil("streams have stopped"_ns, [&] {
2578 return inputState == CUBEB_STATE_STOPPED &&
2579 partnerState == CUBEB_STATE_STOPPED;
2581 inputStateListener.Disconnect();
2582 partnerStateListener.Disconnect();
2585 TEST(TestAudioTrackGraph, CrossGraphPort)
2587 TestCrossGraphPort(44100, 44100, 1);
2588 TestCrossGraphPort(44100, 44100, 1.006);
2589 TestCrossGraphPort(44100, 44100, 0.994);
2591 TestCrossGraphPort(48000, 44100, 1);
2592 TestCrossGraphPort(48000, 44100, 1.006);
2593 TestCrossGraphPort(48000, 44100, 0.994);
2595 TestCrossGraphPort(44100, 48000, 1);
2596 TestCrossGraphPort(44100, 48000, 1.006);
2597 TestCrossGraphPort(44100, 48000, 0.994);
2599 TestCrossGraphPort(52110, 17781, 1);
2600 TestCrossGraphPort(52110, 17781, 1.006);
2601 TestCrossGraphPort(52110, 17781, 0.994);
2604 TEST(TestAudioTrackGraph, CrossGraphPortUnderrun)
2606 TestCrossGraphPort(44100, 44100, 1.01, 30, 1);
2607 TestCrossGraphPort(44100, 44100, 1.03, 40, 3);
2609 TestCrossGraphPort(48000, 44100, 1.01, 30, 1);
2610 TestCrossGraphPort(48000, 44100, 1.03, 40, 3);
2612 TestCrossGraphPort(44100, 48000, 1.01, 30, 1);
2613 TestCrossGraphPort(44100, 48000, 1.03, 40, 3);
2615 TestCrossGraphPort(52110, 17781, 1.01, 30, 1);
2616 TestCrossGraphPort(52110, 17781, 1.03, 40, 3);
2619 TEST(TestAudioTrackGraph, SecondaryOutputDevice)
2621 MockCubeb* cubeb = new MockCubeb();
2622 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2624 const TrackRate primaryRate = 48000;
2625 const TrackRate secondaryRate = 44100; // for secondary output device
2627 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2628 MediaTrackGraph::SYSTEM_THREAD_DRIVER,
2629 /*Window ID*/ 1, primaryRate, nullptr, GetMainThreadSerialEventTarget());
2631 RefPtr<AudioProcessingTrack> processingTrack;
2632 RefPtr<AudioInputProcessing> listener;
2633 DispatchFunction([&] {
2634 /* Create an input track and connect it to a device */
2635 processingTrack = AudioProcessingTrack::Create(graph);
2636 listener = new AudioInputProcessing(2);
2637 QueueExpectIsPassThrough(processingTrack, listener);
2638 processingTrack->SetInputProcessing(listener);
2639 processingTrack->GraphImpl()->AppendMessage(
2640 MakeUnique<StartInputProcessing>(processingTrack, listener));
2641 processingTrack->ConnectDeviceInput(nullptr, listener,
2642 PRINCIPAL_HANDLE_NONE);
2644 RefPtr<SmartMockCubebStream> primaryStream =
2645 WaitFor(cubeb->StreamInitEvent());
2647 const void* secondaryDeviceID = CubebUtils::AudioDeviceID(2);
2648 DispatchFunction([&] {
2649 processingTrack->AddAudioOutput(nullptr, secondaryDeviceID, secondaryRate);
2650 processingTrack->SetAudioOutputVolume(nullptr, 0.f);
2652 RefPtr<SmartMockCubebStream> secondaryStream =
2653 WaitFor(cubeb->StreamInitEvent());
2654 EXPECT_EQ(secondaryStream->GetOutputDeviceID(), secondaryDeviceID);
2655 EXPECT_EQ(static_cast<TrackRate>(secondaryStream->SampleRate()),
2656 secondaryRate);
2658 nsIThread* currentThread = NS_GetCurrentThread();
2659 uint32_t audioFrames = 0; // excludes pre-silence
2660 MediaEventListener audioListener =
2661 secondaryStream->FramesVerifiedEvent().Connect(
2662 currentThread, [&](uint32_t aFrames) { audioFrames += aFrames; });
2664 // Wait for 100ms of pre-silence to verify that SetAudioOutputVolume() is
2665 // effective.
2666 uint32_t processedFrames = 0;
2667 WaitUntil(secondaryStream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2668 processedFrames += aFrames;
2669 return processedFrames > static_cast<uint32_t>(secondaryRate / 10);
2671 EXPECT_EQ(audioFrames, 0U) << "audio frames at zero volume";
2673 secondaryStream->SetOutputRecordingEnabled(true);
2674 DispatchFunction(
2675 [&] { processingTrack->SetAudioOutputVolume(nullptr, 1.f); });
2677 // Wait for enough audio after initial silence to check the frequency.
2678 SpinEventLoopUntil("200ms of audio"_ns, [&] {
2679 return audioFrames > static_cast<uint32_t>(secondaryRate / 5);
2681 audioListener.Disconnect();
2683 // Stop recording now so as not to record the discontinuity when the
2684 // CrossGraphReceiver is removed from the secondary graph before its
2685 // AudioCallbackDriver is stopped.
2686 secondaryStream->SetOutputRecordingEnabled(false);
2688 DispatchFunction([&] { processingTrack->RemoveAudioOutput(nullptr); });
2689 WaitFor(secondaryStream->OutputVerificationEvent());
2690 // The frequency from OutputVerificationEvent() is estimated by
2691 // AudioVerifier from a zero-crossing count. When the discontinuity from
2692 // the volume change is resampled, the discontinuity presents as
2693 // oscillations, which increase the zero-crossing count and corrupt the
2694 // frequency estimate. Trim off sufficient leading from the output to
2695 // remove this discontinuity.
2696 uint32_t channelCount = secondaryStream->OutputChannels();
2697 nsTArray<AudioDataValue> output = secondaryStream->TakeRecordedOutput();
2698 size_t leadingIndex = 0;
2699 for (; leadingIndex < output.Length() && output[leadingIndex] == 0.f;
2700 leadingIndex += channelCount) {
2702 leadingIndex += 10 * channelCount; // skip discontinuity oscillations
2703 EXPECT_LT(leadingIndex, output.Length());
2704 auto trimmed = Span(output).From(std::min(leadingIndex, output.Length()));
2705 size_t frameCount = trimmed.Length() / channelCount;
2706 uint32_t inputFrequency = primaryStream->InputFrequency();
2707 AudioVerifier<AudioDataValue> verifier(secondaryRate, inputFrequency);
2708 verifier.AppendDataInterleaved(trimmed.Elements(), frameCount, channelCount);
2709 EXPECT_EQ(verifier.EstimatedFreq(), inputFrequency);
2710 // AudioVerifier considers the previous value before the initial sample to
2711 // be zero and so considers any initial sample >> 0 to be a discontinuity.
2712 EXPECT_EQ(verifier.CountDiscontinuities(), 1U);
2714 DispatchFunction([&] {
2715 // Clean up
2716 processingTrack->GraphImpl()->AppendMessage(
2717 MakeUnique<StopInputProcessing>(processingTrack, listener));
2718 processingTrack->DisconnectDeviceInput();
2719 processingTrack->Destroy();
2721 WaitFor(primaryStream->OutputVerificationEvent());
2724 // Test when AudioInputProcessing expects clock drift
2725 TEST(TestAudioInputProcessing, ClockDriftExpectation)
2727 MockCubeb* cubeb = new MockCubeb();
2728 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2730 const TrackRate rate = 44100;
2732 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2733 MediaTrackGraph::SYSTEM_THREAD_DRIVER,
2734 /*Window ID*/ 1, rate, nullptr, GetMainThreadSerialEventTarget());
2736 auto createInputProcessing =
2737 [&](CubebUtils::AudioDeviceID aDeviceID,
2738 RefPtr<AudioProcessingTrack>* aProcessingTrack,
2739 RefPtr<AudioInputProcessing>* aInputProcessing) {
2740 /* Create an input track and connect it to a device */
2741 const int32_t channelCount = 2;
2742 RefPtr processingTrack = AudioProcessingTrack::Create(graph);
2743 RefPtr inputProcessing = new AudioInputProcessing(channelCount);
2744 processingTrack->SetInputProcessing(inputProcessing);
2745 MediaEnginePrefs settings;
2746 settings.mChannels = channelCount;
2747 settings.mAecOn = true;
2748 QueueApplySettings(processingTrack, inputProcessing, settings);
2749 processingTrack->GraphImpl()->AppendMessage(
2750 MakeUnique<StartInputProcessing>(processingTrack, inputProcessing));
2751 processingTrack->ConnectDeviceInput(aDeviceID, inputProcessing,
2752 PRINCIPAL_HANDLE_NONE);
2753 aProcessingTrack->swap(processingTrack);
2754 aInputProcessing->swap(inputProcessing);
2757 // Native input, which uses a duplex stream
2758 RefPtr<AudioProcessingTrack> processingTrack1;
2759 RefPtr<AudioInputProcessing> inputProcessing1;
2760 DispatchFunction([&] {
2761 createInputProcessing(nullptr, &processingTrack1, &inputProcessing1);
2763 // Non-native input
2764 const auto* nonNativeInputDeviceID = CubebUtils::AudioDeviceID(1);
2765 RefPtr<AudioProcessingTrack> processingTrack2;
2766 RefPtr<AudioInputProcessing> inputProcessing2;
2767 DispatchFunction([&] {
2768 createInputProcessing(nonNativeInputDeviceID, &processingTrack2,
2769 &inputProcessing2);
2770 processingTrack2->AddAudioOutput(nullptr, nullptr, rate);
2773 RefPtr<SmartMockCubebStream> primaryStream;
2774 RefPtr<SmartMockCubebStream> nonNativeInputStream;
2775 WaitUntil(cubeb->StreamInitEvent(),
2776 [&](RefPtr<SmartMockCubebStream>&& stream) {
2777 if (stream->OutputChannels() > 0) {
2778 primaryStream = std::move(stream);
2779 return false;
2781 nonNativeInputStream = std::move(stream);
2782 return true;
2784 EXPECT_EQ(nonNativeInputStream->GetInputDeviceID(), nonNativeInputDeviceID);
2786 // Wait until non-native input signal reaches the output, when input
2787 // processing has run and so has been configured.
2788 WaitFor(primaryStream->FramesVerifiedEvent());
2790 const void* secondaryOutputDeviceID = CubebUtils::AudioDeviceID(2);
2791 DispatchFunction([&] {
2792 // Check input processing config with output to primary device.
2793 processingTrack1->QueueControlMessageWithNoShutdown([&] {
2794 EXPECT_FALSE(inputProcessing1->HadAECAndDrift());
2795 EXPECT_TRUE(inputProcessing2->HadAECAndDrift());
2798 // Switch output to a secondary device.
2799 processingTrack2->RemoveAudioOutput(nullptr);
2800 processingTrack2->AddAudioOutput(nullptr, secondaryOutputDeviceID, rate);
2803 RefPtr<SmartMockCubebStream> secondaryOutputStream =
2804 WaitFor(cubeb->StreamInitEvent());
2805 EXPECT_EQ(secondaryOutputStream->GetOutputDeviceID(),
2806 secondaryOutputDeviceID);
2808 WaitFor(secondaryOutputStream->FramesVerifiedEvent());
2809 DispatchFunction([&] {
2810 // Check input processing config with output to secondary device.
2811 processingTrack1->QueueControlMessageWithNoShutdown([&] {
2812 EXPECT_TRUE(inputProcessing1->HadAECAndDrift());
2813 EXPECT_TRUE(inputProcessing2->HadAECAndDrift());
2817 auto destroyInputProcessing = [&](AudioProcessingTrack* aProcessingTrack,
2818 AudioInputProcessing* aInputProcessing) {
2819 aProcessingTrack->GraphImpl()->AppendMessage(
2820 MakeUnique<StopInputProcessing>(aProcessingTrack, aInputProcessing));
2821 aProcessingTrack->DisconnectDeviceInput();
2822 aProcessingTrack->Destroy();
2825 DispatchFunction([&] {
2826 // Clean up
2827 destroyInputProcessing(processingTrack1, inputProcessing1);
2828 destroyInputProcessing(processingTrack2, inputProcessing2);
2830 // Wait for stream stop to ensure that expectations have been checked.
2831 WaitFor(nonNativeInputStream->OutputVerificationEvent());
2833 #endif // MOZ_WEBRTC
2835 TEST(TestAudioTrackGraph, PlatformProcessing)
2837 constexpr cubeb_input_processing_params allParams =
2838 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
2839 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
2840 CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
2841 CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
2842 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
2843 cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
2844 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2846 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2847 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2848 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2849 nullptr, GetMainThreadSerialEventTarget());
2851 const CubebUtils::AudioDeviceID device = (CubebUtils::AudioDeviceID)1;
2853 // Set up mock listener.
2854 RefPtr<MockAudioDataListener> listener = MakeRefPtr<MockAudioDataListener>();
2855 EXPECT_CALL(*listener, IsVoiceInput).WillRepeatedly(Return(true));
2856 EXPECT_CALL(*listener, RequestedInputChannelCount).WillRepeatedly(Return(1));
2857 EXPECT_CALL(*listener, RequestedInputProcessingParams)
2858 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
2859 EXPECT_CALL(*listener, Disconnect);
2861 // Expectations.
2862 const Result<cubeb_input_processing_params, int> notSupportedResult(
2863 Err(CUBEB_ERROR_NOT_SUPPORTED));
2864 const Result<cubeb_input_processing_params, int> errorResult(
2865 Err(CUBEB_ERROR));
2866 const Result<cubeb_input_processing_params, int> noneResult(
2867 CUBEB_INPUT_PROCESSING_PARAM_NONE);
2868 const Result<cubeb_input_processing_params, int> echoResult(
2869 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
2870 const Result<cubeb_input_processing_params, int> noiseResult(
2871 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
2872 Atomic<int> numProcessingParamsResults(0);
2874 InSequence s;
2875 // On first driver start.
2876 EXPECT_CALL(*listener,
2877 NotifySetRequestedInputProcessingParams(
2878 graph, 1, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
2879 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2880 graph, 1, Eq(std::ref(echoResult))))
2881 .WillOnce([&] { ++numProcessingParamsResults; });
2882 // After requesting something else.
2883 EXPECT_CALL(*listener,
2884 NotifySetRequestedInputProcessingParams(
2885 graph, 2, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
2886 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2887 graph, 2, Eq(std::ref(noiseResult))))
2888 .WillOnce([&] { ++numProcessingParamsResults; });
2889 // After error request.
2890 EXPECT_CALL(*listener,
2891 NotifySetRequestedInputProcessingParams(
2892 graph, 3, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
2893 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2894 graph, 3, Eq(std::ref(errorResult))))
2895 .WillOnce([&] { ++numProcessingParamsResults; });
2896 // After requesting None, because of the previous error.
2897 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParams(
2898 graph, 4, CUBEB_INPUT_PROCESSING_PARAM_NONE));
2899 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2900 graph, 4, Eq(std::ref(noneResult))))
2901 .WillOnce([&] { ++numProcessingParamsResults; });
2902 // After driver switch.
2903 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2904 graph, 4, Eq(std::ref(noneResult))))
2905 .WillOnce([&] { ++numProcessingParamsResults; });
2906 // After requesting something not supported.
2907 EXPECT_CALL(*listener,
2908 NotifySetRequestedInputProcessingParams(
2909 graph, 5, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
2910 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2911 graph, 5, Eq(std::ref(noneResult))))
2912 .WillOnce([&] { ++numProcessingParamsResults; });
2913 // After requesting something with backend not supporting processing params.
2914 EXPECT_CALL(*listener,
2915 NotifySetRequestedInputProcessingParams(
2916 graph, 6, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
2917 EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
2918 graph, 6, Eq(std::ref(notSupportedResult))))
2919 .WillOnce([&] { ++numProcessingParamsResults; });
2922 // Open a device.
2923 RefPtr<TestDeviceInputConsumerTrack> track;
2924 RefPtr<OnFallbackListener> fallbackListener;
2925 DispatchFunction([&] {
2926 track = TestDeviceInputConsumerTrack::Create(graph);
2927 track->ConnectDeviceInput(device, listener, PRINCIPAL_HANDLE_NONE);
2928 fallbackListener = new OnFallbackListener(track);
2929 track->AddListener(fallbackListener);
2932 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
2933 EXPECT_TRUE(stream->mHasInput);
2934 EXPECT_TRUE(stream->mHasOutput);
2935 EXPECT_EQ(stream->InputChannels(), 1U);
2936 EXPECT_EQ(stream->GetInputDeviceID(), device);
2938 while (
2939 stream->State()
2940 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
2941 .valueOr(true)) {
2942 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2945 const auto waitForResult = [&](int aNumResult) {
2946 while (numProcessingParamsResults < aNumResult) {
2947 EXPECT_EQ(stream->ManualDataCallback(0),
2948 MockCubebStream::KeepProcessing::Yes);
2949 NS_ProcessNextEvent();
2950 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2954 // Wait for the AudioCallbackDriver to come into effect.
2955 while (fallbackListener->OnFallback()) {
2956 EXPECT_EQ(stream->ManualDataCallback(0),
2957 MockCubebStream::KeepProcessing::Yes);
2958 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2961 // Wait for the first result after driver creation.
2962 waitForResult(1);
2964 // Request new processing params.
2965 EXPECT_CALL(*listener, RequestedInputProcessingParams)
2966 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
2967 waitForResult(2);
2969 // Test with returning error on new request.
2970 cubeb->SetInputProcessingApplyRv(CUBEB_ERROR);
2971 EXPECT_CALL(*listener, RequestedInputProcessingParams)
2972 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
2973 waitForResult(3);
2975 // Test unsetting all params.
2976 cubeb->SetInputProcessingApplyRv(CUBEB_OK);
2977 EXPECT_CALL(*listener, RequestedInputProcessingParams)
2978 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
2979 waitForResult(4);
2981 // Switch driver.
2982 EXPECT_CALL(*listener, RequestedInputChannelCount).WillRepeatedly(Return(2));
2983 DispatchFunction([&] {
2984 track->QueueControlMessageWithNoShutdown(
2985 [&] { graph->ReevaluateInputDevice(device); });
2987 ProcessEventQueue();
2988 // Process the reevaluation message.
2989 EXPECT_EQ(stream->ManualDataCallback(0),
2990 MockCubebStream::KeepProcessing::Yes);
2991 // Perform the switch.
2992 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
2993 EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
2994 std::tie(stream) = WaitFor(initPromise).unwrap()[0];
2995 EXPECT_TRUE(stream->mHasInput);
2996 EXPECT_TRUE(stream->mHasOutput);
2997 EXPECT_EQ(stream->InputChannels(), 2U);
2998 EXPECT_EQ(stream->GetInputDeviceID(), device);
3000 while (
3001 stream->State()
3002 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
3003 .valueOr(true)) {
3004 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3007 // Wait for the new AudioCallbackDriver to come into effect.
3008 fallbackListener->Reset();
3009 while (fallbackListener->OnFallback()) {
3010 EXPECT_EQ(stream->ManualDataCallback(0),
3011 MockCubebStream::KeepProcessing::Yes);
3012 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3015 // Wait for the first result after driver creation.
3016 waitForResult(5);
3018 // Test requesting something not supported.
3019 cubeb->SetSupportedInputProcessingParams(
3020 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
3021 CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
3022 CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION,
3023 CUBEB_OK);
3024 EXPECT_CALL(*listener, RequestedInputProcessingParams)
3025 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
3026 waitForResult(6);
3028 // Test requesting something when unsupported by backend.
3029 cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
3030 CUBEB_ERROR_NOT_SUPPORTED);
3031 EXPECT_CALL(*listener, RequestedInputProcessingParams)
3032 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
3033 waitForResult(7);
3035 // Clean up.
3036 DispatchFunction([&] {
3037 track->RemoveListener(fallbackListener);
3038 track->Destroy();
3040 ProcessEventQueue();
3041 // Process the destroy message.
3042 EXPECT_EQ(stream->ManualDataCallback(0),
3043 MockCubebStream::KeepProcessing::Yes);
3044 // Shut down.
3045 EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
3046 RefPtr<SmartMockCubebStream> destroyedStream =
3047 WaitFor(cubeb->StreamDestroyEvent());
3048 EXPECT_EQ(destroyedStream.get(), stream.get());
3050 NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
3051 ASSERT_TRUE(!native);
3055 TEST(TestAudioTrackGraph, PlatformProcessingNonNativeToNativeSwitch)
3057 constexpr cubeb_input_processing_params allParams =
3058 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
3059 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
3060 CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
3061 CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
3062 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
3063 cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
3064 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
3066 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
3067 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
3068 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
3069 nullptr, GetMainThreadSerialEventTarget());
3071 const CubebUtils::AudioDeviceID firstDevice = (CubebUtils::AudioDeviceID)1;
3072 const CubebUtils::AudioDeviceID secondDevice = (CubebUtils::AudioDeviceID)2;
3074 // Set up mock listeners.
3075 RefPtr<MockAudioDataListener> firstListener =
3076 MakeRefPtr<MockAudioDataListener>();
3077 EXPECT_CALL(*firstListener, IsVoiceInput).WillRepeatedly(Return(true));
3078 EXPECT_CALL(*firstListener, RequestedInputChannelCount)
3079 .WillRepeatedly(Return(1));
3080 EXPECT_CALL(*firstListener, RequestedInputProcessingParams)
3081 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
3083 RefPtr<MockAudioDataListener> secondListener =
3084 MakeRefPtr<MockAudioDataListener>();
3085 EXPECT_CALL(*secondListener, IsVoiceInput).WillRepeatedly(Return(true));
3086 EXPECT_CALL(*secondListener, RequestedInputChannelCount)
3087 .WillRepeatedly(Return(1));
3088 EXPECT_CALL(*secondListener, RequestedInputProcessingParams)
3089 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
3091 // Expectations.
3092 const Result<cubeb_input_processing_params, int> noneResult(
3093 CUBEB_INPUT_PROCESSING_PARAM_NONE);
3094 const Result<cubeb_input_processing_params, int> echoResult(
3095 CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
3096 const Result<cubeb_input_processing_params, int> noiseResult(
3097 CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
3098 Atomic<int> numProcessingParamsResults(0);
3100 InSequence s;
3101 // On first driver start.
3102 EXPECT_CALL(*firstListener, NotifySetRequestedInputProcessingParamsResult(
3103 graph, 0, Eq(std::ref(noneResult))))
3104 .WillOnce([&] { ++numProcessingParamsResults; });
3105 // After param update.
3106 EXPECT_CALL(*firstListener,
3107 NotifySetRequestedInputProcessingParams(
3108 graph, 1, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
3109 EXPECT_CALL(*firstListener, NotifySetRequestedInputProcessingParamsResult(
3110 graph, 1, Eq(std::ref(noiseResult))))
3111 .WillOnce([&] { ++numProcessingParamsResults; });
3112 // After second param update.
3113 EXPECT_CALL(*firstListener,
3114 NotifySetRequestedInputProcessingParams(
3115 graph, 2, CUBEB_INPUT_PROCESSING_PARAM_NONE));
3116 EXPECT_CALL(*firstListener, NotifySetRequestedInputProcessingParamsResult(
3117 graph, 2, Eq(std::ref(noneResult))))
3118 .WillOnce([&] { ++numProcessingParamsResults; });
3119 // On non-native device's start.
3120 EXPECT_CALL(*secondListener,
3121 NotifySetRequestedInputProcessingParams(
3122 graph, 1, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
3123 EXPECT_CALL(*secondListener, NotifySetRequestedInputProcessingParamsResult(
3124 graph, 1, Eq(std::ref(echoResult))))
3125 .WillOnce([&] { ++numProcessingParamsResults; });
3126 // After switch to native device for second device.
3127 EXPECT_CALL(*firstListener, Disconnect);
3128 EXPECT_CALL(*secondListener, Disconnect);
3129 EXPECT_CALL(*secondListener,
3130 NotifySetRequestedInputProcessingParams(
3131 graph, 1, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
3132 EXPECT_CALL(*secondListener, NotifySetRequestedInputProcessingParamsResult(
3133 graph, 1, Eq(std::ref(echoResult))))
3134 .WillOnce([&] { ++numProcessingParamsResults; });
3135 // After param update.
3136 EXPECT_CALL(*secondListener,
3137 NotifySetRequestedInputProcessingParams(
3138 graph, 2, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
3139 EXPECT_CALL(*secondListener, NotifySetRequestedInputProcessingParamsResult(
3140 graph, 2, Eq(std::ref(noiseResult))))
3141 .WillOnce([&] { ++numProcessingParamsResults; });
3142 EXPECT_CALL(*secondListener, Disconnect);
3145 // Open the first input device. It will be the native device of the graph.
3146 RefPtr<TestDeviceInputConsumerTrack> firstTrack;
3147 RefPtr<OnFallbackListener> fallbackListener;
3148 DispatchFunction([&] {
3149 firstTrack = TestDeviceInputConsumerTrack::Create(graph);
3150 firstTrack->ConnectDeviceInput(firstDevice, firstListener,
3151 PRINCIPAL_HANDLE_NONE);
3152 fallbackListener = new OnFallbackListener(firstTrack);
3153 firstTrack->AddListener(fallbackListener);
3156 RefPtr<SmartMockCubebStream> nativeStream = WaitFor(cubeb->StreamInitEvent());
3157 RefPtr<SmartMockCubebStream> nonNativeStream;
3158 EXPECT_TRUE(nativeStream->mHasInput);
3159 EXPECT_TRUE(nativeStream->mHasOutput);
3160 EXPECT_EQ(nativeStream->InputChannels(), 1U);
3161 EXPECT_EQ(nativeStream->GetInputDeviceID(), firstDevice);
3163 while (
3164 nativeStream->State()
3165 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
3166 .valueOr(true)) {
3167 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3170 // Wait for the AudioCallbackDriver to come into effect.
3171 while (fallbackListener->OnFallback()) {
3172 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3173 MockCubebStream::KeepProcessing::Yes);
3174 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3177 const auto waitForResult = [&](int aNumResult) {
3178 while (numProcessingParamsResults < aNumResult) {
3179 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3180 MockCubebStream::KeepProcessing::Yes);
3181 if (nonNativeStream) {
3182 EXPECT_EQ(nonNativeStream->ManualDataCallback(0),
3183 MockCubebStream::KeepProcessing::Yes);
3185 NS_ProcessNextEvent();
3186 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3190 // Wait for the first result after driver creation.
3191 waitForResult(1);
3193 // Request new processing params.
3194 EXPECT_CALL(*firstListener, RequestedInputProcessingParams)
3195 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
3196 waitForResult(2);
3198 // Again request new processing params, to move the processing params
3199 // generation of the first device higher so as to avoid ambiguity with the
3200 // processing params generation of the second device.
3201 EXPECT_CALL(*firstListener, RequestedInputProcessingParams)
3202 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
3203 waitForResult(3);
3205 // Open the second input device. It will be a non-native device of the graph.
3206 RefPtr<TestDeviceInputConsumerTrack> secondTrack;
3207 DispatchFunction([&] {
3208 secondTrack = TestDeviceInputConsumerTrack::Create(graph);
3209 secondTrack->ConnectDeviceInput(secondDevice, secondListener,
3210 PRINCIPAL_HANDLE_NONE);
3211 secondTrack->AddListener(fallbackListener);
3214 auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
3215 DispatchFunction([&] {
3216 // TakeN dispatches a task that runs SpinEventLoopUntil while blocking the
3217 // caller. Make sure any event loop spinning that needs to be intertwined
3218 // with driving the graph runs in a nested task, so as to not block the test
3219 // flow. I.e. if the caller that gets blocked on the TakeN task is WaitFor
3220 // below, we will deadlock, because the graph must be iterated to unblock
3221 // the TakeN task.
3222 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3223 MockCubebStream::KeepProcessing::Yes);
3224 ProcessEventQueue();
3225 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3226 MockCubebStream::KeepProcessing::Yes);
3229 std::tie(nonNativeStream) = WaitFor(initPromise).unwrap()[0];
3230 EXPECT_TRUE(nonNativeStream->mHasInput);
3231 EXPECT_FALSE(nonNativeStream->mHasOutput);
3232 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
3233 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), secondDevice);
3235 while (
3236 nonNativeStream->State()
3237 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
3238 .valueOr(true)) {
3239 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3242 // Wait for the first result after non-native device input creation.
3243 waitForResult(4);
3245 // Disconnect native input. First non-native input should be promoted to
3246 // native, and processing param generation should increase monotonically
3247 // through the switch.
3248 DispatchFunction([&] {
3249 firstTrack->RemoveListener(fallbackListener);
3250 secondTrack->AddListener(fallbackListener);
3251 firstTrack->DisconnectDeviceInput();
3253 ProcessEventQueue();
3254 initPromise = TakeN(cubeb->StreamInitEvent(), 1);
3255 // Process the disconnect message, and check that the second device is now
3256 // used with the new graph driver.
3257 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3258 MockCubebStream::KeepProcessing::Yes);
3259 // Perform the switch.
3260 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3261 MockCubebStream::KeepProcessing::No);
3262 std::tie(nativeStream) = WaitFor(initPromise).unwrap()[0];
3263 EXPECT_TRUE(nativeStream->mHasInput);
3264 EXPECT_TRUE(nativeStream->mHasOutput);
3265 EXPECT_EQ(nativeStream->InputChannels(), 1U);
3266 EXPECT_EQ(nativeStream->GetInputDeviceID(), secondDevice);
3268 while (
3269 nativeStream->State()
3270 .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
3271 .valueOr(true)) {
3272 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3275 // Wait for the new AudioCallbackDriver to come into effect.
3276 fallbackListener->Reset();
3277 while (fallbackListener->OnFallback()) {
3278 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3279 MockCubebStream::KeepProcessing::Yes);
3280 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3283 // So that waitForResult doesn't make callbacks to it anymore.
3284 nonNativeStream = nullptr;
3286 // Wait for the first result after driver creation.
3287 waitForResult(5);
3289 // Request new processing params.
3290 EXPECT_CALL(*secondListener, RequestedInputProcessingParams)
3291 .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
3292 waitForResult(6);
3294 // Clean up.
3295 DispatchFunction([&] {
3296 firstTrack->Destroy();
3297 secondTrack->RemoveListener(fallbackListener);
3298 secondTrack->Destroy();
3300 auto destroyPromise = TakeN(cubeb->StreamDestroyEvent(), 1);
3301 DispatchFunction([&] {
3302 ProcessEventQueue();
3303 // Process the destroy message.
3304 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3305 MockCubebStream::KeepProcessing::Yes);
3306 // Shut down native.
3307 EXPECT_EQ(nativeStream->ManualDataCallback(0),
3308 MockCubebStream::KeepProcessing::No);
3310 RefPtr<SmartMockCubebStream> destroyedStream;
3311 std::tie(destroyedStream) = WaitFor(destroyPromise).unwrap()[0];
3312 EXPECT_EQ(destroyedStream, nativeStream);
3314 NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
3315 ASSERT_TRUE(!native);
3319 class MockProcessedMediaTrack : public ProcessedMediaTrack {
3320 public:
3321 explicit MockProcessedMediaTrack(TrackRate aRate)
3322 : ProcessedMediaTrack(aRate, MediaSegment::AUDIO, new AudioSegment()) {
3323 ON_CALL(*this, ProcessInput)
3324 .WillByDefault([segment = GetData<AudioSegment>()](
3325 GraphTime aFrom, GraphTime aTo, uint32_t aFlags) {
3326 segment->AppendNullData(aTo - aFrom);
3330 MOCK_METHOD(void, ProcessInput,
3331 (GraphTime aFrom, GraphTime aTo, uint32_t aFlags), (override));
3333 uint32_t NumberOfChannels() const override { return 2; };
3336 TEST(TestAudioTrackGraph, EmptyProcessingInterval)
3338 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
3339 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
3341 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
3342 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
3343 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
3344 nullptr, GetMainThreadSerialEventTarget());
3346 RefPtr processedTrack = new MockProcessedMediaTrack(graph->GraphRate());
3347 RefPtr fallbackListener = new OnFallbackListener(processedTrack);
3348 MockFunction<void(const char* name)> checkpoint;
3350 InSequence s;
3351 EXPECT_CALL(*processedTrack, ProcessInput).Times(AtLeast(1));
3352 EXPECT_CALL(checkpoint, Call(StrEq("before single iteration")));
3353 EXPECT_CALL(*processedTrack, ProcessInput).Times(1);
3354 EXPECT_CALL(checkpoint, Call(StrEq("before empty iteration")));
3355 EXPECT_CALL(checkpoint, Call(StrEq("after empty iteration")));
3357 DispatchFunction([&] {
3358 graph->AddTrack(processedTrack);
3359 processedTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
3360 processedTrack->AddListener(fallbackListener);
3363 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
3364 while (stream->State().isNothing()) {
3365 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3367 EXPECT_EQ(*stream->State(), CUBEB_STATE_STARTED);
3368 // Wait for the AudioCallbackDriver to come into effect.
3369 while (fallbackListener->OnFallback()) {
3370 EXPECT_EQ(stream->ManualDataCallback(1),
3371 MockCubebStream::KeepProcessing::Yes);
3372 std::this_thread::sleep_for(std::chrono::milliseconds(1));
3374 // The graph is now run by ManualDataCallback().
3375 GraphTime processedUpTo0 = processedTrack->GetEnd();
3376 EXPECT_GT(processedUpTo0, 0u);
3377 checkpoint.Call("before single iteration");
3378 // 128 matches the block size used by MediaTrackGraph and so is expected to
3379 // trigger another block of processing.
3380 EXPECT_EQ(stream->ManualDataCallback(128),
3381 MockCubebStream::KeepProcessing::Yes);
3382 GraphTime processedUpTo1 = processedTrack->GetEnd();
3383 EXPECT_EQ(processedUpTo1, processedUpTo0 + 128);
3384 // Test that ProcessInput() is not called in a graph iteration with no
3385 // frames to be rendered.
3386 checkpoint.Call("before empty iteration");
3387 EXPECT_EQ(stream->ManualDataCallback(0),
3388 MockCubebStream::KeepProcessing::Yes);
3389 checkpoint.Call("after empty iteration");
3390 EXPECT_EQ(processedTrack->GetEnd(), processedUpTo1);
3392 DispatchFunction([&] { processedTrack->Destroy(); });
3393 ProcessEventQueue();
3394 // Process the destroy message and drain the stream.
3395 auto destroyPromise = TakeN(cubeb->StreamDestroyEvent(), 1);
3396 while (stream->ManualDataCallback(0) ==
3397 MockCubebStream::KeepProcessing::Yes) {
3399 // Ensure the stream is no longer used by its MockCubeb before releasing our
3400 // reference, and before the next test might ForceSetCubebContext() to
3401 // destroy our cubeb.
3402 (void)WaitFor(destroyPromise).unwrap()[0];
3405 #undef Invoke
3406 #undef DispatchFunction
3407 #undef DispatchMethod