Bug 1887774 delay AudioProcessing initialization until required r=pehrsons
[gecko.git] / dom / media / gtest / TestAudioTrackGraph.cpp
blob408ec24a697f0b5ef0fbc37ca445287570cade3d
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;
25 // Short-hand for InvokeAsync on the current thread.
26 #define Invoke(f) InvokeAsync(GetCurrentSerialEventTarget(), __func__, f)
28 // Short-hand for DispatchToCurrentThread with a function.
29 #define DispatchFunction(f) \
30 NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
32 // Short-hand for DispatchToCurrentThread with a method with arguments
33 #define DispatchMethod(t, m, args...) \
34 NS_DispatchToCurrentThread(NewRunnableMethod(__func__, t, m, ##args))
36 namespace {
37 #ifdef MOZ_WEBRTC
39 * Common ControlMessages
41 struct StartInputProcessing : public ControlMessage {
42 const RefPtr<AudioProcessingTrack> mProcessingTrack;
43 const RefPtr<AudioInputProcessing> mInputProcessing;
45 StartInputProcessing(AudioProcessingTrack* aTrack,
46 AudioInputProcessing* aInputProcessing)
47 : ControlMessage(aTrack),
48 mProcessingTrack(aTrack),
49 mInputProcessing(aInputProcessing) {}
50 void Run() override { mInputProcessing->Start(mTrack->Graph()); }
53 struct StopInputProcessing : public ControlMessage {
54 const RefPtr<AudioInputProcessing> mInputProcessing;
56 explicit StopInputProcessing(AudioProcessingTrack* aTrack,
57 AudioInputProcessing* aInputProcessing)
58 : ControlMessage(aTrack), mInputProcessing(aInputProcessing) {}
59 void Run() override { mInputProcessing->Stop(mTrack->Graph()); }
62 void QueueApplySettings(AudioProcessingTrack* aTrack,
63 AudioInputProcessing* aInputProcessing,
64 const MediaEnginePrefs& aSettings) {
65 aTrack->QueueControlMessageWithNoShutdown(
66 [inputProcessing = RefPtr{aInputProcessing}, aSettings,
67 // If the track is not connected to a device then the particular
68 // AudioDeviceID (nullptr) passed to ReevaluateInputDevice() is not
69 // important.
70 deviceId = aTrack->DeviceId().valueOr(nullptr),
71 graph = aTrack->Graph()] {
72 inputProcessing->ApplySettings(graph, deviceId, aSettings);
73 });
76 void QueueExpectIsPassThrough(AudioProcessingTrack* aTrack,
77 AudioInputProcessing* aInputProcessing) {
78 aTrack->QueueControlMessageWithNoShutdown(
79 [inputProcessing = RefPtr{aInputProcessing}, graph = aTrack->Graph()] {
80 EXPECT_EQ(inputProcessing->IsPassThrough(graph), true);
81 });
83 #endif // MOZ_WEBRTC
85 class GoFaster : public ControlMessage {
86 MockCubeb* mCubeb;
88 public:
89 explicit GoFaster(MockCubeb* aCubeb)
90 : ControlMessage(nullptr), mCubeb(aCubeb) {}
91 void Run() override { mCubeb->GoFaster(); }
94 struct StartNonNativeInput : public ControlMessage {
95 const RefPtr<NonNativeInputTrack> mInputTrack;
96 RefPtr<AudioInputSource> mInputSource;
98 StartNonNativeInput(NonNativeInputTrack* aInputTrack,
99 RefPtr<AudioInputSource>&& aInputSource)
100 : ControlMessage(aInputTrack),
101 mInputTrack(aInputTrack),
102 mInputSource(std::move(aInputSource)) {}
103 void Run() override { mInputTrack->StartAudio(std::move(mInputSource)); }
106 struct StopNonNativeInput : public ControlMessage {
107 const RefPtr<NonNativeInputTrack> mInputTrack;
109 explicit StopNonNativeInput(NonNativeInputTrack* aInputTrack)
110 : ControlMessage(aInputTrack), mInputTrack(aInputTrack) {}
111 void Run() override { mInputTrack->StopAudio(); }
114 } // namespace
117 * The set of tests here are a bit special. In part because they're async and
118 * depends on the graph thread to function. In part because they depend on main
119 * thread stable state to send messages to the graph.
121 * Any message sent from the main thread to the graph through the graph's
122 * various APIs are scheduled to run in stable state. Stable state occurs after
123 * a task in the main thread eventloop has run to completion.
125 * Since gtests are generally sync and on main thread, calling into the graph
126 * may schedule a stable state runnable but with no task in the eventloop to
127 * trigger stable state. Therefore care must be taken to always call into the
128 * graph from a task, typically via InvokeAsync or a dispatch to main thread.
131 TEST(TestAudioTrackGraph, DifferentDeviceIDs)
133 MockCubeb* cubeb = new MockCubeb();
134 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
136 MediaTrackGraph* g1 = MediaTrackGraphImpl::GetInstance(
137 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
138 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
139 /*OutputDeviceID*/ nullptr, GetMainThreadSerialEventTarget());
141 MediaTrackGraph* g2 = MediaTrackGraphImpl::GetInstance(
142 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
143 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
144 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
145 GetMainThreadSerialEventTarget());
147 MediaTrackGraph* g1_2 = MediaTrackGraphImpl::GetInstance(
148 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
149 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
150 /*OutputDeviceID*/ nullptr, GetMainThreadSerialEventTarget());
152 MediaTrackGraph* g2_2 = MediaTrackGraphImpl::GetInstance(
153 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
154 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
155 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
156 GetMainThreadSerialEventTarget());
158 EXPECT_NE(g1, g2) << "Different graphs due to different device ids";
159 EXPECT_EQ(g1, g1_2) << "Same graphs for same device ids";
160 EXPECT_EQ(g2, g2_2) << "Same graphs for same device ids";
162 for (MediaTrackGraph* g : {g1, g2}) {
163 // Dummy track to make graph rolling. Add it and remove it to remove the
164 // graph from the global hash table and let it shutdown.
166 using SourceTrackPromise = MozPromise<SourceMediaTrack*, nsresult, true>;
167 auto p = Invoke([g] {
168 return SourceTrackPromise::CreateAndResolve(
169 g->CreateSourceTrack(MediaSegment::AUDIO), __func__);
172 WaitFor(cubeb->StreamInitEvent());
173 RefPtr<SourceMediaTrack> dummySource = WaitFor(p).unwrap();
175 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
177 WaitFor(cubeb->StreamDestroyEvent());
181 TEST(TestAudioTrackGraph, SetOutputDeviceID)
183 MockCubeb* cubeb = new MockCubeb();
184 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
186 // Set the output device id in GetInstance method confirm that it is the one
187 // used in cubeb_stream_init.
188 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
189 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
190 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
191 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(2),
192 GetMainThreadSerialEventTarget());
194 // Dummy track to make graph rolling. Add it and remove it to remove the
195 // graph from the global hash table and let it shutdown.
196 RefPtr<SourceMediaTrack> dummySource;
197 DispatchFunction(
198 [&] { dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO); });
200 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
202 EXPECT_EQ(stream->GetOutputDeviceID(), reinterpret_cast<cubeb_devid>(2))
203 << "After init confirm the expected output device id";
205 // Test has finished, destroy the track to shutdown the MTG.
206 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
207 WaitFor(cubeb->StreamDestroyEvent());
210 TEST(TestAudioTrackGraph, StreamName)
212 MockCubeb* cubeb = new MockCubeb();
213 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
215 // Initialize a graph with a system thread driver to check that the stream
216 // name survives the driver switch.
217 MediaTrackGraphImpl* graph = MediaTrackGraphImpl::GetInstance(
218 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
219 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
220 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
221 GetMainThreadSerialEventTarget());
222 nsLiteralCString name1("name1");
223 graph->CurrentDriver()->SetStreamName(name1);
225 // Dummy track to start the graph rolling and switch to an
226 // AudioCallbackDriver.
227 RefPtr<SourceMediaTrack> dummySource;
228 DispatchFunction(
229 [&] { dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO); });
231 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
232 EXPECT_STREQ(stream->StreamName(), name1.get());
234 // Test a name change on an existing stream.
235 nsLiteralCString name2("name2");
236 DispatchFunction([&] {
237 graph->QueueControlMessageWithNoShutdown(
238 [&] { graph->CurrentDriver()->SetStreamName(name2); });
240 nsCString name = WaitFor(stream->NameSetEvent());
241 EXPECT_EQ(name, name2);
243 // Test has finished. Destroy the track to shutdown the MTG.
244 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
245 WaitFor(cubeb->StreamDestroyEvent());
248 TEST(TestAudioTrackGraph, NotifyDeviceStarted)
250 MockCubeb* cubeb = new MockCubeb();
251 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
253 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
254 MediaTrackGraph::AUDIO_THREAD_DRIVER, /*Window ID*/ 1,
255 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
256 nullptr, GetMainThreadSerialEventTarget());
258 RefPtr<SourceMediaTrack> dummySource;
259 Unused << WaitFor(Invoke([&] {
260 // Dummy track to make graph rolling. Add it and remove it to remove the
261 // graph from the global hash table and let it shutdown.
262 dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO);
264 return graph->NotifyWhenDeviceStarted(nullptr);
265 }));
268 MediaTrackGraphImpl* graph = dummySource->GraphImpl();
269 MonitorAutoLock lock(graph->GetMonitor());
270 EXPECT_TRUE(graph->CurrentDriver()->AsAudioCallbackDriver());
271 EXPECT_TRUE(graph->CurrentDriver()->ThreadRunning());
274 // Test has finished, destroy the track to shutdown the MTG.
275 DispatchMethod(dummySource, &SourceMediaTrack::Destroy);
276 WaitFor(cubeb->StreamDestroyEvent());
279 TEST(TestAudioTrackGraph, NonNativeInputTrackStartAndStop)
281 MockCubeb* cubeb = new MockCubeb();
282 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
284 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
285 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
286 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
287 nullptr, GetMainThreadSerialEventTarget());
289 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
291 // Add a NonNativeInputTrack to graph, making graph create an output-only
292 // AudioCallbackDriver since NonNativeInputTrack is an audio-type MediaTrack.
293 RefPtr<NonNativeInputTrack> track;
294 DispatchFunction([&] {
295 track = new NonNativeInputTrack(graph->GraphRate(), deviceId,
296 PRINCIPAL_HANDLE_NONE);
297 graph->AddTrack(track);
300 RefPtr<SmartMockCubebStream> driverStream = WaitFor(cubeb->StreamInitEvent());
301 EXPECT_FALSE(driverStream->mHasInput);
302 EXPECT_TRUE(driverStream->mHasOutput);
304 // Main test below:
306 const AudioInputSource::Id sourceId = 1;
307 const uint32_t channels = 2;
308 const TrackRate rate = 48000;
310 // Start and stop the audio in NonNativeInputTrack.
312 struct DeviceInfo {
313 uint32_t mChannelCount;
314 AudioInputType mType;
316 using DeviceQueryPromise =
317 MozPromise<DeviceInfo, nsresult, /* IsExclusive = */ true>;
319 struct DeviceQueryMessage : public ControlMessage {
320 const NonNativeInputTrack* mInputTrack;
321 MozPromiseHolder<DeviceQueryPromise> mHolder;
323 DeviceQueryMessage(NonNativeInputTrack* aInputTrack,
324 MozPromiseHolder<DeviceQueryPromise>&& aHolder)
325 : ControlMessage(aInputTrack),
326 mInputTrack(aInputTrack),
327 mHolder(std::move(aHolder)) {}
328 void Run() override {
329 DeviceInfo info = {mInputTrack->NumberOfChannels(),
330 mInputTrack->DevicePreference()};
331 // mHolder.Resolve(info, __func__);
332 mTrack->GraphImpl()->Dispatch(NS_NewRunnableFunction(
333 "TestAudioTrackGraph::DeviceQueryMessage",
334 [holder = std::move(mHolder), devInfo = info]() mutable {
335 holder.Resolve(devInfo, __func__);
336 }));
340 // No input channels and device preference before start.
342 MozPromiseHolder<DeviceQueryPromise> h;
343 RefPtr<DeviceQueryPromise> p = h.Ensure(__func__);
344 DispatchFunction([&] {
345 track->GraphImpl()->AppendMessage(
346 MakeUnique<DeviceQueryMessage>(track.get(), std::move(h)));
348 Result<DeviceInfo, nsresult> r = WaitFor(p);
349 ASSERT_TRUE(r.isOk());
350 DeviceInfo info = r.unwrap();
352 EXPECT_EQ(info.mChannelCount, 0U);
353 EXPECT_EQ(info.mType, AudioInputType::Unknown);
356 DispatchFunction([&] {
357 track->GraphImpl()->AppendMessage(MakeUnique<StartNonNativeInput>(
358 track.get(), MakeRefPtr<AudioInputSource>(
359 MakeRefPtr<AudioInputSourceListener>(track.get()),
360 sourceId, deviceId, channels, true /* voice */,
361 PRINCIPAL_HANDLE_NONE, rate, graph->GraphRate())));
363 RefPtr<SmartMockCubebStream> nonNativeStream =
364 WaitFor(cubeb->StreamInitEvent());
365 EXPECT_TRUE(nonNativeStream->mHasInput);
366 EXPECT_FALSE(nonNativeStream->mHasOutput);
367 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), deviceId);
368 EXPECT_EQ(nonNativeStream->InputChannels(), channels);
369 EXPECT_EQ(nonNativeStream->SampleRate(), static_cast<uint32_t>(rate));
371 // Input channels and device preference should be set after start.
373 MozPromiseHolder<DeviceQueryPromise> h;
374 RefPtr<DeviceQueryPromise> p = h.Ensure(__func__);
375 DispatchFunction([&] {
376 track->GraphImpl()->AppendMessage(
377 MakeUnique<DeviceQueryMessage>(track.get(), std::move(h)));
379 Result<DeviceInfo, nsresult> r = WaitFor(p);
380 ASSERT_TRUE(r.isOk());
381 DeviceInfo info = r.unwrap();
383 EXPECT_EQ(info.mChannelCount, channels);
384 EXPECT_EQ(info.mType, AudioInputType::Voice);
387 Unused << WaitFor(nonNativeStream->FramesProcessedEvent());
389 DispatchFunction([&] {
390 track->GraphImpl()->AppendMessage(
391 MakeUnique<StopNonNativeInput>(track.get()));
393 RefPtr<SmartMockCubebStream> destroyedStream =
394 WaitFor(cubeb->StreamDestroyEvent());
395 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
397 // No input channels and device preference after stop.
399 MozPromiseHolder<DeviceQueryPromise> h;
400 RefPtr<DeviceQueryPromise> p = h.Ensure(__func__);
401 DispatchFunction([&] {
402 track->GraphImpl()->AppendMessage(
403 MakeUnique<DeviceQueryMessage>(track.get(), std::move(h)));
405 Result<DeviceInfo, nsresult> r = WaitFor(p);
406 ASSERT_TRUE(r.isOk());
407 DeviceInfo info = r.unwrap();
409 EXPECT_EQ(info.mChannelCount, 0U);
410 EXPECT_EQ(info.mType, AudioInputType::Unknown);
414 // Make sure the NonNativeInputTrack can restart and stop its audio.
416 DispatchFunction([&] {
417 track->GraphImpl()->AppendMessage(MakeUnique<StartNonNativeInput>(
418 track.get(), MakeRefPtr<AudioInputSource>(
419 MakeRefPtr<AudioInputSourceListener>(track.get()),
420 sourceId, deviceId, channels, true,
421 PRINCIPAL_HANDLE_NONE, rate, graph->GraphRate())));
423 RefPtr<SmartMockCubebStream> nonNativeStream =
424 WaitFor(cubeb->StreamInitEvent());
425 EXPECT_TRUE(nonNativeStream->mHasInput);
426 EXPECT_FALSE(nonNativeStream->mHasOutput);
427 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), deviceId);
428 EXPECT_EQ(nonNativeStream->InputChannels(), channels);
429 EXPECT_EQ(nonNativeStream->SampleRate(), static_cast<uint32_t>(rate));
431 Unused << WaitFor(nonNativeStream->FramesProcessedEvent());
433 DispatchFunction([&] {
434 track->GraphImpl()->AppendMessage(
435 MakeUnique<StopNonNativeInput>(track.get()));
437 RefPtr<SmartMockCubebStream> destroyedStream =
438 WaitFor(cubeb->StreamDestroyEvent());
439 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
443 // Clean up.
444 DispatchFunction([&] { track->Destroy(); });
445 RefPtr<SmartMockCubebStream> destroyedStream =
446 WaitFor(cubeb->StreamDestroyEvent());
447 EXPECT_EQ(destroyedStream.get(), driverStream.get());
450 TEST(TestAudioTrackGraph, NonNativeInputTrackErrorCallback)
452 MockCubeb* cubeb = new MockCubeb();
453 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
455 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
456 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
457 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
458 nullptr, GetMainThreadSerialEventTarget());
460 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
462 // Add a NonNativeInputTrack to graph, making graph create an output-only
463 // AudioCallbackDriver since NonNativeInputTrack is an audio-type MediaTrack.
464 RefPtr<NonNativeInputTrack> track;
465 DispatchFunction([&] {
466 track = new NonNativeInputTrack(graph->GraphRate(), deviceId,
467 PRINCIPAL_HANDLE_NONE);
468 graph->AddTrack(track);
471 RefPtr<SmartMockCubebStream> driverStream = WaitFor(cubeb->StreamInitEvent());
472 EXPECT_FALSE(driverStream->mHasInput);
473 EXPECT_TRUE(driverStream->mHasOutput);
475 // Main test below:
477 const AudioInputSource::Id sourceId = 1;
478 const uint32_t channels = 2;
479 const TrackRate rate = 48000;
481 // Launch and start the non-native audio stream.
482 DispatchFunction([&] {
483 track->GraphImpl()->AppendMessage(MakeUnique<StartNonNativeInput>(
484 track.get(), MakeRefPtr<AudioInputSource>(
485 MakeRefPtr<AudioInputSourceListener>(track.get()),
486 sourceId, deviceId, channels, true,
487 PRINCIPAL_HANDLE_NONE, rate, graph->GraphRate())));
489 RefPtr<SmartMockCubebStream> nonNativeStream =
490 WaitFor(cubeb->StreamInitEvent());
491 EXPECT_TRUE(nonNativeStream->mHasInput);
492 EXPECT_FALSE(nonNativeStream->mHasOutput);
493 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), deviceId);
494 EXPECT_EQ(nonNativeStream->InputChannels(), channels);
495 EXPECT_EQ(nonNativeStream->SampleRate(), static_cast<uint32_t>(rate));
497 // Make sure the audio stream is running.
498 Unused << WaitFor(nonNativeStream->FramesProcessedEvent());
500 // Force an error. This results in the audio stream destroying.
501 DispatchFunction([&] { nonNativeStream->ForceError(); });
502 WaitFor(nonNativeStream->ErrorForcedEvent());
504 RefPtr<SmartMockCubebStream> destroyedStream =
505 WaitFor(cubeb->StreamDestroyEvent());
506 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
509 // Make sure it's ok to call audio stop again.
510 DispatchFunction([&] {
511 track->GraphImpl()->AppendMessage(
512 MakeUnique<StopNonNativeInput>(track.get()));
515 // Clean up.
516 DispatchFunction([&] { track->Destroy(); });
517 RefPtr<SmartMockCubebStream> destroyedStream =
518 WaitFor(cubeb->StreamDestroyEvent());
519 EXPECT_EQ(destroyedStream.get(), driverStream.get());
522 class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
523 public:
524 static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
525 MOZ_ASSERT(NS_IsMainThread());
526 TestDeviceInputConsumerTrack* track =
527 new TestDeviceInputConsumerTrack(aGraph->GraphRate());
528 aGraph->AddTrack(track);
529 return track;
532 void Destroy() {
533 MOZ_ASSERT(NS_IsMainThread());
534 DisconnectDeviceInput();
535 DeviceInputConsumerTrack::Destroy();
538 void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override {
539 if (aFrom >= aTo) {
540 return;
543 if (mInputs.IsEmpty()) {
544 GetData<AudioSegment>()->AppendNullData(aTo - aFrom);
545 } else {
546 MOZ_ASSERT(mInputs.Length() == 1);
547 AudioSegment data;
548 DeviceInputConsumerTrack::GetInputSourceData(data, aFrom, aTo);
549 GetData<AudioSegment>()->AppendFrom(&data);
553 uint32_t NumberOfChannels() const override {
554 if (mInputs.IsEmpty()) {
555 return 0;
557 DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
558 MOZ_ASSERT(t);
559 return t->NumberOfChannels();
562 private:
563 explicit TestDeviceInputConsumerTrack(TrackRate aSampleRate)
564 : DeviceInputConsumerTrack(aSampleRate) {}
567 TEST(TestAudioTrackGraph, DeviceChangedCallback)
569 MockCubeb* cubeb = new MockCubeb();
570 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
572 MediaTrackGraph* graphImpl = MediaTrackGraphImpl::GetInstance(
573 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
574 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
575 nullptr, GetMainThreadSerialEventTarget());
577 class TestAudioDataListener : public AudioDataListener {
578 public:
579 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
580 : mChannelCount(aChannelCount),
581 mIsVoice(aIsVoice),
582 mDeviceChangedCount(0) {}
584 uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
585 return mChannelCount;
587 bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
588 return mIsVoice;
590 void DeviceChanged(MediaTrackGraph* aGraph) override {
591 ++mDeviceChangedCount;
593 void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
594 uint32_t DeviceChangedCount() { return mDeviceChangedCount; }
596 private:
597 ~TestAudioDataListener() = default;
598 const uint32_t mChannelCount;
599 const bool mIsVoice;
600 std::atomic<uint32_t> mDeviceChangedCount;
603 // Create a full-duplex AudioCallbackDriver by creating a NativeInputTrack.
604 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
605 RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
606 RefPtr<TestDeviceInputConsumerTrack> track1 =
607 TestDeviceInputConsumerTrack::Create(graphImpl);
608 track1->ConnectDeviceInput(device1, listener1.get(), PRINCIPAL_HANDLE_NONE);
610 EXPECT_TRUE(track1->ConnectedToNativeDevice());
611 EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
612 auto started =
613 Invoke([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
614 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
615 EXPECT_TRUE(stream1->mHasInput);
616 EXPECT_TRUE(stream1->mHasOutput);
617 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
618 Unused << WaitFor(started);
620 // Create a NonNativeInputTrack, and make sure its DeviceChangeCallback works.
621 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
622 RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, true);
623 RefPtr<TestDeviceInputConsumerTrack> track2 =
624 TestDeviceInputConsumerTrack::Create(graphImpl);
625 track2->ConnectDeviceInput(device2, listener2.get(), PRINCIPAL_HANDLE_NONE);
627 EXPECT_FALSE(track2->ConnectedToNativeDevice());
628 EXPECT_TRUE(track2->ConnectedToNonNativeDevice());
629 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
630 EXPECT_TRUE(stream2->mHasInput);
631 EXPECT_FALSE(stream2->mHasOutput);
632 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
634 // Produce a device-changed event for the NonNativeInputTrack.
635 DispatchFunction([&] { stream2->ForceDeviceChanged(); });
636 WaitFor(stream2->DeviceChangeForcedEvent());
638 // Produce a device-changed event for the NativeInputTrack.
639 DispatchFunction([&] { stream1->ForceDeviceChanged(); });
640 WaitFor(stream1->DeviceChangeForcedEvent());
642 // Destroy the NonNativeInputTrack.
643 DispatchFunction([&] {
644 track2->DisconnectDeviceInput();
645 track2->Destroy();
647 RefPtr<SmartMockCubebStream> destroyedStream =
648 WaitFor(cubeb->StreamDestroyEvent());
649 EXPECT_EQ(destroyedStream.get(), stream2.get());
651 // Make sure we only have one device-changed event for the NativeInputTrack.
652 EXPECT_EQ(listener2->DeviceChangedCount(), 1U);
654 // Destroy the NativeInputTrack.
655 DispatchFunction([&] {
656 track1->DisconnectDeviceInput();
657 track1->Destroy();
659 destroyedStream = WaitFor(cubeb->StreamDestroyEvent());
660 EXPECT_EQ(destroyedStream.get(), stream1.get());
662 // Make sure we only have one device-changed event for the NativeInputTrack.
663 EXPECT_EQ(listener1->DeviceChangedCount(), 1U);
666 // The native audio stream (a.k.a. GraphDriver) and the non-native audio stream
667 // should always be the same as the max requested input channel of its paired
668 // DeviceInputTracks. This test checks if the audio stream paired with the
669 // DeviceInputTrack will follow the max requested input channel or not.
671 // The main focus for this test is to make sure DeviceInputTrack::OpenAudio and
672 // ::CloseAudio works as what we expect. Besides, This test also confirms
673 // MediaTrackGraph::ReevaluateInputDevice works correctly by using a
674 // test-only AudioDataListener.
676 // This test is pretty similar to RestartAudioIfProcessingMaxChannelCountChanged
677 // below, which tests the same thing but using AudioProcessingTrack.
678 // AudioProcessingTrack is the consumer of the DeviceInputTrack used in wild.
679 // It has its own customized AudioDataListener. However, it only tests when
680 // MOZ_WEBRTC is defined.
681 TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged)
683 MockCubeb* cubeb = new MockCubeb();
684 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
685 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
686 Unused << unforcer;
688 MediaTrackGraph* graphImpl = MediaTrackGraphImpl::GetInstance(
689 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
690 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
691 nullptr, GetMainThreadSerialEventTarget());
693 // A test-only AudioDataListener that simulates AudioInputProcessing's setter
694 // and getter for the input channel count.
695 class TestAudioDataListener : public AudioDataListener {
696 public:
697 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
698 : mChannelCount(aChannelCount), mIsVoice(aIsVoice) {}
699 // Main thread API
700 void SetInputChannelCount(MediaTrackGraph* aGraph,
701 CubebUtils::AudioDeviceID aDevice,
702 uint32_t aChannelCount) {
703 MOZ_ASSERT(NS_IsMainThread());
705 struct Message : public ControlMessage {
706 MediaTrackGraph* mGraph;
707 TestAudioDataListener* mListener;
708 CubebUtils::AudioDeviceID mDevice;
709 uint32_t mChannelCount;
711 Message(MediaTrackGraph* aGraph, TestAudioDataListener* aListener,
712 CubebUtils::AudioDeviceID aDevice, uint32_t aChannelCount)
713 : ControlMessage(nullptr),
714 mGraph(aGraph),
715 mListener(aListener),
716 mDevice(aDevice),
717 mChannelCount(aChannelCount) {}
718 void Run() override {
719 mListener->mChannelCount = mChannelCount;
720 mGraph->ReevaluateInputDevice(mDevice);
724 static_cast<MediaTrackGraphImpl*>(aGraph)->AppendMessage(
725 MakeUnique<Message>(aGraph, this, aDevice, aChannelCount));
727 // Graph thread APIs: AudioDataListenerInterface implementations.
728 uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
729 aGraph->AssertOnGraphThread();
730 return mChannelCount;
732 bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
733 return mIsVoice;
735 void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */
737 void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
739 private:
740 ~TestAudioDataListener() = default;
742 // Graph thread-only.
743 uint32_t mChannelCount;
744 // Any thread.
745 const bool mIsVoice;
748 // Request a new input channel count and expect to have a new stream.
749 auto setNewChannelCount = [&](const RefPtr<TestAudioDataListener>& aListener,
750 RefPtr<SmartMockCubebStream>& aStream,
751 uint32_t aChannelCount) {
752 ASSERT_TRUE(!!aListener);
753 ASSERT_TRUE(!!aStream);
754 ASSERT_TRUE(aStream->mHasInput);
755 ASSERT_NE(aChannelCount, 0U);
757 const CubebUtils::AudioDeviceID device = aStream->GetInputDeviceID();
759 bool destroyed = false;
760 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
761 AbstractThread::GetCurrent(),
762 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
763 destroyed = aDestroyed.get() == aStream.get();
766 RefPtr<SmartMockCubebStream> newStream;
767 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
768 AbstractThread::GetCurrent(),
769 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
770 newStream = aCreated;
773 DispatchFunction([&] {
774 aListener->SetInputChannelCount(graphImpl, device, aChannelCount);
777 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
778 "TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) #1"_ns,
779 [&] { return destroyed && newStream; });
781 destroyListener.Disconnect();
782 restartListener.Disconnect();
784 aStream = newStream;
787 // Open a new track and expect to have a new stream.
788 auto openTrack = [&](RefPtr<SmartMockCubebStream>& aCurrentStream,
789 RefPtr<TestDeviceInputConsumerTrack>& aTrack,
790 const RefPtr<TestAudioDataListener>& aListener,
791 CubebUtils::AudioDeviceID aDevice) {
792 ASSERT_TRUE(!!aCurrentStream);
793 ASSERT_TRUE(aCurrentStream->mHasInput);
794 ASSERT_TRUE(!aTrack);
795 ASSERT_TRUE(!!aListener);
797 bool destroyed = false;
798 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
799 AbstractThread::GetCurrent(),
800 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
801 destroyed = aDestroyed.get() == aCurrentStream.get();
804 RefPtr<SmartMockCubebStream> newStream;
805 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
806 AbstractThread::GetCurrent(),
807 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
808 newStream = aCreated;
811 aTrack = TestDeviceInputConsumerTrack::Create(graphImpl);
812 aTrack->ConnectDeviceInput(aDevice, aListener.get(), PRINCIPAL_HANDLE_NONE);
814 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
815 "TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged) #2"_ns,
816 [&] { return destroyed && newStream; });
818 destroyListener.Disconnect();
819 restartListener.Disconnect();
821 aCurrentStream = newStream;
824 // Test for the native input device first then non-native device. The
825 // non-native device will be destroyed before the native device in case of
826 // causing a driver switching.
828 // Test for the native device.
829 const CubebUtils::AudioDeviceID nativeDevice = (CubebUtils::AudioDeviceID)1;
830 RefPtr<TestDeviceInputConsumerTrack> track1;
831 RefPtr<TestAudioDataListener> listener1;
832 RefPtr<SmartMockCubebStream> nativeStream;
833 RefPtr<TestDeviceInputConsumerTrack> track2;
834 RefPtr<TestAudioDataListener> listener2;
836 // Open a 1-channel NativeInputTrack.
837 listener1 = new TestAudioDataListener(1, false);
838 track1 = TestDeviceInputConsumerTrack::Create(graphImpl);
839 track1->ConnectDeviceInput(nativeDevice, listener1.get(),
840 PRINCIPAL_HANDLE_NONE);
842 EXPECT_TRUE(track1->ConnectedToNativeDevice());
843 EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
844 auto started =
845 Invoke([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
846 nativeStream = WaitFor(cubeb->StreamInitEvent());
847 EXPECT_TRUE(nativeStream->mHasInput);
848 EXPECT_TRUE(nativeStream->mHasOutput);
849 EXPECT_EQ(nativeStream->GetInputDeviceID(), nativeDevice);
850 Unused << WaitFor(started);
852 // Open a 2-channel NativeInputTrack and wait for a new driver since the
853 // max-channel for the native device becomes 2 now.
854 listener2 = new TestAudioDataListener(2, false);
855 openTrack(nativeStream, track2, listener2, nativeDevice);
856 EXPECT_EQ(nativeStream->InputChannels(), 2U);
858 // Set the second NativeInputTrack to 1-channel and wait for a new driver
859 // since the max-channel for the native device becomes 1 now.
860 setNewChannelCount(listener2, nativeStream, 1);
861 EXPECT_EQ(nativeStream->InputChannels(), 1U);
863 // Set the first NativeInputTrack to 2-channel and wait for a new driver
864 // since the max input channel for the native device becomes 2 now.
865 setNewChannelCount(listener1, nativeStream, 2);
866 EXPECT_EQ(nativeStream->InputChannels(), 2U);
869 // Test for the non-native device.
871 const CubebUtils::AudioDeviceID nonNativeDevice =
872 (CubebUtils::AudioDeviceID)2;
874 // Open a 1-channel NonNativeInputTrack.
875 RefPtr<TestAudioDataListener> listener3 =
876 new TestAudioDataListener(1, false);
877 RefPtr<TestDeviceInputConsumerTrack> track3 =
878 TestDeviceInputConsumerTrack::Create(graphImpl);
879 track3->ConnectDeviceInput(nonNativeDevice, listener3.get(),
880 PRINCIPAL_HANDLE_NONE);
881 EXPECT_FALSE(track3->ConnectedToNativeDevice());
882 EXPECT_TRUE(track3->ConnectedToNonNativeDevice());
884 RefPtr<SmartMockCubebStream> nonNativeStream =
885 WaitFor(cubeb->StreamInitEvent());
886 EXPECT_TRUE(nonNativeStream->mHasInput);
887 EXPECT_FALSE(nonNativeStream->mHasOutput);
888 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
889 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
891 // Open a 2-channel NonNativeInputTrack and wait for a new stream since
892 // the max-channel for the non-native device becomes 2 now.
893 RefPtr<TestAudioDataListener> listener4 =
894 new TestAudioDataListener(2, false);
895 RefPtr<TestDeviceInputConsumerTrack> track4;
896 openTrack(nonNativeStream, track4, listener4, nonNativeDevice);
897 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
898 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
900 // Set the second NonNativeInputTrack to 1-channel and wait for a new
901 // driver since the max-channel for the non-native device becomes 1 now.
902 setNewChannelCount(listener4, nonNativeStream, 1);
903 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
905 // Set the first NonNativeInputTrack to 2-channel and wait for a new
906 // driver since the max input channel for the non-native device becomes 2
907 // now.
908 setNewChannelCount(listener3, nonNativeStream, 2);
909 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
911 // Close the second NonNativeInputTrack (1-channel) then the first one
912 // (2-channel) so we won't result in another stream creation.
913 DispatchFunction([&] {
914 track4->DisconnectDeviceInput();
915 track4->Destroy();
917 DispatchFunction([&] {
918 track3->DisconnectDeviceInput();
919 track3->Destroy();
921 RefPtr<SmartMockCubebStream> destroyedStream =
922 WaitFor(cubeb->StreamDestroyEvent());
923 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
926 // Tear down for the native device.
928 // Close the second NativeInputTrack (1-channel) then the first one
929 // (2-channel) so we won't have driver switching.
930 DispatchFunction([&] {
931 track2->DisconnectDeviceInput();
932 track2->Destroy();
934 DispatchFunction([&] {
935 track1->DisconnectDeviceInput();
936 track1->Destroy();
938 RefPtr<SmartMockCubebStream> destroyedStream =
939 WaitFor(cubeb->StreamDestroyEvent());
940 EXPECT_EQ(destroyedStream.get(), nativeStream.get());
944 // This test is pretty similar to SwitchNativeAudioProcessingTrack below, which
945 // tests the same thing but using AudioProcessingTrack. AudioProcessingTrack is
946 // the consumer of the DeviceInputTrack used in wild. It has its own customized
947 // AudioDataListener. However, it only tests when MOZ_WEBRTC is defined.
948 TEST(TestAudioTrackGraph, SwitchNativeInputDevice)
950 class TestAudioDataListener : public AudioDataListener {
951 public:
952 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
953 : mChannelCount(aChannelCount),
954 mIsVoice(aIsVoice),
955 mDeviceChangedCount(0) {}
957 uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
958 return mChannelCount;
960 bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
961 return mIsVoice;
963 void DeviceChanged(MediaTrackGraph* aGraph) override {
964 ++mDeviceChangedCount;
966 void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
967 uint32_t DeviceChangedCount() { return mDeviceChangedCount; }
969 private:
970 ~TestAudioDataListener() = default;
971 const uint32_t mChannelCount;
972 const bool mIsVoice;
973 std::atomic<uint32_t> mDeviceChangedCount;
976 MockCubeb* cubeb = new MockCubeb();
977 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
979 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
980 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
981 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
982 nullptr, GetMainThreadSerialEventTarget());
984 auto switchNativeDevice =
985 [&](RefPtr<SmartMockCubebStream>&& aCurrentNativeStream,
986 RefPtr<TestDeviceInputConsumerTrack>& aCurrentNativeTrack,
987 RefPtr<SmartMockCubebStream>& aNextNativeStream,
988 RefPtr<TestDeviceInputConsumerTrack>& aNextNativeTrack) {
989 ASSERT_TRUE(aCurrentNativeStream->mHasInput);
990 ASSERT_TRUE(aCurrentNativeStream->mHasOutput);
991 ASSERT_TRUE(aNextNativeStream->mHasInput);
992 ASSERT_FALSE(aNextNativeStream->mHasOutput);
994 std::cerr << "Switching native input from device "
995 << aCurrentNativeStream->GetInputDeviceID() << " to "
996 << aNextNativeStream->GetInputDeviceID() << std::endl;
998 uint32_t destroyed = 0;
999 MediaEventListener destroyListener =
1000 cubeb->StreamDestroyEvent().Connect(
1001 AbstractThread::GetCurrent(),
1002 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1003 if (aDestroyed.get() == aCurrentNativeStream.get() ||
1004 aDestroyed.get() == aNextNativeStream.get()) {
1005 std::cerr << "cubeb stream " << aDestroyed.get()
1006 << " (device " << aDestroyed->GetInputDeviceID()
1007 << ") has been destroyed" << std::endl;
1008 destroyed += 1;
1012 RefPtr<SmartMockCubebStream> newStream;
1013 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1014 AbstractThread::GetCurrent(),
1015 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1016 // Make sure new stream has input, to prevent from getting a
1017 // temporary output-only AudioCallbackDriver after closing current
1018 // native device but before setting a new native input.
1019 if (aCreated->mHasInput) {
1020 ASSERT_TRUE(aCreated->mHasOutput);
1021 newStream = aCreated;
1025 std::cerr << "Close device " << aCurrentNativeStream->GetInputDeviceID()
1026 << std::endl;
1027 DispatchFunction([&] {
1028 aCurrentNativeTrack->DisconnectDeviceInput();
1029 aCurrentNativeTrack->Destroy();
1032 std::cerr << "Wait for the switching" << std::endl;
1033 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1034 "TEST(TestAudioTrackGraph, SwitchNativeInputDevice)"_ns,
1035 [&] { return destroyed >= 2 && newStream; });
1037 destroyListener.Disconnect();
1038 restartListener.Disconnect();
1040 aCurrentNativeStream = nullptr;
1041 aNextNativeStream = newStream;
1043 std::cerr << "Now the native input is device "
1044 << aNextNativeStream->GetInputDeviceID() << std::endl;
1047 // Open a DeviceInputConsumerTrack for device 1.
1048 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
1049 RefPtr<TestDeviceInputConsumerTrack> track1 =
1050 TestDeviceInputConsumerTrack::Create(graph);
1051 RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
1052 track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
1053 EXPECT_EQ(track1->DeviceId().value(), device1);
1055 auto started =
1056 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
1058 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
1059 EXPECT_TRUE(stream1->mHasInput);
1060 EXPECT_TRUE(stream1->mHasOutput);
1061 EXPECT_EQ(stream1->InputChannels(), 1U);
1062 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
1063 Unused << WaitFor(started);
1064 std::cerr << "Device " << device1 << " is opened (stream " << stream1.get()
1065 << ")" << std::endl;
1067 // Open a DeviceInputConsumerTrack for device 2.
1068 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
1069 RefPtr<TestDeviceInputConsumerTrack> track2 =
1070 TestDeviceInputConsumerTrack::Create(graph);
1071 RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false);
1072 track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
1073 EXPECT_EQ(track2->DeviceId().value(), device2);
1075 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
1076 EXPECT_TRUE(stream2->mHasInput);
1077 EXPECT_FALSE(stream2->mHasOutput);
1078 EXPECT_EQ(stream2->InputChannels(), 2U);
1079 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1080 std::cerr << "Device " << device2 << " is opened (stream " << stream2.get()
1081 << ")" << std::endl;
1083 // Open a DeviceInputConsumerTrack for device 3.
1084 const CubebUtils::AudioDeviceID device3 = (CubebUtils::AudioDeviceID)3;
1085 RefPtr<TestDeviceInputConsumerTrack> track3 =
1086 TestDeviceInputConsumerTrack::Create(graph);
1087 RefPtr<TestAudioDataListener> listener3 = new TestAudioDataListener(1, false);
1088 track3->ConnectDeviceInput(device3, listener3, PRINCIPAL_HANDLE_NONE);
1089 EXPECT_EQ(track3->DeviceId().value(), device3);
1091 RefPtr<SmartMockCubebStream> stream3 = WaitFor(cubeb->StreamInitEvent());
1092 EXPECT_TRUE(stream3->mHasInput);
1093 EXPECT_FALSE(stream3->mHasOutput);
1094 EXPECT_EQ(stream3->InputChannels(), 1U);
1095 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
1096 std::cerr << "Device " << device3 << " is opened (stream " << stream3.get()
1097 << ")" << std::endl;
1099 // Close device 1, so the native input device is switched from device 1 to
1100 // device 2.
1101 switchNativeDevice(std::move(stream1), track1, stream2, track2);
1102 EXPECT_TRUE(stream2->mHasInput);
1103 EXPECT_TRUE(stream2->mHasOutput);
1104 EXPECT_EQ(stream2->InputChannels(), 2U);
1105 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1107 NativeInputTrack* native = track2->Graph()->GetNativeInputTrackMainThread();
1108 ASSERT_TRUE(!!native);
1109 EXPECT_EQ(native->mDeviceId, device2);
1112 // Close device 2, so the native input device is switched from device 2 to
1113 // device 3.
1114 switchNativeDevice(std::move(stream2), track2, stream3, track3);
1115 EXPECT_TRUE(stream3->mHasInput);
1116 EXPECT_TRUE(stream3->mHasOutput);
1117 EXPECT_EQ(stream3->InputChannels(), 1U);
1118 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
1120 NativeInputTrack* native = track3->Graph()->GetNativeInputTrackMainThread();
1121 ASSERT_TRUE(!!native);
1122 EXPECT_EQ(native->mDeviceId, device3);
1125 // Clean up.
1126 std::cerr << "Close device " << device3 << std::endl;
1127 DispatchFunction([&] {
1128 track3->DisconnectDeviceInput();
1129 track3->Destroy();
1131 RefPtr<SmartMockCubebStream> destroyedStream =
1132 WaitFor(cubeb->StreamDestroyEvent());
1133 EXPECT_EQ(destroyedStream.get(), stream3.get());
1135 NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
1136 ASSERT_TRUE(!native);
1138 std::cerr << "No native input now" << std::endl;
1141 #ifdef MOZ_WEBRTC
1142 TEST(TestAudioTrackGraph, ErrorCallback)
1144 MockCubeb* cubeb = new MockCubeb();
1145 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1147 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1148 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1149 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1150 nullptr, GetMainThreadSerialEventTarget());
1152 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1154 // Dummy track to make graph rolling. Add it and remove it to remove the
1155 // graph from the global hash table and let it shutdown.
1157 // We open an input through this track so that there's something triggering
1158 // EnsureNextIteration on the fallback driver after the callback driver has
1159 // gotten the error, and to check that a replacement cubeb_stream receives
1160 // output from the graph.
1161 RefPtr<AudioProcessingTrack> processingTrack;
1162 RefPtr<AudioInputProcessing> listener;
1163 auto started = Invoke([&] {
1164 processingTrack = AudioProcessingTrack::Create(graph);
1165 listener = new AudioInputProcessing(2);
1166 QueueExpectIsPassThrough(processingTrack, listener);
1167 processingTrack->SetInputProcessing(listener);
1168 processingTrack->GraphImpl()->AppendMessage(
1169 MakeUnique<StartInputProcessing>(processingTrack, listener));
1170 processingTrack->ConnectDeviceInput(deviceId, listener,
1171 PRINCIPAL_HANDLE_NONE);
1172 EXPECT_EQ(processingTrack->DeviceId().value(), deviceId);
1173 processingTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1174 return graph->NotifyWhenDeviceStarted(nullptr);
1177 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1178 Result<bool, nsresult> rv = WaitFor(started);
1179 EXPECT_TRUE(rv.unwrapOr(false));
1181 // Force a cubeb state_callback error and see that we don't crash.
1182 DispatchFunction([&] { stream->ForceError(); });
1184 // Wait for the error to take effect, and the driver to restart and receive
1185 // output.
1186 bool errored = false;
1187 MediaEventListener errorListener = stream->ErrorForcedEvent().Connect(
1188 AbstractThread::GetCurrent(), [&] { errored = true; });
1189 stream = WaitFor(cubeb->StreamInitEvent());
1190 WaitFor(stream->FramesVerifiedEvent());
1191 // The error event is notified after CUBEB_STATE_ERROR triggers other
1192 // threads to init a new cubeb_stream, so there is a theoretical chance that
1193 // `errored` might not be set when `stream` is set.
1194 errorListener.Disconnect();
1195 EXPECT_TRUE(errored);
1197 // Clean up.
1198 DispatchFunction([&] {
1199 processingTrack->GraphImpl()->AppendMessage(
1200 MakeUnique<StopInputProcessing>(processingTrack, listener));
1201 processingTrack->DisconnectDeviceInput();
1202 processingTrack->Destroy();
1204 WaitFor(cubeb->StreamDestroyEvent());
1207 TEST(TestAudioTrackGraph, AudioProcessingTrack)
1209 MockCubeb* cubeb = new MockCubeb();
1210 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1211 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
1212 Unused << unforcer;
1214 // Start on a system clock driver, then switch to full-duplex in one go. If we
1215 // did output-then-full-duplex we'd risk a second NotifyWhenDeviceStarted
1216 // resolving early after checking the first audio driver only.
1217 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1218 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1219 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1220 nullptr, GetMainThreadSerialEventTarget());
1222 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1224 RefPtr<AudioProcessingTrack> processingTrack;
1225 RefPtr<ProcessedMediaTrack> outputTrack;
1226 RefPtr<MediaInputPort> port;
1227 RefPtr<AudioInputProcessing> listener;
1228 auto p = Invoke([&] {
1229 processingTrack = AudioProcessingTrack::Create(graph);
1230 outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
1231 outputTrack->QueueSetAutoend(false);
1232 outputTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1233 port = outputTrack->AllocateInputPort(processingTrack);
1234 /* Primary graph: Open Audio Input through SourceMediaTrack */
1235 listener = new AudioInputProcessing(2);
1236 QueueExpectIsPassThrough(processingTrack, listener);
1237 processingTrack->SetInputProcessing(listener);
1238 processingTrack->GraphImpl()->AppendMessage(
1239 MakeUnique<StartInputProcessing>(processingTrack, listener));
1240 // Device id does not matter. Ignore.
1241 processingTrack->ConnectDeviceInput(deviceId, listener,
1242 PRINCIPAL_HANDLE_NONE);
1243 return graph->NotifyWhenDeviceStarted(nullptr);
1246 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1247 EXPECT_TRUE(stream->mHasInput);
1248 Unused << WaitFor(p);
1250 // Wait for a second worth of audio data. GoFaster is dispatched through a
1251 // ControlMessage so that it is called in the first audio driver iteration.
1252 // Otherwise the audio driver might be going very fast while the fallback
1253 // system clock driver is still in an iteration.
1254 DispatchFunction([&] {
1255 processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
1257 uint32_t totalFrames = 0;
1258 WaitUntil(stream->FramesVerifiedEvent(), [&](uint32_t aFrames) {
1259 totalFrames += aFrames;
1260 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
1262 cubeb->DontGoFaster();
1264 // Clean up.
1265 DispatchFunction([&] {
1266 outputTrack->RemoveAudioOutput((void*)1);
1267 outputTrack->Destroy();
1268 port->Destroy();
1269 processingTrack->GraphImpl()->AppendMessage(
1270 MakeUnique<StopInputProcessing>(processingTrack, listener));
1271 processingTrack->DisconnectDeviceInput();
1272 processingTrack->Destroy();
1275 uint32_t inputRate = stream->SampleRate();
1276 uint32_t inputFrequency = stream->InputFrequency();
1277 uint64_t preSilenceSamples;
1278 uint32_t estimatedFreq;
1279 uint32_t nrDiscontinuities;
1280 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
1281 WaitFor(stream->OutputVerificationEvent());
1283 EXPECT_EQ(estimatedFreq, inputFrequency);
1284 std::cerr << "PreSilence: " << preSilenceSamples << std::endl;
1285 // We buffer 128 frames. See DeviceInputTrack::ProcessInput.
1286 EXPECT_GE(preSilenceSamples, 128U);
1287 // If the fallback system clock driver is doing a graph iteration before the
1288 // first audio driver iteration comes in, that iteration is ignored and
1289 // results in zeros. It takes one fallback driver iteration *after* the audio
1290 // driver has started to complete the switch, *usually* resulting two
1291 // 10ms-iterations of silence; sometimes only one.
1292 EXPECT_LE(preSilenceSamples, 128U + 2 * inputRate / 100 /* 2*10ms */);
1293 // The waveform from AudioGenerator starts at 0, but we don't control its
1294 // ending, so we expect a discontinuity there.
1295 EXPECT_LE(nrDiscontinuities, 1U);
1298 TEST(TestAudioTrackGraph, ReConnectDeviceInput)
1300 MockCubeb* cubeb = new MockCubeb();
1301 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1303 // 48k is a native processing rate, and avoids a resampling pass compared
1304 // to 44.1k. The resampler may add take a few frames to stabilize, which show
1305 // as unexected discontinuities in the test.
1306 const TrackRate rate = 48000;
1308 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1309 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, rate, nullptr,
1310 GetMainThreadSerialEventTarget());
1312 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1314 RefPtr<AudioProcessingTrack> processingTrack;
1315 RefPtr<ProcessedMediaTrack> outputTrack;
1316 RefPtr<MediaInputPort> port;
1317 RefPtr<AudioInputProcessing> listener;
1318 auto p = Invoke([&] {
1319 processingTrack = AudioProcessingTrack::Create(graph);
1320 outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
1321 outputTrack->QueueSetAutoend(false);
1322 outputTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1323 port = outputTrack->AllocateInputPort(processingTrack);
1325 const int32_t channelCount = 2;
1326 listener = new AudioInputProcessing(channelCount);
1327 processingTrack->SetInputProcessing(listener);
1328 processingTrack->GraphImpl()->AppendMessage(
1329 MakeUnique<StartInputProcessing>(processingTrack, listener));
1330 processingTrack->ConnectDeviceInput(deviceId, listener,
1331 PRINCIPAL_HANDLE_NONE);
1332 MediaEnginePrefs settings;
1333 settings.mChannels = channelCount;
1334 settings.mAgcOn = true; // Turn off pass-through.
1335 // AGC1 Mode 0 interferes with AudioVerifier's frequency estimation
1336 // through zero-crossing counts.
1337 settings.mAgc2Forced = true;
1338 QueueApplySettings(processingTrack, listener, settings);
1340 return graph->NotifyWhenDeviceStarted(nullptr);
1343 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1344 EXPECT_TRUE(stream->mHasInput);
1345 Unused << WaitFor(p);
1347 // Set a drift factor so that we don't dont produce perfect 10ms-chunks. This
1348 // will exercise whatever buffers are in the audio processing pipeline, and
1349 // the bookkeeping surrounding them.
1350 stream->SetDriftFactor(1.111);
1352 // Wait for a second worth of audio data. GoFaster is dispatched through a
1353 // ControlMessage so that it is called in the first audio driver iteration.
1354 // Otherwise the audio driver might be going very fast while the fallback
1355 // system clock driver is still in an iteration.
1356 DispatchFunction([&] {
1357 processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
1360 uint32_t totalFrames = 0;
1361 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
1362 totalFrames += aFrames;
1363 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
1366 cubeb->DontGoFaster();
1368 // Close the input to see that no asserts go off due to bad state.
1369 DispatchFunction([&] { processingTrack->DisconnectDeviceInput(); });
1371 stream = WaitFor(cubeb->StreamInitEvent());
1372 EXPECT_FALSE(stream->mHasInput);
1373 Unused << WaitFor(
1374 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
1376 // Output-only. Wait for another second before unmuting.
1377 DispatchFunction([&] {
1378 processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
1381 uint32_t totalFrames = 0;
1382 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
1383 totalFrames += aFrames;
1384 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
1387 cubeb->DontGoFaster();
1389 // Re-open the input to again see that no asserts go off due to bad state.
1390 DispatchFunction([&] {
1391 // Device id does not matter. Ignore.
1392 processingTrack->ConnectDeviceInput(deviceId, listener,
1393 PRINCIPAL_HANDLE_NONE);
1396 stream = WaitFor(cubeb->StreamInitEvent());
1397 EXPECT_TRUE(stream->mHasInput);
1398 Unused << WaitFor(
1399 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
1401 // Full-duplex. Wait for another second before finishing.
1402 DispatchFunction([&] {
1403 processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
1406 uint32_t totalFrames = 0;
1407 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
1408 totalFrames += aFrames;
1409 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
1412 cubeb->DontGoFaster();
1414 // Clean up.
1415 DispatchFunction([&] {
1416 outputTrack->RemoveAudioOutput((void*)1);
1417 outputTrack->Destroy();
1418 port->Destroy();
1419 processingTrack->GraphImpl()->AppendMessage(
1420 MakeUnique<StopInputProcessing>(processingTrack, listener));
1421 processingTrack->DisconnectDeviceInput();
1422 processingTrack->Destroy();
1425 uint32_t inputRate = stream->SampleRate();
1426 uint32_t inputFrequency = stream->InputFrequency();
1427 uint64_t preSilenceSamples;
1428 uint32_t estimatedFreq;
1429 uint32_t nrDiscontinuities;
1430 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
1431 WaitFor(stream->OutputVerificationEvent());
1433 EXPECT_EQ(estimatedFreq, inputFrequency);
1434 std::cerr << "PreSilence: " << preSilenceSamples << std::endl;
1435 // We buffer 10ms worth of frames in non-passthrough mode, plus up to 128
1436 // frames as we round up to the nearest block. See
1437 // AudioInputProcessing::Process and DeviceInputTrack::PrcoessInput.
1438 EXPECT_GE(preSilenceSamples, 128U + inputRate / 100);
1439 // If the fallback system clock driver is doing a graph iteration before the
1440 // first audio driver iteration comes in, that iteration is ignored and
1441 // results in zeros. It takes one fallback driver iteration *after* the audio
1442 // driver has started to complete the switch, *usually* resulting two
1443 // 10ms-iterations of silence; sometimes only one.
1444 EXPECT_LE(preSilenceSamples, 128U + 3 * inputRate / 100 /* 3*10ms */);
1445 // The waveform from AudioGenerator starts at 0, but we don't control its
1446 // ending, so we expect a discontinuity there. Note that this check is only
1447 // for the waveform on the stream *after* re-opening the input.
1448 EXPECT_LE(nrDiscontinuities, 1U);
1451 // Sum the signal to mono and compute the root mean square, in float32,
1452 // regardless of the input format.
1453 float rmsf32(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) {
1454 float downmixed;
1455 float rms = 0.;
1456 uint32_t readIdx = 0;
1457 for (uint32_t i = 0; i < aFrames; i++) {
1458 downmixed = 0.;
1459 for (uint32_t j = 0; j < aChannels; j++) {
1460 downmixed += ConvertAudioSample<float>(aSamples[readIdx++]);
1462 rms += downmixed * downmixed;
1464 rms = rms / aFrames;
1465 return sqrt(rms);
1468 TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
1470 MockCubeb* cubeb = new MockCubeb();
1471 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1473 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1474 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1475 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1476 nullptr, GetMainThreadSerialEventTarget());
1478 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1480 RefPtr<AudioProcessingTrack> processingTrack;
1481 RefPtr<ProcessedMediaTrack> outputTrack;
1482 RefPtr<MediaInputPort> port;
1483 RefPtr<AudioInputProcessing> listener;
1484 auto p = Invoke([&] {
1485 processingTrack = AudioProcessingTrack::Create(graph);
1486 outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
1487 outputTrack->QueueSetAutoend(false);
1488 outputTrack->AddAudioOutput(reinterpret_cast<void*>(1), nullptr);
1489 port = outputTrack->AllocateInputPort(processingTrack);
1490 /* Primary graph: Open Audio Input through SourceMediaTrack */
1491 listener = new AudioInputProcessing(2);
1492 QueueExpectIsPassThrough(processingTrack, listener);
1493 processingTrack->SetInputProcessing(listener);
1494 processingTrack->ConnectDeviceInput(deviceId, listener,
1495 PRINCIPAL_HANDLE_NONE);
1496 processingTrack->GraphImpl()->AppendMessage(
1497 MakeUnique<StartInputProcessing>(processingTrack, listener));
1498 return graph->NotifyWhenDeviceStarted(nullptr);
1501 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
1502 EXPECT_TRUE(stream->mHasInput);
1503 Unused << WaitFor(p);
1505 stream->SetOutputRecordingEnabled(true);
1507 // Wait for a second worth of audio data.
1508 uint64_t targetPosition = graph->GraphRate();
1509 auto AdvanceToTargetPosition = [&] {
1510 DispatchFunction([&] {
1511 processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
1513 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
1514 // Position() gives a more up-to-date indication than summing aFrames if
1515 // multiple events are queued.
1516 if (stream->Position() < targetPosition) {
1517 return false;
1519 cubeb->DontGoFaster();
1520 return true;
1523 AdvanceToTargetPosition();
1525 const uint32_t ITERATION_COUNT = 5;
1526 uint32_t iterations = ITERATION_COUNT;
1527 DisabledTrackMode nextMode = DisabledTrackMode::SILENCE_BLACK;
1528 while (iterations--) {
1529 // toggle the track enabled mode, wait a second, do this ITERATION_COUNT
1530 // times
1531 DispatchFunction([&] {
1532 processingTrack->SetDisabledTrackMode(nextMode);
1533 if (nextMode == DisabledTrackMode::SILENCE_BLACK) {
1534 nextMode = DisabledTrackMode::ENABLED;
1535 } else {
1536 nextMode = DisabledTrackMode::SILENCE_BLACK;
1540 targetPosition += graph->GraphRate();
1541 AdvanceToTargetPosition();
1544 // Clean up.
1545 DispatchFunction([&] {
1546 outputTrack->RemoveAudioOutput((void*)1);
1547 outputTrack->Destroy();
1548 port->Destroy();
1549 processingTrack->GraphImpl()->AppendMessage(
1550 MakeUnique<StopInputProcessing>(processingTrack, listener));
1551 processingTrack->DisconnectDeviceInput();
1552 processingTrack->Destroy();
1555 uint64_t preSilenceSamples;
1556 uint32_t estimatedFreq;
1557 uint32_t nrDiscontinuities;
1558 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
1559 WaitFor(stream->OutputVerificationEvent());
1561 auto data = stream->TakeRecordedOutput();
1563 // check that there is non-silence and silence at the expected time in the
1564 // stereo recording, while allowing for a bit of scheduling uncertainty, by
1565 // checking half a second after the theoretical muting/unmuting.
1566 // non-silence starts around: 0s, 2s, 4s
1567 // silence start around: 1s, 3s, 5s
1568 // To detect silence or non-silence, we compute the RMS of the signal for
1569 // 100ms.
1570 float noisyTime_s[] = {0.5, 2.5, 4.5};
1571 float silenceTime_s[] = {1.5, 3.5, 5.5};
1573 uint32_t rate = graph->GraphRate();
1574 for (float& time : noisyTime_s) {
1575 uint32_t startIdx = time * rate * 2 /* stereo */;
1576 EXPECT_NE(rmsf32(&(data[startIdx]), 2, rate / 10), 0.0);
1579 for (float& time : silenceTime_s) {
1580 uint32_t startIdx = time * rate * 2 /* stereo */;
1581 EXPECT_EQ(rmsf32(&(data[startIdx]), 2, rate / 10), 0.0);
1585 TEST(TestAudioTrackGraph, SetRequestedInputChannelCount)
1587 MockCubeb* cubeb = new MockCubeb();
1588 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1590 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1591 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1592 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1593 nullptr, GetMainThreadSerialEventTarget());
1595 // Open a 2-channel native input stream.
1596 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
1597 RefPtr<AudioProcessingTrack> track1 = AudioProcessingTrack::Create(graph);
1598 RefPtr<AudioInputProcessing> listener1 = new AudioInputProcessing(2);
1599 track1->SetInputProcessing(listener1);
1600 QueueExpectIsPassThrough(track1, listener1);
1601 track1->GraphImpl()->AppendMessage(
1602 MakeUnique<StartInputProcessing>(track1, listener1));
1603 track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
1604 EXPECT_EQ(track1->DeviceId().value(), device1);
1606 auto started =
1607 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
1609 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
1610 EXPECT_TRUE(stream1->mHasInput);
1611 EXPECT_TRUE(stream1->mHasOutput);
1612 EXPECT_EQ(stream1->InputChannels(), 2U);
1613 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
1614 Unused << WaitFor(started);
1616 // Open a 1-channel non-native input stream.
1617 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
1618 RefPtr<AudioProcessingTrack> track2 = AudioProcessingTrack::Create(graph);
1619 RefPtr<AudioInputProcessing> listener2 = new AudioInputProcessing(1);
1620 track2->SetInputProcessing(listener2);
1621 QueueExpectIsPassThrough(track2, listener2);
1622 track2->GraphImpl()->AppendMessage(
1623 MakeUnique<StartInputProcessing>(track2, listener2));
1624 track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
1625 EXPECT_EQ(track2->DeviceId().value(), device2);
1627 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
1628 EXPECT_TRUE(stream2->mHasInput);
1629 EXPECT_FALSE(stream2->mHasOutput);
1630 EXPECT_EQ(stream2->InputChannels(), 1U);
1631 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1633 // Request a new input channel count. This should re-create new input stream
1634 // accordingly.
1635 auto setNewChannelCount = [&](const RefPtr<AudioProcessingTrack> aTrack,
1636 const RefPtr<AudioInputProcessing>& aListener,
1637 RefPtr<SmartMockCubebStream>& aStream,
1638 int32_t aChannelCount) {
1639 bool destroyed = false;
1640 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
1641 AbstractThread::GetCurrent(),
1642 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1643 destroyed = aDestroyed.get() == aStream.get();
1646 RefPtr<SmartMockCubebStream> newStream;
1647 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1648 AbstractThread::GetCurrent(),
1649 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1650 newStream = aCreated;
1653 MediaEnginePrefs settings;
1654 settings.mChannels = aChannelCount;
1655 QueueApplySettings(aTrack, aListener, settings);
1657 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1658 "TEST(TestAudioTrackGraph, SetRequestedInputChannelCount)"_ns,
1659 [&] { return destroyed && newStream; });
1661 destroyListener.Disconnect();
1662 restartListener.Disconnect();
1664 aStream = newStream;
1667 // Set the native input stream's input channel count to 1.
1668 setNewChannelCount(track1, listener1, stream1, 1);
1669 EXPECT_TRUE(stream1->mHasInput);
1670 EXPECT_TRUE(stream1->mHasOutput);
1671 EXPECT_EQ(stream1->InputChannels(), 1U);
1672 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
1674 // Set the non-native input stream's input channel count to 2.
1675 setNewChannelCount(track2, listener2, stream2, 2);
1676 EXPECT_TRUE(stream2->mHasInput);
1677 EXPECT_FALSE(stream2->mHasOutput);
1678 EXPECT_EQ(stream2->InputChannels(), 2U);
1679 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
1681 // Close the non-native input stream.
1682 DispatchFunction([&] {
1683 track2->GraphImpl()->AppendMessage(
1684 MakeUnique<StopInputProcessing>(track2, listener2));
1685 track2->DisconnectDeviceInput();
1686 track2->Destroy();
1688 RefPtr<SmartMockCubebStream> destroyed = WaitFor(cubeb->StreamDestroyEvent());
1689 EXPECT_EQ(destroyed.get(), stream2.get());
1691 // Close the native input stream.
1692 DispatchFunction([&] {
1693 track1->GraphImpl()->AppendMessage(
1694 MakeUnique<StopInputProcessing>(track1, listener1));
1695 track1->DisconnectDeviceInput();
1696 track1->Destroy();
1698 destroyed = WaitFor(cubeb->StreamDestroyEvent());
1699 EXPECT_EQ(destroyed.get(), stream1.get());
1702 // The native audio stream (a.k.a. GraphDriver) and the non-native audio stream
1703 // should always be the same as the max requested input channel of its paired
1704 // AudioProcessingTracks. This test checks if the audio stream paired with the
1705 // AudioProcessingTrack will follow the max requested input channel or not.
1707 // This test is pretty similar to RestartAudioIfMaxChannelCountChanged above,
1708 // which makes sure the related DeviceInputTrack operations for the test here
1709 // works correctly. Instead of using a test-only AudioDataListener, we use
1710 // AudioInputProcessing here to simulate the real world use case.
1711 TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged)
1713 MockCubeb* cubeb = new MockCubeb();
1714 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1715 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
1716 Unused << unforcer;
1718 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1719 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1720 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1721 nullptr, GetMainThreadSerialEventTarget());
1723 // Request a new input channel count and expect to have a new stream.
1724 auto setNewChannelCount = [&](const RefPtr<AudioProcessingTrack>& aTrack,
1725 const RefPtr<AudioInputProcessing>& aListener,
1726 RefPtr<SmartMockCubebStream>& aStream,
1727 int32_t aChannelCount) {
1728 ASSERT_TRUE(!!aTrack);
1729 ASSERT_TRUE(!!aListener);
1730 ASSERT_TRUE(!!aStream);
1731 ASSERT_TRUE(aStream->mHasInput);
1732 ASSERT_NE(aChannelCount, 0);
1734 bool destroyed = false;
1735 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
1736 AbstractThread::GetCurrent(),
1737 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1738 destroyed = aDestroyed.get() == aStream.get();
1741 RefPtr<SmartMockCubebStream> newStream;
1742 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1743 AbstractThread::GetCurrent(),
1744 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1745 newStream = aCreated;
1748 MediaEnginePrefs settings;
1749 settings.mChannels = aChannelCount;
1750 QueueApplySettings(aTrack, aListener, settings);
1752 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1753 "TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged) #1"_ns,
1754 [&] { return destroyed && newStream; });
1756 destroyListener.Disconnect();
1757 restartListener.Disconnect();
1759 aStream = newStream;
1762 // Open a new track and expect to have a new stream.
1763 auto openTrack = [&](RefPtr<SmartMockCubebStream>& aCurrentStream,
1764 RefPtr<AudioProcessingTrack>& aTrack,
1765 RefPtr<AudioInputProcessing>& aListener,
1766 CubebUtils::AudioDeviceID aDevice,
1767 uint32_t aChannelCount) {
1768 ASSERT_TRUE(!!aCurrentStream);
1769 ASSERT_TRUE(aCurrentStream->mHasInput);
1770 ASSERT_TRUE(aChannelCount > aCurrentStream->InputChannels());
1771 ASSERT_TRUE(!aTrack);
1772 ASSERT_TRUE(!aListener);
1774 bool destroyed = false;
1775 MediaEventListener destroyListener = cubeb->StreamDestroyEvent().Connect(
1776 AbstractThread::GetCurrent(),
1777 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
1778 destroyed = aDestroyed.get() == aCurrentStream.get();
1781 RefPtr<SmartMockCubebStream> newStream;
1782 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
1783 AbstractThread::GetCurrent(),
1784 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
1785 newStream = aCreated;
1788 aTrack = AudioProcessingTrack::Create(graph);
1789 aListener = new AudioInputProcessing(aChannelCount);
1790 aTrack->SetInputProcessing(aListener);
1791 QueueExpectIsPassThrough(aTrack, aListener);
1792 aTrack->GraphImpl()->AppendMessage(
1793 MakeUnique<StartInputProcessing>(aTrack, aListener));
1795 DispatchFunction([&] {
1796 aTrack->ConnectDeviceInput(aDevice, aListener, PRINCIPAL_HANDLE_NONE);
1799 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
1800 "TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged) #2"_ns,
1801 [&] { return destroyed && newStream; });
1803 destroyListener.Disconnect();
1804 restartListener.Disconnect();
1806 aCurrentStream = newStream;
1809 // Test for the native input device first then non-native device. The
1810 // non-native device will be destroyed before the native device in case of
1811 // causing a native-device-switching.
1813 // Test for the native device.
1814 const CubebUtils::AudioDeviceID nativeDevice = (CubebUtils::AudioDeviceID)1;
1815 RefPtr<AudioProcessingTrack> track1;
1816 RefPtr<AudioInputProcessing> listener1;
1817 RefPtr<SmartMockCubebStream> nativeStream;
1818 RefPtr<AudioProcessingTrack> track2;
1819 RefPtr<AudioInputProcessing> listener2;
1821 // Open a 1-channel AudioProcessingTrack for the native device.
1822 track1 = AudioProcessingTrack::Create(graph);
1823 listener1 = new AudioInputProcessing(1);
1824 track1->SetInputProcessing(listener1);
1825 QueueExpectIsPassThrough(track1, listener1);
1826 track1->GraphImpl()->AppendMessage(
1827 MakeUnique<StartInputProcessing>(track1, listener1));
1828 track1->ConnectDeviceInput(nativeDevice, listener1, PRINCIPAL_HANDLE_NONE);
1829 EXPECT_EQ(track1->DeviceId().value(), nativeDevice);
1831 auto started =
1832 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
1834 nativeStream = WaitFor(cubeb->StreamInitEvent());
1835 EXPECT_TRUE(nativeStream->mHasInput);
1836 EXPECT_TRUE(nativeStream->mHasOutput);
1837 EXPECT_EQ(nativeStream->InputChannels(), 1U);
1838 EXPECT_EQ(nativeStream->GetInputDeviceID(), nativeDevice);
1839 Unused << WaitFor(started);
1841 // Open a 2-channel AudioProcessingTrack for the native device and wait for
1842 // a new driver since the max-channel for the native device becomes 2 now.
1843 openTrack(nativeStream, track2, listener2, nativeDevice, 2);
1844 EXPECT_EQ(nativeStream->InputChannels(), 2U);
1846 // Set the second AudioProcessingTrack for the native device to 1-channel
1847 // and wait for a new driver since the max-channel for the native device
1848 // becomes 1 now.
1849 setNewChannelCount(track2, listener2, nativeStream, 1);
1850 EXPECT_EQ(nativeStream->InputChannels(), 1U);
1852 // Set the first AudioProcessingTrack for the native device to 2-channel and
1853 // wait for a new driver since the max input channel for the native device
1854 // becomes 2 now.
1855 setNewChannelCount(track1, listener1, nativeStream, 2);
1856 EXPECT_EQ(nativeStream->InputChannels(), 2U);
1859 // Test for the non-native device.
1861 const CubebUtils::AudioDeviceID nonNativeDevice =
1862 (CubebUtils::AudioDeviceID)2;
1864 // Open a 1-channel AudioProcessingTrack for the non-native device.
1865 RefPtr<AudioProcessingTrack> track3 = AudioProcessingTrack::Create(graph);
1866 RefPtr<AudioInputProcessing> listener3 = new AudioInputProcessing(1);
1867 track3->SetInputProcessing(listener3);
1868 QueueExpectIsPassThrough(track3, listener3);
1869 track3->GraphImpl()->AppendMessage(
1870 MakeUnique<StartInputProcessing>(track3, listener3));
1871 track3->ConnectDeviceInput(nonNativeDevice, listener3,
1872 PRINCIPAL_HANDLE_NONE);
1873 EXPECT_EQ(track3->DeviceId().value(), nonNativeDevice);
1875 RefPtr<SmartMockCubebStream> nonNativeStream =
1876 WaitFor(cubeb->StreamInitEvent());
1877 EXPECT_TRUE(nonNativeStream->mHasInput);
1878 EXPECT_FALSE(nonNativeStream->mHasOutput);
1879 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
1880 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1882 // Open a 2-channel AudioProcessingTrack for the non-native device and wait
1883 // for a new stream since the max-channel for the non-native device becomes
1884 // 2 now.
1885 RefPtr<AudioProcessingTrack> track4;
1886 RefPtr<AudioInputProcessing> listener4;
1887 openTrack(nonNativeStream, track4, listener4, nonNativeDevice, 2);
1888 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
1889 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1891 // Set the second AudioProcessingTrack for the non-native to 1-channel and
1892 // wait for a new driver since the max-channel for the non-native device
1893 // becomes 1 now.
1894 setNewChannelCount(track4, listener4, nonNativeStream, 1);
1895 EXPECT_EQ(nonNativeStream->InputChannels(), 1U);
1896 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1898 // Set the first AudioProcessingTrack for the non-native device to 2-channel
1899 // and wait for a new driver since the max input channel for the non-native
1900 // device becomes 2 now.
1901 setNewChannelCount(track3, listener3, nonNativeStream, 2);
1902 EXPECT_EQ(nonNativeStream->InputChannels(), 2U);
1903 EXPECT_EQ(nonNativeStream->GetInputDeviceID(), nonNativeDevice);
1905 // Close the second AudioProcessingTrack (1-channel) for the non-native
1906 // device then the first one (2-channel) so we won't result in another
1907 // stream creation.
1908 DispatchFunction([&] {
1909 track4->GraphImpl()->AppendMessage(
1910 MakeUnique<StopInputProcessing>(track4, listener4));
1911 track4->DisconnectDeviceInput();
1912 track4->Destroy();
1914 DispatchFunction([&] {
1915 track3->GraphImpl()->AppendMessage(
1916 MakeUnique<StopInputProcessing>(track3, listener3));
1917 track3->DisconnectDeviceInput();
1918 track3->Destroy();
1920 RefPtr<SmartMockCubebStream> destroyedStream =
1921 WaitFor(cubeb->StreamDestroyEvent());
1922 EXPECT_EQ(destroyedStream.get(), nonNativeStream.get());
1925 // Tear down for the native device.
1927 // Close the second AudioProcessingTrack (1-channel) for the native device
1928 // then the first one (2-channel) so we won't have driver switching.
1929 DispatchFunction([&] {
1930 track2->GraphImpl()->AppendMessage(
1931 MakeUnique<StopInputProcessing>(track2, listener2));
1932 track2->DisconnectDeviceInput();
1933 track2->Destroy();
1935 DispatchFunction([&] {
1936 track1->GraphImpl()->AppendMessage(
1937 MakeUnique<StopInputProcessing>(track1, listener1));
1938 track1->DisconnectDeviceInput();
1939 track1->Destroy();
1941 RefPtr<SmartMockCubebStream> destroyedStream =
1942 WaitFor(cubeb->StreamDestroyEvent());
1943 EXPECT_EQ(destroyedStream.get(), nativeStream.get());
1947 TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)
1949 MockCubeb* cubeb = new MockCubeb();
1950 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
1952 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
1953 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
1954 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
1955 nullptr, GetMainThreadSerialEventTarget());
1957 // Set the input channel count of AudioInputProcessing, which will force
1958 // MediaTrackGraph to re-evaluate input device, when the MediaTrackGraph is
1959 // driven by the SystemClockDriver.
1961 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
1962 RefPtr<AudioProcessingTrack> track;
1963 RefPtr<AudioInputProcessing> listener;
1965 MozPromiseHolder<GenericPromise> h;
1966 RefPtr<GenericPromise> p = h.Ensure(__func__);
1968 struct GuardMessage : public ControlMessage {
1969 MozPromiseHolder<GenericPromise> mHolder;
1971 GuardMessage(MediaTrack* aTrack,
1972 MozPromiseHolder<GenericPromise>&& aHolder)
1973 : ControlMessage(aTrack), mHolder(std::move(aHolder)) {}
1974 void Run() override {
1975 mTrack->GraphImpl()->Dispatch(NS_NewRunnableFunction(
1976 "TestAudioTrackGraph::SetInputChannel::Message::Resolver",
1977 [holder = std::move(mHolder)]() mutable {
1978 holder.Resolve(true, __func__);
1979 }));
1983 DispatchFunction([&] {
1984 track = AudioProcessingTrack::Create(graph);
1985 listener = new AudioInputProcessing(2);
1986 QueueExpectIsPassThrough(track, listener);
1987 track->SetInputProcessing(listener);
1989 MediaEnginePrefs settings;
1990 settings.mChannels = 1;
1991 QueueApplySettings(track, listener, settings);
1993 track->GraphImpl()->AppendMessage(
1994 MakeUnique<GuardMessage>(track, std::move(h)));
1997 Unused << WaitFor(p);
2000 // Open a full-duplex AudioCallbackDriver.
2002 RefPtr<MediaInputPort> port;
2003 DispatchFunction([&] {
2004 track->GraphImpl()->AppendMessage(
2005 MakeUnique<StartInputProcessing>(track, listener));
2006 track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
2009 // MediaTrackGraph will create a output-only AudioCallbackDriver in
2010 // CheckDriver before we open an audio input above, since AudioProcessingTrack
2011 // is a audio-type MediaTrack, so we need to wait here until the duplex
2012 // AudioCallbackDriver is created.
2013 RefPtr<SmartMockCubebStream> stream;
2014 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
2015 "TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)"_ns,
2016 [&] {
2017 stream = WaitFor(cubeb->StreamInitEvent());
2018 EXPECT_TRUE(stream->mHasOutput);
2019 return stream->mHasInput;
2021 EXPECT_EQ(stream->InputChannels(), 1U);
2023 Unused << WaitFor(
2024 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
2026 // Clean up.
2027 DispatchFunction([&] {
2028 track->GraphImpl()->AppendMessage(
2029 MakeUnique<StopInputProcessing>(track, listener));
2030 track->DisconnectDeviceInput();
2031 track->Destroy();
2033 Unused << WaitFor(cubeb->StreamDestroyEvent());
2036 TEST(TestAudioTrackGraph, StartAudioDeviceBeforeStartingAudioProcessing)
2038 MockCubeb* cubeb = new MockCubeb();
2039 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2041 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2042 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2043 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2044 nullptr, GetMainThreadSerialEventTarget());
2046 // Create a duplex AudioCallbackDriver
2047 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
2048 RefPtr<AudioProcessingTrack> track;
2049 RefPtr<AudioInputProcessing> listener;
2050 DispatchFunction([&] {
2051 track = AudioProcessingTrack::Create(graph);
2052 listener = new AudioInputProcessing(2);
2053 QueueExpectIsPassThrough(track, listener);
2054 track->SetInputProcessing(listener);
2055 // Start audio device without starting audio processing.
2056 track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
2059 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
2060 EXPECT_TRUE(stream->mHasInput);
2061 EXPECT_TRUE(stream->mHasOutput);
2063 // Wait for a second to make sure audio output callback has been fired.
2064 DispatchFunction(
2065 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2067 uint32_t totalFrames = 0;
2068 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2069 totalFrames += aFrames;
2070 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2073 cubeb->DontGoFaster();
2075 // Start the audio processing.
2076 DispatchFunction([&] {
2077 track->GraphImpl()->AppendMessage(
2078 MakeUnique<StartInputProcessing>(track, listener));
2081 // Wait for a second to make sure audio output callback has been fired.
2082 DispatchFunction(
2083 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2085 uint32_t totalFrames = 0;
2086 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2087 totalFrames += aFrames;
2088 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2091 cubeb->DontGoFaster();
2093 // Clean up.
2094 DispatchFunction([&] {
2095 track->DisconnectDeviceInput();
2096 track->Destroy();
2098 Unused << WaitFor(cubeb->StreamDestroyEvent());
2101 TEST(TestAudioTrackGraph, StopAudioProcessingBeforeStoppingAudioDevice)
2103 MockCubeb* cubeb = new MockCubeb();
2104 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2106 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2107 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2108 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2109 nullptr, GetMainThreadSerialEventTarget());
2111 // Create a duplex AudioCallbackDriver
2112 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
2113 RefPtr<AudioProcessingTrack> track;
2114 RefPtr<AudioInputProcessing> listener;
2115 DispatchFunction([&] {
2116 track = AudioProcessingTrack::Create(graph);
2117 listener = new AudioInputProcessing(2);
2118 QueueExpectIsPassThrough(track, listener);
2119 track->SetInputProcessing(listener);
2120 track->GraphImpl()->AppendMessage(
2121 MakeUnique<StartInputProcessing>(track, listener));
2122 track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
2125 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
2126 EXPECT_TRUE(stream->mHasInput);
2127 EXPECT_TRUE(stream->mHasOutput);
2129 // Wait for a second to make sure audio output callback has been fired.
2130 DispatchFunction(
2131 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2133 uint32_t totalFrames = 0;
2134 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2135 totalFrames += aFrames;
2136 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2139 cubeb->DontGoFaster();
2141 // Stop the audio processing
2142 DispatchFunction([&] {
2143 track->GraphImpl()->AppendMessage(
2144 MakeUnique<StopInputProcessing>(track, listener));
2147 // Wait for a second to make sure audio output callback has been fired.
2148 DispatchFunction(
2149 [&] { track->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb)); });
2151 uint32_t totalFrames = 0;
2152 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2153 totalFrames += aFrames;
2154 return totalFrames > static_cast<uint32_t>(graph->GraphRate());
2157 cubeb->DontGoFaster();
2159 // Clean up.
2160 DispatchFunction([&] {
2161 track->DisconnectDeviceInput();
2162 track->Destroy();
2164 Unused << WaitFor(cubeb->StreamDestroyEvent());
2167 // This test is pretty similar to SwitchNativeInputDevice above, which makes
2168 // sure the related DeviceInputTrack operations for the test here works
2169 // correctly. Instead of using a test-only DeviceInputTrack consumer, we use
2170 // AudioProcessingTrack here to simulate the real world use case.
2171 TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack)
2173 MockCubeb* cubeb = new MockCubeb();
2174 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2176 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2177 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
2178 CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
2179 nullptr, GetMainThreadSerialEventTarget());
2181 auto switchNativeDevice =
2182 [&](RefPtr<SmartMockCubebStream>&& aCurrentNativeStream,
2183 RefPtr<AudioProcessingTrack>& aCurrentNativeTrack,
2184 RefPtr<AudioInputProcessing>& aCurrentNativeListener,
2185 RefPtr<SmartMockCubebStream>& aNextNativeStream,
2186 RefPtr<AudioProcessingTrack>& aNextNativeTrack) {
2187 ASSERT_TRUE(aCurrentNativeStream->mHasInput);
2188 ASSERT_TRUE(aCurrentNativeStream->mHasOutput);
2189 ASSERT_TRUE(aNextNativeStream->mHasInput);
2190 ASSERT_FALSE(aNextNativeStream->mHasOutput);
2192 std::cerr << "Switching native input from device "
2193 << aCurrentNativeStream->GetInputDeviceID() << " to "
2194 << aNextNativeStream->GetInputDeviceID() << std::endl;
2196 uint32_t destroyed = 0;
2197 MediaEventListener destroyListener =
2198 cubeb->StreamDestroyEvent().Connect(
2199 AbstractThread::GetCurrent(),
2200 [&](const RefPtr<SmartMockCubebStream>& aDestroyed) {
2201 if (aDestroyed.get() == aCurrentNativeStream.get() ||
2202 aDestroyed.get() == aNextNativeStream.get()) {
2203 std::cerr << "cubeb stream " << aDestroyed.get()
2204 << " (device " << aDestroyed->GetInputDeviceID()
2205 << ") has been destroyed" << std::endl;
2206 destroyed += 1;
2210 RefPtr<SmartMockCubebStream> newStream;
2211 MediaEventListener restartListener = cubeb->StreamInitEvent().Connect(
2212 AbstractThread::GetCurrent(),
2213 [&](const RefPtr<SmartMockCubebStream>& aCreated) {
2214 // Make sure new stream has input, to prevent from getting a
2215 // temporary output-only AudioCallbackDriver after closing current
2216 // native device but before setting a new native input.
2217 if (aCreated->mHasInput) {
2218 ASSERT_TRUE(aCreated->mHasOutput);
2219 newStream = aCreated;
2223 std::cerr << "Close device " << aCurrentNativeStream->GetInputDeviceID()
2224 << std::endl;
2225 DispatchFunction([&] {
2226 aCurrentNativeTrack->GraphImpl()->AppendMessage(
2227 MakeUnique<StopInputProcessing>(aCurrentNativeTrack,
2228 aCurrentNativeListener));
2229 aCurrentNativeTrack->DisconnectDeviceInput();
2230 aCurrentNativeTrack->Destroy();
2233 std::cerr << "Wait for the switching" << std::endl;
2234 SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
2235 "TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack)"_ns,
2236 [&] { return destroyed >= 2 && newStream; });
2238 destroyListener.Disconnect();
2239 restartListener.Disconnect();
2241 aCurrentNativeStream = nullptr;
2242 aNextNativeStream = newStream;
2244 std::cerr << "Now the native input is device "
2245 << aNextNativeStream->GetInputDeviceID() << std::endl;
2248 // Open a AudioProcessingTrack for device 1.
2249 const CubebUtils::AudioDeviceID device1 = (CubebUtils::AudioDeviceID)1;
2250 RefPtr<AudioProcessingTrack> track1 = AudioProcessingTrack::Create(graph);
2251 RefPtr<AudioInputProcessing> listener1 = new AudioInputProcessing(1);
2252 track1->SetInputProcessing(listener1);
2253 QueueExpectIsPassThrough(track1, listener1);
2254 track1->GraphImpl()->AppendMessage(
2255 MakeUnique<StartInputProcessing>(track1, listener1));
2256 track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
2257 EXPECT_EQ(track1->DeviceId().value(), device1);
2259 auto started =
2260 Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
2262 RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
2263 EXPECT_TRUE(stream1->mHasInput);
2264 EXPECT_TRUE(stream1->mHasOutput);
2265 EXPECT_EQ(stream1->InputChannels(), 1U);
2266 EXPECT_EQ(stream1->GetInputDeviceID(), device1);
2267 Unused << WaitFor(started);
2268 std::cerr << "Device " << device1 << " is opened (stream " << stream1.get()
2269 << ")" << std::endl;
2271 // Open a AudioProcessingTrack for device 2.
2272 const CubebUtils::AudioDeviceID device2 = (CubebUtils::AudioDeviceID)2;
2273 RefPtr<AudioProcessingTrack> track2 = AudioProcessingTrack::Create(graph);
2274 RefPtr<AudioInputProcessing> listener2 = new AudioInputProcessing(2);
2275 track2->SetInputProcessing(listener2);
2276 QueueExpectIsPassThrough(track2, listener2);
2277 track2->GraphImpl()->AppendMessage(
2278 MakeUnique<StartInputProcessing>(track2, listener2));
2279 track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
2280 EXPECT_EQ(track2->DeviceId().value(), device2);
2282 RefPtr<SmartMockCubebStream> stream2 = WaitFor(cubeb->StreamInitEvent());
2283 EXPECT_TRUE(stream2->mHasInput);
2284 EXPECT_FALSE(stream2->mHasOutput);
2285 EXPECT_EQ(stream2->InputChannels(), 2U);
2286 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
2287 std::cerr << "Device " << device2 << " is opened (stream " << stream2.get()
2288 << ")" << std::endl;
2290 // Open a AudioProcessingTrack for device 3.
2291 const CubebUtils::AudioDeviceID device3 = (CubebUtils::AudioDeviceID)3;
2292 RefPtr<AudioProcessingTrack> track3 = AudioProcessingTrack::Create(graph);
2293 RefPtr<AudioInputProcessing> listener3 = new AudioInputProcessing(1);
2294 track3->SetInputProcessing(listener3);
2295 QueueExpectIsPassThrough(track3, listener3);
2296 track3->GraphImpl()->AppendMessage(
2297 MakeUnique<StartInputProcessing>(track3, listener3));
2298 track3->ConnectDeviceInput(device3, listener3, PRINCIPAL_HANDLE_NONE);
2299 EXPECT_EQ(track3->DeviceId().value(), device3);
2301 RefPtr<SmartMockCubebStream> stream3 = WaitFor(cubeb->StreamInitEvent());
2302 EXPECT_TRUE(stream3->mHasInput);
2303 EXPECT_FALSE(stream3->mHasOutput);
2304 EXPECT_EQ(stream3->InputChannels(), 1U);
2305 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
2306 std::cerr << "Device " << device3 << " is opened (stream " << stream3.get()
2307 << ")" << std::endl;
2309 // Close device 1, so the native input device is switched from device 1 to
2310 // device 2.
2311 switchNativeDevice(std::move(stream1), track1, listener1, stream2, track2);
2312 EXPECT_TRUE(stream2->mHasInput);
2313 EXPECT_TRUE(stream2->mHasOutput);
2314 EXPECT_EQ(stream2->InputChannels(), 2U);
2315 EXPECT_EQ(stream2->GetInputDeviceID(), device2);
2317 NativeInputTrack* native = track2->Graph()->GetNativeInputTrackMainThread();
2318 ASSERT_TRUE(!!native);
2319 EXPECT_EQ(native->mDeviceId, device2);
2322 // Close device 2, so the native input device is switched from device 2 to
2323 // device 3.
2324 switchNativeDevice(std::move(stream2), track2, listener2, stream3, track3);
2325 EXPECT_TRUE(stream3->mHasInput);
2326 EXPECT_TRUE(stream3->mHasOutput);
2327 EXPECT_EQ(stream3->InputChannels(), 1U);
2328 EXPECT_EQ(stream3->GetInputDeviceID(), device3);
2330 NativeInputTrack* native = track3->Graph()->GetNativeInputTrackMainThread();
2331 ASSERT_TRUE(!!native);
2332 EXPECT_EQ(native->mDeviceId, device3);
2335 // Clean up.
2336 std::cerr << "Close device " << device3 << std::endl;
2337 DispatchFunction([&] {
2338 track3->GraphImpl()->AppendMessage(
2339 MakeUnique<StopInputProcessing>(track3, listener3));
2340 track3->DisconnectDeviceInput();
2341 track3->Destroy();
2343 RefPtr<SmartMockCubebStream> destroyedStream =
2344 WaitFor(cubeb->StreamDestroyEvent());
2345 EXPECT_EQ(destroyedStream.get(), stream3.get());
2347 NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
2348 ASSERT_TRUE(!native);
2350 std::cerr << "No native input now" << std::endl;
2353 class OnFallbackListener : public MediaTrackListener {
2354 const RefPtr<MediaTrack> mTrack;
2355 Atomic<bool> mOnFallback{true};
2357 public:
2358 explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {}
2360 bool OnFallback() { return mOnFallback; }
2362 void NotifyOutput(MediaTrackGraph*, TrackTime) override {
2363 if (auto* ad =
2364 mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) {
2365 mOnFallback = ad->OnFallback();
2370 void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
2371 float aDriftFactor, uint32_t aRunTimeSeconds = 10,
2372 uint32_t aNumExpectedUnderruns = 0) {
2373 std::cerr << "TestCrossGraphPort input: " << aInputRate
2374 << ", output: " << aOutputRate << ", driftFactor: " << aDriftFactor
2375 << std::endl;
2377 MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
2378 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2380 /* Primary graph: Create the graph. */
2381 MediaTrackGraph* primary = MediaTrackGraphImpl::GetInstance(
2382 MediaTrackGraph::SYSTEM_THREAD_DRIVER,
2383 /*Window ID*/ 1, aInputRate, nullptr, GetMainThreadSerialEventTarget());
2385 /* Partner graph: Create the graph. */
2386 MediaTrackGraph* partner = MediaTrackGraphImpl::GetInstance(
2387 MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, aOutputRate,
2388 /*OutputDeviceID*/ reinterpret_cast<cubeb_devid>(1),
2389 GetMainThreadSerialEventTarget());
2391 const CubebUtils::AudioDeviceID inputDeviceId = (CubebUtils::AudioDeviceID)1;
2393 RefPtr<AudioProcessingTrack> processingTrack;
2394 RefPtr<AudioInputProcessing> listener;
2395 RefPtr<OnFallbackListener> primaryFallbackListener;
2396 DispatchFunction([&] {
2397 /* Primary graph: Create input track and open it */
2398 processingTrack = AudioProcessingTrack::Create(primary);
2399 listener = new AudioInputProcessing(2);
2400 QueueExpectIsPassThrough(processingTrack, listener);
2401 processingTrack->SetInputProcessing(listener);
2402 processingTrack->GraphImpl()->AppendMessage(
2403 MakeUnique<StartInputProcessing>(processingTrack, listener));
2404 processingTrack->ConnectDeviceInput(inputDeviceId, listener,
2405 PRINCIPAL_HANDLE_NONE);
2406 primaryFallbackListener = new OnFallbackListener(processingTrack);
2407 processingTrack->AddListener(primaryFallbackListener);
2410 RefPtr<SmartMockCubebStream> inputStream = WaitFor(cubeb->StreamInitEvent());
2412 // Wait for the primary AudioCallbackDriver to come into effect.
2413 while (primaryFallbackListener->OnFallback()) {
2414 EXPECT_EQ(inputStream->ManualDataCallback(0),
2415 MockCubebStream::KeepProcessing::Yes);
2416 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2419 RefPtr<CrossGraphTransmitter> transmitter;
2420 RefPtr<MediaInputPort> port;
2421 RefPtr<CrossGraphReceiver> receiver;
2422 RefPtr<OnFallbackListener> partnerFallbackListener;
2423 DispatchFunction([&] {
2424 processingTrack->RemoveListener(primaryFallbackListener);
2426 /* Partner graph: Create CrossGraphReceiver */
2427 receiver = partner->CreateCrossGraphReceiver(primary->GraphRate());
2429 /* Primary graph: Create CrossGraphTransmitter */
2430 transmitter = primary->CreateCrossGraphTransmitter(receiver);
2432 /* How the input track connects to another ProcessedMediaTrack.
2433 * Check in MediaManager how it is connected to AudioStreamTrack. */
2434 port = transmitter->AllocateInputPort(processingTrack);
2435 receiver->AddAudioOutput((void*)1, partner->PrimaryOutputDeviceID(), 0);
2437 partnerFallbackListener = new OnFallbackListener(receiver);
2438 receiver->AddListener(partnerFallbackListener);
2441 RefPtr<SmartMockCubebStream> partnerStream =
2442 WaitFor(cubeb->StreamInitEvent());
2444 // Process the CrossGraphTransmitter on the primary graph.
2445 EXPECT_EQ(inputStream->ManualDataCallback(0),
2446 MockCubebStream::KeepProcessing::Yes);
2448 // Wait for the partner AudioCallbackDriver to come into effect.
2449 while (partnerFallbackListener->OnFallback()) {
2450 EXPECT_EQ(partnerStream->ManualDataCallback(0),
2451 MockCubebStream::KeepProcessing::Yes);
2452 std::this_thread::sleep_for(std::chrono::milliseconds(1));
2455 DispatchFunction([&] { receiver->RemoveListener(partnerFallbackListener); });
2456 while (NS_ProcessNextEvent(nullptr, false)) {
2459 nsIThread* currentThread = NS_GetCurrentThread();
2460 cubeb_state inputState = CUBEB_STATE_STARTED;
2461 MediaEventListener inputStateListener = inputStream->StateEvent().Connect(
2462 currentThread, [&](cubeb_state aState) { inputState = aState; });
2463 cubeb_state partnerState = CUBEB_STATE_STARTED;
2464 MediaEventListener partnerStateListener = partnerStream->StateEvent().Connect(
2465 currentThread, [&](cubeb_state aState) { partnerState = aState; });
2467 const media::TimeUnit runtime = media::TimeUnit::FromSeconds(aRunTimeSeconds);
2468 // 10ms per iteration.
2469 const media::TimeUnit step = media::TimeUnit::FromSeconds(0.01);
2471 media::TimeUnit pos = media::TimeUnit::Zero();
2472 long inputFrames = 0;
2473 long outputFrames = 0;
2474 while (pos < runtime) {
2475 pos += step;
2476 const long newInputFrames = pos.ToTicksAtRate(aInputRate);
2477 const long newOutputFrames =
2478 (pos.MultDouble(aDriftFactor)).ToTicksAtRate(aOutputRate);
2479 EXPECT_EQ(inputStream->ManualDataCallback(newInputFrames - inputFrames),
2480 MockCubebStream::KeepProcessing::Yes);
2481 EXPECT_EQ(
2482 partnerStream->ManualDataCallback(newOutputFrames - outputFrames),
2483 MockCubebStream::KeepProcessing::Yes);
2485 inputFrames = newInputFrames;
2486 outputFrames = newOutputFrames;
2490 DispatchFunction([&] {
2491 // Clean up on MainThread
2492 receiver->RemoveAudioOutput((void*)1);
2493 receiver->Destroy();
2494 transmitter->Destroy();
2495 port->Destroy();
2496 processingTrack->GraphImpl()->AppendMessage(
2497 MakeUnique<StopInputProcessing>(processingTrack, listener));
2498 processingTrack->DisconnectDeviceInput();
2499 processingTrack->Destroy();
2502 while (NS_ProcessNextEvent(nullptr, false)) {
2505 EXPECT_EQ(inputStream->ManualDataCallback(0),
2506 MockCubebStream::KeepProcessing::Yes);
2507 EXPECT_EQ(partnerStream->ManualDataCallback(0),
2508 MockCubebStream::KeepProcessing::Yes);
2510 EXPECT_EQ(inputStream->ManualDataCallback(128),
2511 MockCubebStream::KeepProcessing::No);
2512 EXPECT_EQ(partnerStream->ManualDataCallback(128),
2513 MockCubebStream::KeepProcessing::No);
2515 uint32_t inputFrequency = inputStream->InputFrequency();
2517 uint64_t preSilenceSamples;
2518 float estimatedFreq;
2519 uint32_t nrDiscontinuities;
2520 std::tie(preSilenceSamples, estimatedFreq, nrDiscontinuities) =
2521 WaitFor(partnerStream->OutputVerificationEvent());
2523 EXPECT_NEAR(estimatedFreq, inputFrequency / aDriftFactor, 5);
2524 // Note that pre-silence is in the output rate. The buffering is on the input
2525 // side. There is one block buffered in NativeInputTrack. Then
2526 // AudioDriftCorrection sets its pre-buffering so that *after* the first
2527 // resample of real input data, the buffer contains enough data to match the
2528 // desired level, which is initially 50ms. I.e. silence = buffering -
2529 // inputStep + outputStep. Note that the steps here are rounded up to block
2530 // size.
2531 const media::TimeUnit inputBuffering(WEBAUDIO_BLOCK_SIZE, aInputRate);
2532 const media::TimeUnit buffering =
2533 media::TimeUnit::FromSeconds(0.05).ToBase(aInputRate);
2534 const media::TimeUnit inputStepSize(
2535 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(
2536 step.ToTicksAtRate(aInputRate)),
2537 aInputRate);
2538 const media::TimeUnit outputStepSize =
2539 media::TimeUnit(MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(
2540 step.ToBase(aOutputRate)
2541 .MultDouble(aDriftFactor)
2542 .ToTicksAtRate(aOutputRate)),
2543 aOutputRate)
2544 .ToBase(aInputRate);
2545 const uint32_t expectedPreSilence =
2546 (outputStepSize + inputBuffering + buffering - inputStepSize)
2547 .ToBase(aInputRate)
2548 .ToBase<media::TimeUnit::CeilingPolicy>(aOutputRate)
2549 .ToTicksAtRate(aOutputRate);
2550 // Use a margin of 0.1% of the expected pre-silence, since the resampler is
2551 // adapting to drift and will process the pre-silence frames. Because of
2552 // rounding errors, we don't use a margin lower than 1.
2553 const uint32_t margin = std::max(1U, expectedPreSilence / 1000);
2554 EXPECT_NEAR(preSilenceSamples, expectedPreSilence, margin);
2555 // The waveform from AudioGenerator starts at 0, but we don't control its
2556 // ending, so we expect a discontinuity there. For each expected underrun
2557 // there could be an additional 2 discontinuities (start and end of the silent
2558 // period).
2559 EXPECT_LE(nrDiscontinuities, 1U + 2 * aNumExpectedUnderruns);
2561 SpinEventLoopUntil("streams have stopped"_ns, [&] {
2562 return inputState == CUBEB_STATE_STOPPED &&
2563 partnerState == CUBEB_STATE_STOPPED;
2565 inputStateListener.Disconnect();
2566 partnerStateListener.Disconnect();
2569 TEST(TestAudioTrackGraph, CrossGraphPort)
2571 TestCrossGraphPort(44100, 44100, 1);
2572 TestCrossGraphPort(44100, 44100, 1.006);
2573 TestCrossGraphPort(44100, 44100, 0.994);
2575 TestCrossGraphPort(48000, 44100, 1);
2576 TestCrossGraphPort(48000, 44100, 1.006);
2577 TestCrossGraphPort(48000, 44100, 0.994);
2579 TestCrossGraphPort(44100, 48000, 1);
2580 TestCrossGraphPort(44100, 48000, 1.006);
2581 TestCrossGraphPort(44100, 48000, 0.994);
2583 TestCrossGraphPort(52110, 17781, 1);
2584 TestCrossGraphPort(52110, 17781, 1.006);
2585 TestCrossGraphPort(52110, 17781, 0.994);
2588 TEST(TestAudioTrackGraph, CrossGraphPortUnderrun)
2590 TestCrossGraphPort(44100, 44100, 1.01, 30, 1);
2591 TestCrossGraphPort(44100, 44100, 1.03, 40, 3);
2593 TestCrossGraphPort(48000, 44100, 1.01, 30, 1);
2594 TestCrossGraphPort(48000, 44100, 1.03, 40, 3);
2596 TestCrossGraphPort(44100, 48000, 1.01, 30, 1);
2597 TestCrossGraphPort(44100, 48000, 1.03, 40, 3);
2599 TestCrossGraphPort(52110, 17781, 1.01, 30, 1);
2600 TestCrossGraphPort(52110, 17781, 1.03, 40, 3);
2603 TEST(TestAudioTrackGraph, SecondaryOutputDevice)
2605 MockCubeb* cubeb = new MockCubeb();
2606 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
2608 const TrackRate primaryRate = 48000;
2609 const TrackRate secondaryRate = 44100; // for secondary output device
2611 MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
2612 MediaTrackGraph::SYSTEM_THREAD_DRIVER,
2613 /*Window ID*/ 1, primaryRate, nullptr, GetMainThreadSerialEventTarget());
2615 RefPtr<AudioProcessingTrack> processingTrack;
2616 RefPtr<AudioInputProcessing> listener;
2617 DispatchFunction([&] {
2618 /* Create an input track and connect it to a device */
2619 processingTrack = AudioProcessingTrack::Create(graph);
2620 listener = new AudioInputProcessing(2);
2621 QueueExpectIsPassThrough(processingTrack, listener);
2622 processingTrack->SetInputProcessing(listener);
2623 processingTrack->GraphImpl()->AppendMessage(
2624 MakeUnique<StartInputProcessing>(processingTrack, listener));
2625 processingTrack->ConnectDeviceInput(nullptr, listener,
2626 PRINCIPAL_HANDLE_NONE);
2628 RefPtr<SmartMockCubebStream> primaryStream =
2629 WaitFor(cubeb->StreamInitEvent());
2631 const void* secondaryDeviceID = CubebUtils::AudioDeviceID(2);
2632 DispatchFunction([&] {
2633 processingTrack->AddAudioOutput(nullptr, secondaryDeviceID, secondaryRate);
2634 processingTrack->SetAudioOutputVolume(nullptr, 0.f);
2636 RefPtr<SmartMockCubebStream> secondaryStream =
2637 WaitFor(cubeb->StreamInitEvent());
2638 EXPECT_EQ(secondaryStream->GetOutputDeviceID(), secondaryDeviceID);
2639 EXPECT_EQ(static_cast<TrackRate>(secondaryStream->SampleRate()),
2640 secondaryRate);
2642 nsIThread* currentThread = NS_GetCurrentThread();
2643 uint32_t audioFrames = 0; // excludes pre-silence
2644 MediaEventListener audioListener =
2645 secondaryStream->FramesVerifiedEvent().Connect(
2646 currentThread, [&](uint32_t aFrames) { audioFrames += aFrames; });
2648 // Wait for 100ms of pre-silence to verify that SetAudioOutputVolume() is
2649 // effective.
2650 uint32_t processedFrames = 0;
2651 WaitUntil(secondaryStream->FramesProcessedEvent(), [&](uint32_t aFrames) {
2652 processedFrames += aFrames;
2653 return processedFrames > static_cast<uint32_t>(secondaryRate / 10);
2655 EXPECT_EQ(audioFrames, 0U) << "audio frames at zero volume";
2657 secondaryStream->SetOutputRecordingEnabled(true);
2658 DispatchFunction(
2659 [&] { processingTrack->SetAudioOutputVolume(nullptr, 1.f); });
2661 // Wait for enough audio after initial silence to check the frequency.
2662 SpinEventLoopUntil("200ms of audio"_ns, [&] {
2663 return audioFrames > static_cast<uint32_t>(secondaryRate / 5);
2665 audioListener.Disconnect();
2667 // Stop recording now so as not to record the discontinuity when the
2668 // CrossGraphReceiver is removed from the secondary graph before its
2669 // AudioCallbackDriver is stopped.
2670 secondaryStream->SetOutputRecordingEnabled(false);
2672 DispatchFunction([&] { processingTrack->RemoveAudioOutput(nullptr); });
2673 WaitFor(secondaryStream->OutputVerificationEvent());
2674 // The frequency from OutputVerificationEvent() is estimated by
2675 // AudioVerifier from a zero-crossing count. When the discontinuity from
2676 // the volume change is resampled, the discontinuity presents as
2677 // oscillations, which increase the zero-crossing count and corrupt the
2678 // frequency estimate. Trim off sufficient leading from the output to
2679 // remove this discontinuity.
2680 uint32_t channelCount = secondaryStream->OutputChannels();
2681 nsTArray<AudioDataValue> output = secondaryStream->TakeRecordedOutput();
2682 size_t leadingIndex = 0;
2683 for (; leadingIndex < output.Length() && output[leadingIndex] == 0.f;
2684 leadingIndex += channelCount) {
2686 leadingIndex += 10 * channelCount; // skip discontinuity oscillations
2687 EXPECT_LT(leadingIndex, output.Length());
2688 auto trimmed = Span(output).From(std::min(leadingIndex, output.Length()));
2689 size_t frameCount = trimmed.Length() / channelCount;
2690 uint32_t inputFrequency = primaryStream->InputFrequency();
2691 AudioVerifier<AudioDataValue> verifier(secondaryRate, inputFrequency);
2692 verifier.AppendDataInterleaved(trimmed.Elements(), frameCount, channelCount);
2693 EXPECT_EQ(verifier.EstimatedFreq(), inputFrequency);
2694 // AudioVerifier considers the previous value before the initial sample to
2695 // be zero and so considers any initial sample >> 0 to be a discontinuity.
2696 EXPECT_EQ(verifier.CountDiscontinuities(), 1U);
2698 DispatchFunction([&] {
2699 // Clean up
2700 processingTrack->GraphImpl()->AppendMessage(
2701 MakeUnique<StopInputProcessing>(processingTrack, listener));
2702 processingTrack->DisconnectDeviceInput();
2703 processingTrack->Destroy();
2705 WaitFor(primaryStream->OutputVerificationEvent());
2707 #endif // MOZ_WEBRTC
2709 #undef Invoke
2710 #undef DispatchFunction
2711 #undef DispatchMethod