26 if (
const char *testFunction = QTest::currentTestFunction();
27 testFunction && QTestPrivate::checkBlackLists(testFunction, QTest::currentDataTag())) {
28 return std::chrono::seconds(1);
32 return std::chrono::seconds(60);
33 return std::chrono::seconds(5);
38 if (!frameRate || *frameRate <= 0)
42 const qreal margin =
isCI() ? 3.0 : 1.5;
43 return std::chrono::milliseconds(qRound64(1000.0 / *frameRate * margin));
48 return frame.startTime();
53 return info.startTime;
69 connect(
this, &QVideoSink::videoFrameChanged,
this, &FrameGrabber::onFrameReceived);
72void FrameGrabber::onFrameReceived(
const QVideoFrame &frame)
74 m_frameInfos.push_back(makeVideoFrameInfo(frame));
75 if (m_retainVideoFrames)
76 m_videoFrames.push_back(frame);
78 if (!frame.isValid()) {
81 m_streamStarted =
false;
82 }
else if (!m_streamStarted) {
83 m_firstFrameInfo = m_frameInfos.back();
84 if (m_retainFirstVideoFrame)
85 m_firstVideoFrame = frame;
86 m_streamStarted =
true;
97 std::optional<qreal> expectedFrameRate,
98 qint64 noOlderThanTime)
100 return waitAndTake(m_frameInfos, minCount, expectedFrameRate, noOlderThanTime);
105 std::optional<qreal> expectedFrameRate,
106 qint64 noOlderThanTime)
110 m_retainVideoFrames =
true;
111 const auto stopRetaining = qScopeGuard([
this] {
112 m_retainVideoFrames =
false;
113 m_videoFrames.clear();
116 return waitAndTake(m_videoFrames, minCount, expectedFrameRate, noOlderThanTime);
119template <
typename Frame>
121 std::vector<Frame> &frames,
123 std::optional<qreal> expectedFrameRate,
124 qint64 noOlderThanTime)
126 m_frameInfos.clear();
127 m_videoFrames.clear();
129 const auto enoughFramesOrStopped = [
this, &frames, minCount, noOlderThanTime]() ->
bool {
134 QCoreApplication::processEvents(QEventLoop::AllEvents);
136 if (noOlderThanTime > 0) {
138 q20::erase_if(frames, [noOlderThanTime](
const Frame &frame) {
139 return startTimeOf(frame) <= noOlderThanTime;
143 return frames.size() >= minCount;
148 std::chrono::milliseconds timeout = surfaceCaptureTestTimeout();
149 std::optional<
std::chrono::milliseconds> interval = maxFrameInterval(expectedFrameRate);
151 timeout += *interval * qint64(minCount);
153 if (!QTest::qWaitFor(enoughFramesOrStopped, timeout))
159 return std::exchange(frames, {});
164 const auto firstFrameReceivedOrStopped = [
this] {
165 return m_stopped || m_firstFrameInfo;
168 if (!QTest::qWaitFor(firstFrameReceivedOrStopped, surfaceCaptureTestTimeout()))
176 if (!waitForFirstFrame())
179 return m_firstFrameInfo;
185 m_retainFirstVideoFrame,
186 "FrameGrabber::consumeFirstVideoFrame",
187 "setRetainFirstVideoFrame() must be enabled before the stream starts");
189 if (!waitForFirstFrame())
192 return std::exchange(m_firstVideoFrame,
std::nullopt);
197 m_retainFirstVideoFrame = retain;
199 m_firstVideoFrame.reset();
204 m_firstFrameInfo.reset();
205 m_firstVideoFrame.reset();
209 qsizetype frameCount,
210 std::optional<qreal> expectedFrameRate)
212 Q_ASSERT(frameCount > 0);
214 using namespace std::chrono;
217 QElapsedTimer sinceLastFrame;
218 qsizetype framesReceived = 0;
221 connect(
this, &QVideoSink::videoFrameChanged, &context, [&]() {
222 if (framesReceived++ == 0)
224 sinceLastFrame.start();
227 std::optional<milliseconds> maxInterval = maxFrameInterval(expectedFrameRate);
230 auto allFramesReceivedOrStalled = [&]() {
231 if (framesReceived > frameCount)
233 return maxInterval && framesReceived > 0 && sinceLastFrame.hasExpired(maxInterval->count());
239 milliseconds timeout = surfaceCaptureTestTimeout();
241 timeout += *maxInterval * frameCount;
243 timeout = isCI() ? 10s * frameCount : 2s * frameCount;
245 if (!QTest::qWaitFor(allFramesReceivedOrStalled, timeout) || framesReceived <= frameCount)
248 return milliseconds(timer.elapsed() / frameCount);
262 m_streamStarted =
false;
268 qWarning() <<
"Stopping grabber";