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