Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / gtest / TestDeviceInputTrack.cpp
blob4e369104476461a10392706870b2ac4501f2af58
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 "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, 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<ControlMessage>));
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(MediaTrackGraphImpl* aGraph) override {
122 MOZ_ASSERT(aGraph->OnGraphThread());
123 return mChannelCount;
125 bool IsVoiceInput(MediaTrackGraphImpl* aGraph) const override {
126 return mIsVoice;
128 void DeviceChanged(MediaTrackGraphImpl* aGraph) override { /* Ignored */
130 void Disconnect(MediaTrackGraphImpl* 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;
249 const uint32_t bufferingMs = StaticPrefs::media_clockdrift_buffering();
251 // Setup: Create a NonNativeInputTrack and add it to mGraph.
252 RefPtr<NonNativeInputTrack> track =
253 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
254 mGraph->AddTrack(track);
256 // Main test below:
258 // Make sure the NonNativeInputTrack can start and stop its audio correctly.
260 auto listener = MakeRefPtr<MockEventListener>();
261 EXPECT_CALL(*listener,
262 AudioStateCallback(
263 sourceId, AudioInputSource::EventListener::State::Started));
264 EXPECT_CALL(*listener,
265 AudioStateCallback(
266 sourceId, AudioInputSource::EventListener::State::Stopped));
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(), bufferingMs));
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->InputSampleRate(), 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));
313 DispatchFunction([&] {
314 track->StartAudio(MakeRefPtr<AudioInputSource>(
315 std::move(listener), sourceId, deviceId, channels, true,
316 testPrincipal, rate, mGraph->GraphRate(), bufferingMs));
319 // Wait for stream creation.
320 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
321 EXPECT_TRUE(stream->mHasInput);
322 EXPECT_FALSE(stream->mHasOutput);
323 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
324 EXPECT_EQ(stream->InputChannels(), channels);
325 EXPECT_EQ(stream->InputSampleRate(), static_cast<uint32_t>(rate));
327 // Wait for stream callbacks.
328 Unused << WaitFor(stream->FramesProcessedEvent());
330 DispatchFunction([&] { track->StopAudio(); });
332 // Wait for stream destroy.
333 Unused << WaitFor(cubeb->StreamDestroyEvent());
336 // Tear down: Destroy the NativeInputTrack and remove it from mGraph.
337 track->Destroy();
338 mGraph->RemoveTrackGraphThread(track);
341 TEST_F(TestDeviceInputTrack, NonNativeInputTrackData) {
342 MockCubeb* cubeb = new MockCubeb();
343 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
345 // Graph settings
346 const uint32_t flags = 0;
347 const GraphTime frames = 440;
349 // Non native input settings
350 const AudioInputSource::Id sourceId = 1;
351 const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
352 const uint32_t channels = 2;
353 const PrincipalHandle testPrincipal =
354 MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
355 const TrackRate rate = 48000;
356 const uint32_t bufferingMs =
357 static_cast<uint32_t>(StaticPrefs::media_clockdrift_buffering());
359 // Setup: Create a NonNativeInputTrack and add it to mGraph.
360 RefPtr<NonNativeInputTrack> track =
361 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
362 mGraph->AddTrack(track);
364 // Main test below:
366 // Make sure we get null data if the track is not started yet.
367 GraphTime current = 0;
368 GraphTime next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(frames);
369 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput.
371 track->ProcessInput(current, next, flags);
373 AudioSegment data;
374 data.AppendSegment(track->GetData<AudioSegment>());
375 EXPECT_TRUE(data.IsNull());
378 // Make sure we get the AudioInputSource's data once we start the track.
380 current = next;
381 next = MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(2 * frames);
382 ASSERT_NE(current, next); // Make sure we have data produced in ProcessInput.
384 auto listener = MakeRefPtr<MockEventListener>();
385 EXPECT_CALL(*listener,
386 AudioStateCallback(
387 sourceId, AudioInputSource::EventListener::State::Started));
388 EXPECT_CALL(*listener,
389 AudioStateCallback(
390 sourceId, AudioInputSource::EventListener::State::Stopped));
392 DispatchFunction([&] {
393 track->StartAudio(MakeRefPtr<AudioInputSource>(
394 std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
395 rate, mGraph->GraphRate(), bufferingMs));
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->InputSampleRate(), 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;
449 const uint32_t bufferingMs = StaticPrefs::media_clockdrift_buffering();
451 // Setup: Create a NonNativeInputTrack and add it to mGraph.
452 RefPtr<NonNativeInputTrack> track =
453 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
454 mGraph->AddTrack(track);
456 // Main test below:
458 auto listener = MakeRefPtr<MockEventListener>();
459 EXPECT_CALL(*listener, AudioDeviceChanged(sourceId));
460 EXPECT_CALL(*listener,
461 AudioStateCallback(
462 sourceId, AudioInputSource::EventListener::State::Started));
463 EXPECT_CALL(*listener,
464 AudioStateCallback(
465 sourceId, AudioInputSource::EventListener::State::Stopped));
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(), bufferingMs));
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->InputSampleRate(), 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;
507 const uint32_t bufferingMs = StaticPrefs::media_clockdrift_buffering();
509 // Setup: Create a NonNativeInputTrack and add it to mGraph.
510 RefPtr<NonNativeInputTrack> track =
511 new NonNativeInputTrack(mGraph->GraphRate(), deviceId, testPrincipal);
512 mGraph->AddTrack(track);
514 // Main test below:
516 auto listener = MakeRefPtr<MockEventListener>();
517 EXPECT_CALL(*listener,
518 AudioStateCallback(
519 sourceId, AudioInputSource::EventListener::State::Started));
520 EXPECT_CALL(*listener,
521 AudioStateCallback(
522 sourceId, AudioInputSource::EventListener::State::Error));
524 // Launch and start an audio stream.
525 DispatchFunction([&] {
526 track->StartAudio(MakeRefPtr<AudioInputSource>(
527 std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
528 rate, mGraph->GraphRate(), bufferingMs));
530 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
531 EXPECT_TRUE(stream->mHasInput);
532 EXPECT_FALSE(stream->mHasOutput);
533 EXPECT_EQ(stream->GetInputDeviceID(), deviceId);
534 EXPECT_EQ(stream->InputChannels(), channels);
535 EXPECT_EQ(stream->InputSampleRate(), static_cast<uint32_t>(rate));
537 // Make sure the stream is running.
538 Unused << WaitFor(stream->FramesProcessedEvent());
540 // Force an error in the MockCubeb.
541 DispatchFunction([&] { stream->ForceError(); });
542 WaitFor(stream->ErrorForcedEvent());
544 // Make sure the stream has been stopped by the error-state's backgroud thread
545 // task, to avoid getting a stopped state callback by `track->StopAudio`
546 // below.
547 WaitFor(stream->ErrorStoppedEvent());
549 // Stop and destroy the stream.
550 DispatchFunction([&] { track->StopAudio(); });
551 Unused << WaitFor(cubeb->StreamDestroyEvent());
553 // Tear down: Destroy the NonNativeInputTrack and remove it from mGraph.
554 track->Destroy();
555 mGraph->RemoveTrackGraphThread(track);