1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
7 #include "AudioInputSource.h"
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
12 #include "MockCubeb.h"
13 #include "mozilla/gtest/WaitFor.h"
14 #include "nsContentUtils.h"
16 using namespace mozilla
;
17 using testing::ContainerEq
;
20 #define DispatchFunction(f) \
21 NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
24 class MockEventListener
: public AudioInputSource::EventListener
{
26 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockEventListener
, override
);
27 MOCK_METHOD1(AudioDeviceChanged
, void(AudioInputSource::Id
));
28 MOCK_METHOD2(AudioStateCallback
,
29 void(AudioInputSource::Id
,
30 AudioInputSource::EventListener::State
));
33 ~MockEventListener() = default;
36 TEST(TestAudioInputSource
, StartAndStop
)
38 MockCubeb
* cubeb
= new MockCubeb();
39 CubebUtils::ForceSetCubebContext(cubeb
->AsCubebContext());
41 const AudioInputSource::Id sourceId
= 1;
42 const CubebUtils::AudioDeviceID deviceId
= (CubebUtils::AudioDeviceID
)1;
43 const uint32_t channels
= 2;
44 const PrincipalHandle testPrincipal
=
45 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
46 const TrackRate sourceRate
= 44100;
47 const TrackRate targetRate
= 48000;
49 auto listener
= MakeRefPtr
<MockEventListener
>();
50 EXPECT_CALL(*listener
,
52 sourceId
, AudioInputSource::EventListener::State::Started
))
54 EXPECT_CALL(*listener
,
56 sourceId
, AudioInputSource::EventListener::State::Stopped
))
59 RefPtr
<AudioInputSource
> ais
= MakeRefPtr
<AudioInputSource
>(
60 std::move(listener
), sourceId
, deviceId
, channels
, true, testPrincipal
,
61 sourceRate
, targetRate
);
64 // Make sure start and stop works.
66 DispatchFunction([&] { ais
->Start(); });
67 RefPtr
<SmartMockCubebStream
> stream
= WaitFor(cubeb
->StreamInitEvent());
68 EXPECT_TRUE(stream
->mHasInput
);
69 EXPECT_FALSE(stream
->mHasOutput
);
70 EXPECT_EQ(stream
->GetInputDeviceID(), deviceId
);
71 EXPECT_EQ(stream
->InputChannels(), channels
);
72 EXPECT_EQ(stream
->SampleRate(), static_cast<uint32_t>(sourceRate
));
74 Unused
<< WaitFor(stream
->FramesProcessedEvent());
76 DispatchFunction([&] { ais
->Stop(); });
77 Unused
<< WaitFor(cubeb
->StreamDestroyEvent());
80 // Make sure restart is ok.
82 DispatchFunction([&] { ais
->Start(); });
83 RefPtr
<SmartMockCubebStream
> stream
= WaitFor(cubeb
->StreamInitEvent());
84 EXPECT_TRUE(stream
->mHasInput
);
85 EXPECT_FALSE(stream
->mHasOutput
);
86 EXPECT_EQ(stream
->GetInputDeviceID(), deviceId
);
87 EXPECT_EQ(stream
->InputChannels(), channels
);
88 EXPECT_EQ(stream
->SampleRate(), static_cast<uint32_t>(sourceRate
));
90 Unused
<< WaitFor(stream
->FramesProcessedEvent());
92 DispatchFunction([&] { ais
->Stop(); });
93 Unused
<< WaitFor(cubeb
->StreamDestroyEvent());
96 ais
= nullptr; // Drop the SharedThreadPool here.
99 TEST(TestAudioInputSource
, DataOutputBeforeStartAndAfterStop
)
101 MockCubeb
* cubeb
= new MockCubeb();
102 CubebUtils::ForceSetCubebContext(cubeb
->AsCubebContext());
104 const AudioInputSource::Id sourceId
= 1;
105 const CubebUtils::AudioDeviceID deviceId
= (CubebUtils::AudioDeviceID
)1;
106 const uint32_t channels
= 2;
107 const PrincipalHandle testPrincipal
=
108 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
109 const TrackRate sourceRate
= 44100;
110 const TrackRate targetRate
= 48000;
112 const TrackTime requestFrames
= 2 * WEBAUDIO_BLOCK_SIZE
;
114 auto listener
= MakeRefPtr
<MockEventListener
>();
115 EXPECT_CALL(*listener
,
117 sourceId
, AudioInputSource::EventListener::State::Started
));
118 EXPECT_CALL(*listener
,
120 sourceId
, AudioInputSource::EventListener::State::Stopped
))
123 RefPtr
<AudioInputSource
> ais
= MakeRefPtr
<AudioInputSource
>(
124 std::move(listener
), sourceId
, deviceId
, channels
, true, testPrincipal
,
125 sourceRate
, targetRate
);
128 // It's ok to call GetAudioSegment before starting
131 ais
->GetAudioSegment(requestFrames
, AudioInputSource::Consumer::Same
);
132 EXPECT_EQ(data
.GetDuration(), requestFrames
);
133 EXPECT_TRUE(data
.IsNull());
136 DispatchFunction([&] { ais
->Start(); });
137 RefPtr
<SmartMockCubebStream
> stream
= WaitFor(cubeb
->StreamInitEvent());
138 EXPECT_TRUE(stream
->mHasInput
);
139 EXPECT_FALSE(stream
->mHasOutput
);
140 EXPECT_EQ(stream
->InputChannels(), channels
);
142 stream
->SetInputRecordingEnabled(true);
144 Unused
<< WaitFor(stream
->FramesProcessedEvent());
146 DispatchFunction([&] { ais
->Stop(); });
147 Unused
<< WaitFor(cubeb
->StreamDestroyEvent());
149 // Check the data output
151 nsTArray
<AudioDataValue
> record
= stream
->TakeRecordedInput();
152 size_t frames
= record
.Length() / channels
;
153 AudioSegment deinterleaved
;
154 deinterleaved
.AppendFromInterleavedBuffer(record
.Elements(), frames
,
155 channels
, testPrincipal
);
156 AudioDriftCorrection
driftCorrector(sourceRate
, targetRate
, testPrincipal
);
157 AudioSegment expectedSegment
= driftCorrector
.RequestFrames(
158 deinterleaved
, static_cast<uint32_t>(requestFrames
));
160 CopyableTArray
<AudioDataValue
> expected
;
161 size_t expectedSamples
=
162 expectedSegment
.WriteToInterleavedBuffer(expected
, channels
);
164 AudioSegment actualSegment
=
165 ais
->GetAudioSegment(requestFrames
, AudioInputSource::Consumer::Same
);
166 EXPECT_EQ(actualSegment
.GetDuration(), requestFrames
);
167 CopyableTArray
<AudioDataValue
> actual
;
168 size_t actualSamples
=
169 actualSegment
.WriteToInterleavedBuffer(actual
, channels
);
171 EXPECT_EQ(actualSamples
, expectedSamples
);
172 EXPECT_EQ(actualSamples
/ channels
, static_cast<size_t>(requestFrames
));
173 EXPECT_THAT(actual
, ContainerEq(expected
));
176 ais
= nullptr; // Drop the SharedThreadPool here.
179 TEST(TestAudioInputSource
, ErrorCallback
)
181 MockCubeb
* cubeb
= new MockCubeb();
182 CubebUtils::ForceSetCubebContext(cubeb
->AsCubebContext());
184 const AudioInputSource::Id sourceId
= 1;
185 const CubebUtils::AudioDeviceID deviceId
= (CubebUtils::AudioDeviceID
)1;
186 const uint32_t channels
= 2;
187 const PrincipalHandle testPrincipal
=
188 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
189 const TrackRate sourceRate
= 44100;
190 const TrackRate targetRate
= 48000;
192 auto listener
= MakeRefPtr
<MockEventListener
>();
193 EXPECT_CALL(*listener
,
195 sourceId
, AudioInputSource::EventListener::State::Started
));
196 EXPECT_CALL(*listener
,
198 sourceId
, AudioInputSource::EventListener::State::Error
));
199 EXPECT_CALL(*listener
,
201 sourceId
, AudioInputSource::EventListener::State::Stopped
))
204 RefPtr
<AudioInputSource
> ais
= MakeRefPtr
<AudioInputSource
>(
205 std::move(listener
), sourceId
, deviceId
, channels
, true, testPrincipal
,
206 sourceRate
, targetRate
);
209 DispatchFunction([&] { ais
->Start(); });
210 RefPtr
<SmartMockCubebStream
> stream
= WaitFor(cubeb
->StreamInitEvent());
211 EXPECT_TRUE(stream
->mHasInput
);
212 EXPECT_FALSE(stream
->mHasOutput
);
213 EXPECT_EQ(stream
->InputChannels(), channels
);
215 Unused
<< WaitFor(stream
->FramesProcessedEvent());
217 DispatchFunction([&] { stream
->ForceError(); });
218 WaitFor(stream
->ErrorForcedEvent());
220 DispatchFunction([&] { ais
->Stop(); });
221 Unused
<< WaitFor(cubeb
->StreamDestroyEvent());
223 ais
= nullptr; // Drop the SharedThreadPool here.
226 TEST(TestAudioInputSource
, DeviceChangedCallback
)
228 MockCubeb
* cubeb
= new MockCubeb();
229 CubebUtils::ForceSetCubebContext(cubeb
->AsCubebContext());
231 const AudioInputSource::Id sourceId
= 1;
232 const CubebUtils::AudioDeviceID deviceId
= (CubebUtils::AudioDeviceID
)1;
233 const uint32_t channels
= 2;
234 const PrincipalHandle testPrincipal
=
235 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
236 const TrackRate sourceRate
= 44100;
237 const TrackRate targetRate
= 48000;
239 auto listener
= MakeRefPtr
<MockEventListener
>();
240 EXPECT_CALL(*listener
, AudioDeviceChanged(sourceId
));
241 EXPECT_CALL(*listener
,
243 sourceId
, AudioInputSource::EventListener::State::Started
));
244 EXPECT_CALL(*listener
,
246 sourceId
, AudioInputSource::EventListener::State::Stopped
))
249 RefPtr
<AudioInputSource
> ais
= MakeRefPtr
<AudioInputSource
>(
250 std::move(listener
), sourceId
, deviceId
, channels
, true, testPrincipal
,
251 sourceRate
, targetRate
);
254 DispatchFunction([&] { ais
->Start(); });
255 RefPtr
<SmartMockCubebStream
> stream
= WaitFor(cubeb
->StreamInitEvent());
256 EXPECT_TRUE(stream
->mHasInput
);
257 EXPECT_FALSE(stream
->mHasOutput
);
258 EXPECT_EQ(stream
->InputChannels(), channels
);
260 Unused
<< WaitFor(stream
->FramesProcessedEvent());
262 DispatchFunction([&] { stream
->ForceDeviceChanged(); });
263 WaitFor(stream
->DeviceChangeForcedEvent());
265 DispatchFunction([&] { ais
->Stop(); });
266 Unused
<< WaitFor(cubeb
->StreamDestroyEvent());
268 ais
= nullptr; // Drop the SharedThreadPool here.