Bug 1869043 rename MockCubebStream::InputSampleRate() SampleRate() r=pehrsons
[gecko.git] / dom / media / gtest / TestDeviceInputTrack.cpp
bloba5bdbcad18155b0b42b8415d78be0ddcba25e24a
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 "DeviceInputTrack.h"
9 #include "gmock/gmock.h"
10 #include "gtest/gtest.h"
12 #include "AudioGenerator.h"
13 #include "MediaTrackGraphImpl.h"
14 #include "MockCubeb.h"
15 #include "mozilla/gtest/WaitFor.h"
16 #include "mozilla/StaticPrefs_media.h"
17 #include "nsContentUtils.h"
19 using namespace mozilla;
20 using testing::NiceMock;
21 using testing::Return;
23 namespace {
24 #define DispatchFunction(f) \
25 NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
26 } // namespace
28 class MockGraphImpl : public MediaTrackGraphImpl {
29 public:
30 MockGraphImpl(TrackRate aRate, uint32_t aChannels)
31 : MediaTrackGraphImpl(OFFLINE_THREAD_DRIVER, DIRECT_DRIVER, 0, aRate,
32 aChannels, nullptr, NS_GetCurrentThread()) {
33 ON_CALL(*this, OnGraphThread).WillByDefault(Return(true));
34 // We have to call `Destroy()` manually in order to break the reference.
35 // The reason we don't assign a null driver is because we would add a track
36 // to the graph, then it would trigger graph's `EnsureNextIteration()` that
37 // requires a non-null driver.
38 SetCurrentDriver(new NiceMock<MockDriver>());
41 MOCK_CONST_METHOD0(OnGraphThread, bool());
42 MOCK_METHOD1(AppendMessage, void(UniquePtr<ControlMessageInterface>));
44 protected:
45 ~MockGraphImpl() = default;
47 class MockDriver : public GraphDriver {
48 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockDriver, override);
50 MockDriver() : GraphDriver(nullptr, nullptr, 0) {
51 ON_CALL(*this, OnThread).WillByDefault(Return(true));
52 ON_CALL(*this, ThreadRunning).WillByDefault(Return(true));
55 MOCK_METHOD0(Start, void());
56 MOCK_METHOD0(Shutdown, void());
57 MOCK_METHOD0(IterationDuration, uint32_t());
58 MOCK_METHOD0(EnsureNextIteration, void());
59 MOCK_CONST_METHOD0(OnThread, bool());
60 MOCK_CONST_METHOD0(ThreadRunning, bool());
62 protected:
63 ~MockDriver() = default;
67 class TestDeviceInputTrack : public testing::Test {
68 protected:
69 TestDeviceInputTrack() : mChannels(2), mRate(44100) {}
71 void SetUp() override {
72 mGraph = MakeRefPtr<NiceMock<MockGraphImpl>>(mRate, mChannels);
75 void TearDown() override { mGraph->Destroy(); }
77 const uint32_t mChannels;
78 const TrackRate mRate;
79 RefPtr<MockGraphImpl> mGraph;
82 TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
83 class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
84 public:
85 static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
86 MOZ_ASSERT(NS_IsMainThread());
87 TestDeviceInputConsumerTrack* track =
88 new TestDeviceInputConsumerTrack(aGraph->GraphRate());
89 aGraph->AddTrack(track);
90 return track;
93 void Destroy() {
94 MOZ_ASSERT(NS_IsMainThread());
95 DisconnectDeviceInput();
96 DeviceInputConsumerTrack::Destroy();
99 void ProcessInput(GraphTime aFrom, GraphTime aTo,
100 uint32_t aFlags) override{/* Ignored */};
102 uint32_t NumberOfChannels() const override {
103 if (mInputs.IsEmpty()) {
104 return 0;
106 DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
107 MOZ_ASSERT(t);
108 return t->NumberOfChannels();
111 private:
112 explicit TestDeviceInputConsumerTrack(TrackRate aSampleRate)
113 : DeviceInputConsumerTrack(aSampleRate) {}
116 class TestAudioDataListener : public AudioDataListener {
117 public:
118 TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
119 : mChannelCount(aChannelCount), mIsVoice(aIsVoice) {}
120 // Graph thread APIs: AudioDataListenerInterface implementations.
121 uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
122 aGraph->AssertOnGraphThread();
123 return mChannelCount;
125 bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
126 return mIsVoice;
128 void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */
130 void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
132 private:
133 ~TestAudioDataListener() = default;
135 // Graph thread-only.
136 uint32_t mChannelCount;
137 // Any thread.
138 const bool mIsVoice;
141 const PrincipalHandle testPrincipal =
142 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
144 const CubebUtils::AudioDeviceID device1 = (void*)1;
145 RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
146 RefPtr<TestDeviceInputConsumerTrack> track1 =
147 TestDeviceInputConsumerTrack::Create(mGraph);
148 track1->ConnectDeviceInput(device1, listener1.get(), testPrincipal);
149 EXPECT_TRUE(track1->ConnectToNativeDevice());
150 EXPECT_FALSE(track1->ConnectToNonNativeDevice());
152 const CubebUtils::AudioDeviceID device2 = (void*)2;
153 RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false);
154 RefPtr<TestDeviceInputConsumerTrack> track2 =
155 TestDeviceInputConsumerTrack::Create(mGraph);
156 track2->ConnectDeviceInput(device2, listener2.get(), testPrincipal);
157 EXPECT_FALSE(track2->ConnectToNativeDevice());
158 EXPECT_TRUE(track2->ConnectToNonNativeDevice());
160 track2->Destroy();
161 mGraph->RemoveTrackGraphThread(track2);
163 track1->Destroy();
164 mGraph->RemoveTrackGraphThread(track1);
167 TEST_F(TestDeviceInputTrack, NativeInputTrackData) {
168 const uint32_t flags = 0;
169 const CubebUtils::AudioDeviceID deviceId = (void*)1;
171 AudioGenerator<AudioDataValue> generator(mChannels, mRate);
172 const size_t nrFrames = 10;
173 const size_t bufferSize = nrFrames * mChannels;
174 nsTArray<AudioDataValue> buffer(bufferSize);
175 buffer.AppendElements(bufferSize);
177 const PrincipalHandle testPrincipal =
178 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
180 // Setup: Create a NativeInputTrack and add it to mGraph
181 RefPtr<NativeInputTrack> track =
182 new NativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
183 mGraph->AddTrack(track);
185 // Main test below:
187 generator.GenerateInterleaved(buffer.Elements(), nrFrames);
188 track->NotifyInputData(mGraph.get(), buffer.Elements(), nrFrames, mRate,
189 mChannels, 0);
191 track->ProcessInput(0, WEBAUDIO_BLOCK_SIZE + nrFrames, flags);
192 EXPECT_EQ(static_cast<size_t>(track->GetEnd()),
193 static_cast<size_t>(WEBAUDIO_BLOCK_SIZE) + nrFrames);
195 // Check pre-buffering: null data with PRINCIPAL_HANDLE_NONE principal
196 AudioSegment preBuffering;
197 preBuffering.AppendSlice(*track->GetData(), 0, WEBAUDIO_BLOCK_SIZE);
198 EXPECT_TRUE(preBuffering.IsNull());
199 for (AudioSegment::ConstChunkIterator iter(preBuffering); !iter.IsEnded();
200 iter.Next()) {
201 const AudioChunk& chunk = *iter;
202 EXPECT_EQ(chunk.mPrincipalHandle, PRINCIPAL_HANDLE_NONE);
205 // Check rest of the data
206 AudioSegment data;
207 data.AppendSlice(*track->GetData(), WEBAUDIO_BLOCK_SIZE,
208 WEBAUDIO_BLOCK_SIZE + nrFrames);
209 nsTArray<AudioDataValue> interleaved;
210 size_t sampleCount = data.WriteToInterleavedBuffer(interleaved, mChannels);
211 EXPECT_EQ(sampleCount, bufferSize);
212 EXPECT_EQ(interleaved, buffer);
214 // Check principal in data
215 for (AudioSegment::ConstChunkIterator iter(data); !iter.IsEnded();
216 iter.Next()) {
217 const AudioChunk& chunk = *iter;
218 EXPECT_EQ(chunk.mPrincipalHandle, testPrincipal);
221 // Tear down: Destroy the NativeInputTrack and remove it from mGraph.
222 track->Destroy();
223 mGraph->RemoveTrackGraphThread(track);
226 class MockEventListener : public AudioInputSource::EventListener {
227 public:
228 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MockEventListener, override);
229 MOCK_METHOD1(AudioDeviceChanged, void(AudioInputSource::Id));
230 MOCK_METHOD2(AudioStateCallback,
231 void(AudioInputSource::Id,
232 AudioInputSource::EventListener::State));
234 private:
235 ~MockEventListener() = default;
238 TEST_F(TestDeviceInputTrack, StartAndStop) {
239 MockCubeb* cubeb = new MockCubeb();
240 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
242 // Non native input settings
243 const AudioInputSource::Id sourceId = 1;
244 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
245 const uint32_t channels = 2;
246 const PrincipalHandle testPrincipal =
247 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
248 const TrackRate rate = 48000;
250 // Setup: Create a NonNativeInputTrack and add it to mGraph.
251 RefPtr<NonNativeInputTrack> track =
252 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
253 mGraph->AddTrack(track);
255 // Main test below:
257 // Make sure the NonNativeInputTrack can start and stop its audio correctly.
259 auto listener = MakeRefPtr<MockEventListener>();
260 EXPECT_CALL(*listener,
261 AudioStateCallback(
262 sourceId, AudioInputSource::EventListener::State::Started));
263 EXPECT_CALL(*listener,
264 AudioStateCallback(
265 sourceId, AudioInputSource::EventListener::State::Stopped))
266 .Times(2);
268 // No input channels and device preference before start.
269 EXPECT_EQ(track->NumberOfChannels(), 0U);
270 EXPECT_EQ(track->DevicePreference(), AudioInputType::Unknown);
272 DispatchFunction([&] {
273 track->StartAudio(MakeRefPtr<AudioInputSource>(
274 std::move(listener), sourceId, deviceId, channels, true /* voice */,
275 testPrincipal, rate, mGraph->GraphRate()));
278 // Wait for stream creation.
279 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
281 // Make sure the audio stream and the track's settings are correct.
282 EXPECT_TRUE(stream->mHasInput);
283 EXPECT_FALSE(stream->mHasOutput);
284 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
285 EXPECT_EQ(stream->InputChannels(), channels);
286 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
287 EXPECT_EQ(track->NumberOfChannels(), channels);
288 EXPECT_EQ(track->DevicePreference(), AudioInputType::Voice);
290 // Wait for stream callbacks.
291 Unused << WaitFor(stream->FramesProcessedEvent());
293 DispatchFunction([&] { track->StopAudio(); });
295 // Wait for stream destroy.
296 Unused << WaitFor(cubeb->StreamDestroyEvent());
298 // No input channels and device preference after stop.
299 EXPECT_EQ(track->NumberOfChannels(), 0U);
300 EXPECT_EQ(track->DevicePreference(), AudioInputType::Unknown);
303 // Make sure the NonNativeInputTrack can restart its audio correctly.
305 auto listener = MakeRefPtr<MockEventListener>();
306 EXPECT_CALL(*listener,
307 AudioStateCallback(
308 sourceId, AudioInputSource::EventListener::State::Started));
309 EXPECT_CALL(*listener,
310 AudioStateCallback(
311 sourceId, AudioInputSource::EventListener::State::Stopped))
312 .Times(2);
314 DispatchFunction([&] {
315 track->StartAudio(MakeRefPtr<AudioInputSource>(
316 std::move(listener), sourceId, deviceId, channels, true,
317 testPrincipal, rate, mGraph->GraphRate()));
320 // Wait for stream creation.
321 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
322 EXPECT_TRUE(stream->mHasInput);
323 EXPECT_FALSE(stream->mHasOutput);
324 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
325 EXPECT_EQ(stream->InputChannels(), channels);
326 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
328 // Wait for stream callbacks.
329 Unused << WaitFor(stream->FramesProcessedEvent());
331 DispatchFunction([&] { track->StopAudio(); });
333 // Wait for stream destroy.
334 Unused << WaitFor(cubeb->StreamDestroyEvent());
337 // Tear down: Destroy the NativeInputTrack and remove it from mGraph.
338 track->Destroy();
339 mGraph->RemoveTrackGraphThread(track);
342 TEST_F(TestDeviceInputTrack, NonNativeInputTrackData) {
343 MockCubeb* cubeb = new MockCubeb();
344 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
346 // Graph settings
347 const uint32_t flags = 0;
348 const GraphTime frames = 440;
350 // Non native input settings
351 const AudioInputSource::Id sourceId = 1;
352 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
353 const uint32_t channels = 2;
354 const PrincipalHandle testPrincipal =
355 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
356 const TrackRate rate = 48000;
358 // Setup: Create a NonNativeInputTrack and add it to mGraph.
359 RefPtr<NonNativeInputTrack> track =
360 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
361 mGraph->AddTrack(track);
363 // Main test below:
365 // Make sure we get null data if the track is not started yet.
366 GraphTime current = 0;
367 GraphTime next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(frames);
368 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput.
370 track->ProcessInput(current, next, flags);
372 AudioSegment data;
373 data.AppendSegment(track->GetData<AudioSegment>());
374 EXPECT_TRUE(data.IsNull());
377 // Make sure we get the AudioInputSource's data once we start the track.
379 current = next;
380 next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(2 * frames);
381 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput.
383 auto listener = MakeRefPtr<MockEventListener>();
384 EXPECT_CALL(*listener,
385 AudioStateCallback(
386 sourceId, AudioInputSource::EventListener::State::Started));
387 EXPECT_CALL(*listener,
388 AudioStateCallback(
389 sourceId, AudioInputSource::EventListener::State::Stopped))
390 .Times(2);
392 DispatchFunction([&] {
393 track->StartAudio(MakeRefPtr<AudioInputSource>(
394 std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
395 rate, mGraph->GraphRate()));
397 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
398 EXPECT_TRUE(stream->mHasInput);
399 EXPECT_FALSE(stream->mHasOutput);
400 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
401 EXPECT_EQ(stream->InputChannels(), channels);
402 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
404 // Check audio data.
405 Unused << WaitFor(stream->FramesProcessedEvent());
406 track->ProcessInput(current, next, flags);
408 AudioSegment data;
409 data.AppendSlice(*track->GetData<AudioSegment>(), current, next);
410 EXPECT_FALSE(data.IsNull());
411 for (AudioSegment::ConstChunkIterator iter(data); !iter.IsEnded();
412 iter.Next()) {
413 EXPECT_EQ(iter->mChannelData.Length(), channels);
414 EXPECT_EQ(iter->mPrincipalHandle, testPrincipal);
418 // Stop the track and make sure it produces null data again.
419 current = next;
420 next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(3 * frames);
421 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput.
423 DispatchFunction([&] { track->StopAudio(); });
424 Unused << WaitFor(cubeb->StreamDestroyEvent());
426 track->ProcessInput(current, next, flags);
428 AudioSegment data;
429 data.AppendSlice(*track->GetData<AudioSegment>(), current, next);
430 EXPECT_TRUE(data.IsNull());
433 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
434 track->Destroy();
435 mGraph->RemoveTrackGraphThread(track);
438 TEST_F(TestDeviceInputTrack, NonNativeDeviceChangedCallback) {
439 MockCubeb* cubeb = new MockCubeb();
440 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
442 // Non native input settings
443 const AudioInputSource::Id sourceId = 1;
444 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
445 const uint32_t channels = 2;
446 const PrincipalHandle testPrincipal =
447 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
448 const TrackRate rate = 48000;
450 // Setup: Create a NonNativeInputTrack and add it to mGraph.
451 RefPtr<NonNativeInputTrack> track =
452 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
453 mGraph->AddTrack(track);
455 // Main test below:
457 auto listener = MakeRefPtr<MockEventListener>();
458 EXPECT_CALL(*listener, AudioDeviceChanged(sourceId));
459 EXPECT_CALL(*listener,
460 AudioStateCallback(
461 sourceId, AudioInputSource::EventListener::State::Started));
462 EXPECT_CALL(*listener,
463 AudioStateCallback(
464 sourceId, AudioInputSource::EventListener::State::Stopped))
465 .Times(2);
467 // Launch and start an audio stream.
468 DispatchFunction([&] {
469 track->StartAudio(MakeRefPtr<AudioInputSource>(
470 std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
471 rate, mGraph->GraphRate()));
473 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
474 EXPECT_TRUE(stream->mHasInput);
475 EXPECT_FALSE(stream->mHasOutput);
476 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
477 EXPECT_EQ(stream->InputChannels(), channels);
478 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
480 // Make sure the stream is running.
481 Unused << WaitFor(stream->FramesProcessedEvent());
483 // Fire a device-changed callback.
484 DispatchFunction([&] { stream->ForceDeviceChanged(); });
485 WaitFor(stream->DeviceChangeForcedEvent());
487 // Stop and destroy the stream.
488 DispatchFunction([&] { track->StopAudio(); });
489 Unused << WaitFor(cubeb->StreamDestroyEvent());
491 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
492 track->Destroy();
493 mGraph->RemoveTrackGraphThread(track);
496 TEST_F(TestDeviceInputTrack, NonNativeErrorCallback) {
497 MockCubeb* cubeb = new MockCubeb();
498 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
500 // Non native input settings
501 const AudioInputSource::Id sourceId = 1;
502 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
503 const uint32_t channels = 2;
504 const PrincipalHandle testPrincipal =
505 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
506 const TrackRate rate = 48000;
508 // Setup: Create a NonNativeInputTrack and add it to mGraph.
509 RefPtr<NonNativeInputTrack> track =
510 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
511 mGraph->AddTrack(track);
513 // Main test below:
515 auto listener = MakeRefPtr<MockEventListener>();
516 EXPECT_CALL(*listener,
517 AudioStateCallback(
518 sourceId, AudioInputSource::EventListener::State::Started));
519 EXPECT_CALL(*listener,
520 AudioStateCallback(
521 sourceId, AudioInputSource::EventListener::State::Error));
522 EXPECT_CALL(*listener,
523 AudioStateCallback(
524 sourceId, AudioInputSource::EventListener::State::Stopped))
525 .Times(2);
527 // Launch and start an audio stream.
528 DispatchFunction([&] {
529 track->StartAudio(MakeRefPtr<AudioInputSource>(
530 std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
531 rate, mGraph->GraphRate()));
533 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
534 EXPECT_TRUE(stream->mHasInput);
535 EXPECT_FALSE(stream->mHasOutput);
536 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
537 EXPECT_EQ(stream->InputChannels(), channels);
538 EXPECT_EQ(stream->SampleRate(), static_cast<uint32_t>(rate));
540 // Make sure the stream is running.
541 Unused << WaitFor(stream->FramesProcessedEvent());
543 // Force an error in the MockCubeb.
544 DispatchFunction([&] { stream->ForceError(); });
545 WaitFor(stream->ErrorForcedEvent());
547 // Stop and destroy the stream.
548 DispatchFunction([&] { track->StopAudio(); });
549 Unused << WaitFor(cubeb->StreamDestroyEvent());
551 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
552 track->Destroy();
553 mGraph->RemoveTrackGraphThread(track);