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"
18 #include "MockCubeb.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
,
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
,
39 GraphDriver
* driver
= mCurrentDriver
;
41 aMixer
->StartMixing();
43 driver
->AsAudioCallbackDriver()->OutputChannelCount(),
44 aStateComputedTime
- mStateComputedTime
, mSampleRate
);
45 aMixer
->FinishMixing();
47 if (aStateComputedTime
!= mStateComputedTime
) {
48 mFramesIteratedEvent
.Notify(aStateComputedTime
- mStateComputedTime
);
51 mStateComputedTime
= aStateComputedTime
;
52 if (!mKeepProcessing
) {
53 return IterationResult::CreateStop(
54 NS_NewRunnableFunction(__func__
, [] {}));
56 GraphDriver
* next
= mNextDriver
.exchange(nullptr);
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
; }
69 bool InDriverIteration(const GraphDriver
* aDriver
) const override
{
70 return aDriver
->OnThread();
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
;
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
);
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();
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
) {
153 audioStart
= Some(graph
->StateComputedTime());
154 alreadyBuffered
= Some(aAlreadyBuffered
);
156 EXPECT_NEAR(inputFrameCount
,
157 static_cast<int64_t>(graph
->StateComputedTime() -
158 *audioStart
+ *alreadyBuffered
),
160 << "Input should be behind state time, due to the delayed start. "
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);
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
;
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