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