Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / gtest / TestAudioCallbackDriver.cpp
blobafde4781509b9a9ba022f1acc7367fab1fba94d2
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 "CubebUtils.h"
7 #include "GraphDriver.h"
9 #include "gmock/gmock.h"
10 #include "gtest/gtest-printers.h"
11 #include "gtest/gtest.h"
13 #include "MediaTrackGraphImpl.h"
14 #include "mozilla/Attributes.h"
15 #include "mozilla/UniquePtr.h"
16 #include "nsTArray.h"
18 #include "MockCubeb.h"
19 #include "WaitFor.h"
21 using namespace mozilla;
22 using IterationResult = GraphInterface::IterationResult;
23 using ::testing::NiceMock;
25 class MockGraphInterface : public GraphInterface {
26 NS_DECL_THREADSAFE_ISUPPORTS
27 explicit MockGraphInterface(TrackRate aSampleRate)
28 : mSampleRate(aSampleRate) {}
29 MOCK_METHOD4(NotifyOutputData,
30 void(AudioDataValue*, size_t, TrackRate, uint32_t));
31 MOCK_METHOD0(NotifyInputStopped, void());
32 MOCK_METHOD5(NotifyInputData, void(const AudioDataValue*, size_t, TrackRate,
33 uint32_t, uint32_t));
34 MOCK_METHOD0(DeviceChanged, void());
35 /* OneIteration cannot be mocked because IterationResult is non-memmovable and
36 * cannot be passed as a parameter, which GMock does internally. */
37 IterationResult OneIteration(GraphTime aStateComputedTime, GraphTime,
38 AudioMixer* aMixer) {
39 GraphDriver* driver = mCurrentDriver;
40 if (aMixer) {
41 aMixer->StartMixing();
42 aMixer->Mix(nullptr,
43 driver->AsAudioCallbackDriver()->OutputChannelCount(),
44 aStateComputedTime - mStateComputedTime, mSampleRate);
45 aMixer->FinishMixing();
47 if (aStateComputedTime != mStateComputedTime) {
48 mFramesIteratedEvent.Notify(aStateComputedTime - mStateComputedTime);
49 ++mIterationCount;
51 mStateComputedTime = aStateComputedTime;
52 if (!mKeepProcessing) {
53 return IterationResult::CreateStop(
54 NS_NewRunnableFunction(__func__, [] {}));
56 GraphDriver* next = mNextDriver.exchange(nullptr);
57 if (next) {
58 return IterationResult::CreateSwitchDriver(
59 next, NS_NewRunnableFunction(__func__, [] {}));
61 if (mEnsureNextIteration) {
62 driver->EnsureNextIteration();
64 return IterationResult::CreateStillProcessing();
66 void SetEnsureNextIteration(bool aEnsure) { mEnsureNextIteration = aEnsure; }
68 #ifdef DEBUG
69 bool InDriverIteration(const GraphDriver* aDriver) const override {
70 return aDriver->OnThread();
72 #endif
74 size_t IterationCount() const { return mIterationCount; }
76 GraphTime StateComputedTime() const { return mStateComputedTime; }
77 void SetCurrentDriver(GraphDriver* aDriver) { mCurrentDriver = aDriver; }
79 void StopIterating() { mKeepProcessing = false; }
81 void SwitchTo(GraphDriver* aDriver) { mNextDriver = aDriver; }
82 const TrackRate mSampleRate;
84 MediaEventSource<uint32_t>& FramesIteratedEvent() {
85 return mFramesIteratedEvent;
88 protected:
89 Atomic<size_t> mIterationCount{0};
90 Atomic<GraphTime> mStateComputedTime{0};
91 Atomic<GraphDriver*> mCurrentDriver{nullptr};
92 Atomic<bool> mEnsureNextIteration{false};
93 Atomic<bool> mKeepProcessing{true};
94 Atomic<GraphDriver*> mNextDriver{nullptr};
95 MediaEventProducer<uint32_t> mFramesIteratedEvent;
96 virtual ~MockGraphInterface() = default;
99 NS_IMPL_ISUPPORTS0(MockGraphInterface)
101 TEST(TestAudioCallbackDriver, StartStop)
102 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
103 const TrackRate rate = 44100;
104 MockCubeb* cubeb = new MockCubeb();
105 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
107 RefPtr<AudioCallbackDriver> driver;
108 auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate);
109 EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
110 ON_CALL(*graph, NotifyOutputData)
111 .WillByDefault([&](AudioDataValue*, size_t, TrackRate, uint32_t) {});
113 driver = MakeRefPtr<AudioCallbackDriver>(graph, nullptr, rate, 2, 0, nullptr,
114 nullptr, AudioInputType::Unknown);
115 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
116 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
118 graph->SetCurrentDriver(driver);
119 driver->Start();
120 // Allow some time to "play" audio.
121 std::this_thread::sleep_for(std::chrono::milliseconds(200));
122 EXPECT_TRUE(driver->ThreadRunning()) << "Verify thread is running";
123 EXPECT_TRUE(driver->IsStarted()) << "Verify thread is started";
125 // This will block untill all events have been executed.
126 MOZ_KnownLive(driver)->Shutdown();
127 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
128 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
131 void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
132 std::cerr << "TestSlowStart with rate " << aRate << std::endl;
134 MockCubeb* cubeb = new MockCubeb();
135 cubeb->SetStreamStartFreezeEnabled(true);
136 auto unforcer = WaitFor(cubeb->ForceAudioThread()).unwrap();
137 Unused << unforcer;
138 CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
140 RefPtr<AudioCallbackDriver> driver;
141 auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(aRate);
142 EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
144 Maybe<int64_t> audioStart;
145 Maybe<uint32_t> alreadyBuffered;
146 int64_t inputFrameCount = 0;
147 int64_t outputFrameCount = 0;
148 int64_t processedFrameCount = 0;
149 ON_CALL(*graph, NotifyInputData)
150 .WillByDefault([&](const AudioDataValue*, size_t aFrames, TrackRate,
151 uint32_t, uint32_t aAlreadyBuffered) {
152 if (!audioStart) {
153 audioStart = Some(graph->StateComputedTime());
154 alreadyBuffered = Some(aAlreadyBuffered);
156 EXPECT_NEAR(inputFrameCount,
157 static_cast<int64_t>(graph->StateComputedTime() -
158 *audioStart + *alreadyBuffered),
159 WEBAUDIO_BLOCK_SIZE)
160 << "Input should be behind state time, due to the delayed start. "
161 "stateComputedTime="
162 << graph->StateComputedTime() << ", audioStartTime=" << *audioStart
163 << ", alreadyBuffered=" << *alreadyBuffered;
164 inputFrameCount += aFrames;
166 ON_CALL(*graph, NotifyOutputData)
167 .WillByDefault([&](AudioDataValue*, size_t aFrames, TrackRate aRate,
168 uint32_t) { outputFrameCount += aFrames; });
170 driver = MakeRefPtr<AudioCallbackDriver>(graph, nullptr, aRate, 2, 2, nullptr,
171 (void*)1, AudioInputType::Voice);
172 EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
173 EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
175 graph->SetCurrentDriver(driver);
176 graph->SetEnsureNextIteration(true);
178 driver->Start();
179 RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
180 cubeb->SetStreamStartFreezeEnabled(false);
182 const size_t fallbackIterations = 3;
183 WaitUntil(graph->FramesIteratedEvent(), [&](uint32_t aFrames) {
184 const GraphTime tenMillis = aRate / 100;
185 // An iteration is always rounded upwards to the next full block.
186 const GraphTime tenMillisIteration =
187 MediaTrackGraphImpl::RoundUpToEndOfAudioBlock(tenMillis);
188 // The iteration may be smaller because up to an extra block may have been
189 // processed and buffered.
190 const GraphTime tenMillisMinIteration =
191 tenMillisIteration - WEBAUDIO_BLOCK_SIZE;
192 // An iteration must be at least one audio block.
193 const GraphTime minIteration =
194 std::max<GraphTime>(WEBAUDIO_BLOCK_SIZE, tenMillisMinIteration);
195 EXPECT_GE(aFrames, minIteration)
196 << "Fallback driver iteration >= 10ms, modulo an audio block";
197 EXPECT_LT(aFrames, static_cast<size_t>(aRate))
198 << "Fallback driver iteration <1s (sanity)";
199 return graph->IterationCount() >= fallbackIterations;
201 stream->Thaw();
203 // Wait for at least 100ms of audio data.
204 WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
205 processedFrameCount += aFrames;
206 return processedFrameCount >= aRate / 10;
209 // This will block untill all events have been executed.
210 MOZ_KnownLive(driver)->Shutdown();
212 EXPECT_EQ(inputFrameCount, outputFrameCount);
213 EXPECT_NEAR(graph->StateComputedTime() - *audioStart,
214 inputFrameCount + *alreadyBuffered, WEBAUDIO_BLOCK_SIZE)
215 << "Graph progresses while audio driver runs. stateComputedTime="
216 << graph->StateComputedTime() << ", inputFrameCount=" << inputFrameCount;
219 TEST(TestAudioCallbackDriver, SlowStart)
220 MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
221 TestSlowStart(1000); // 10ms = 10 <<< 128 samples
222 TestSlowStart(8000); // 10ms = 80 < 128 samples
223 TestSlowStart(44100); // 10ms = 441 > 128 samples