8#include <QtTest/qtest.h>
9#include <QtTest/private/qtestblacklist_p.h>
10#include <QtMultimedia/qvideoframe.h>
11#include <QtCore/q20vector.h>
12#include <QtCore/qcoreapplication.h>
13#include <QtCore/qelapsedtimer.h>
14#include <QtCore/qscopeguard.h>
28 if (
const char *testFunction = QTest::currentTestFunction();
29 testFunction && QTestPrivate::checkBlackLists(testFunction, QTest::currentDataTag())) {
30 return std::chrono::seconds(1);
34 return std::chrono::seconds(60);
35 return std::chrono::seconds(5);
40 if (!frameRate || *frameRate <= 0)
44 const qreal margin =
isCI() ? 3.0 : 1.5;
45 return std::chrono::milliseconds(qRound64(1000.0 / *frameRate * margin));
52 return u"%1us"_s.arg(time->count());
60 return std::chrono::microseconds(time);
65 return toFrameTime(frame.startTime());
70 return info.startTime;
75 return VideoFrameInfo{
79 toFrameTime(frame.startTime()),
80 toFrameTime(frame.endTime()),
81 frame.streamFrameRate(),
87 connect(
this, &QVideoSink::videoFrameChanged,
this, &FrameGrabber::onFrameReceived);
90void FrameGrabber::onFrameReceived(
const QVideoFrame &frame)
92 m_frameInfos.push_back(makeVideoFrameInfo(frame));
93 if (m_retainVideoFrames)
94 m_videoFrames.push_back(frame);
96 if (!frame.isValid()) {
99 m_streamStarted =
false;
100 }
else if (!m_streamStarted) {
101 m_firstFrameInfo = m_frameInfos.back();
102 if (m_retainFirstVideoFrame)
103 m_firstVideoFrame = frame;
104 m_streamStarted =
true;
115 std::optional<qreal> expectedFrameRate,
116 std::optional<
std::chrono::microseconds> noOlderThanTime)
118 return waitAndTake(m_frameInfos, minCount, expectedFrameRate, noOlderThanTime);
123 std::optional<qreal> expectedFrameRate,
124 std::optional<
std::chrono::microseconds> noOlderThanTime)
128 m_retainVideoFrames =
true;
129 const auto stopRetaining = qScopeGuard([
this] {
130 m_retainVideoFrames =
false;
131 m_videoFrames.clear();
134 return waitAndTake(m_videoFrames, minCount, expectedFrameRate, noOlderThanTime);
137template <
typename Frame>
139 std::vector<Frame> &frames,
141 std::optional<qreal> expectedFrameRate,
142 std::optional<
std::chrono::microseconds> noOlderThanTime)
144 m_frameInfos.clear();
145 m_videoFrames.clear();
147 const auto enoughFramesOrStopped = [
this, &frames, minCount, noOlderThanTime]() ->
bool {
152 QCoreApplication::processEvents(QEventLoop::AllEvents);
154 if (noOlderThanTime) {
156 q20::erase_if(frames, [noOlderThanTime](
const Frame &frame) {
157 return startTimeOf(frame) <= noOlderThanTime;
161 return frames.size() >= minCount;
166 std::chrono::milliseconds timeout = surfaceCaptureTestTimeout();
167 std::optional<
std::chrono::milliseconds> interval = maxFrameInterval(expectedFrameRate);
169 timeout += *interval * qint64(minCount);
171 if (!QTest::qWaitFor(enoughFramesOrStopped, timeout))
177 return std::exchange(frames, {});
182 const auto firstFrameReceivedOrStopped = [
this] {
183 return m_stopped || m_firstFrameInfo;
186 if (!QTest::qWaitFor(firstFrameReceivedOrStopped, surfaceCaptureTestTimeout()))
194 if (!waitForFirstFrame())
197 return m_firstFrameInfo;
203 m_retainFirstVideoFrame,
204 "FrameGrabber::consumeFirstVideoFrame",
205 "setRetainFirstVideoFrame() must be enabled before the stream starts");
207 if (!waitForFirstFrame())
210 return std::exchange(m_firstVideoFrame,
std::nullopt);
215 m_retainFirstVideoFrame = retain;
217 m_firstVideoFrame.reset();
222 m_firstFrameInfo.reset();
223 m_firstVideoFrame.reset();
227 qsizetype frameCount,
228 std::optional<qreal> expectedFrameRate)
230 Q_ASSERT(frameCount > 0);
232 using namespace std::chrono;
235 QElapsedTimer sinceLastFrame;
236 qsizetype framesReceived = 0;
239 connect(
this, &QVideoSink::videoFrameChanged, &context, [&]() {
240 if (framesReceived++ == 0)
242 sinceLastFrame.start();
245 std::optional<milliseconds> maxInterval = maxFrameInterval(expectedFrameRate);
248 auto allFramesReceivedOrStalled = [&]() {
249 if (framesReceived > frameCount)
251 return maxInterval && framesReceived > 0 && sinceLastFrame.hasExpired(maxInterval->count());
257 milliseconds timeout = surfaceCaptureTestTimeout();
259 timeout += *maxInterval * frameCount;
261 timeout = isCI() ? 10s * frameCount : 2s * frameCount;
263 if (!QTest::qWaitFor(allFramesReceivedOrStalled, timeout) || framesReceived <= frameCount)
266 return milliseconds(timer.elapsed() / frameCount);
280 m_streamStarted =
false;
293#include "moc_framegrabber_p.cpp"