Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qsurfacecapturegrabber.cpp
Go to the documentation of this file.
1
// Copyright (C) 2021 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
#
include
"qsurfacecapturegrabber_p.h"
5
6
#
include
<
QtCore
/
qchronotimer
.
h
>
7
#
include
<
QtCore
/
qelapsedtimer
.
h
>
8
#
include
<
QtCore
/
qloggingcategory
.
h
>
9
#
include
<
QtCore
/
qmetaobject
.
h
>
10
#
include
<
QtCore
/
qthread
.
h
>
11
#
include
<
QtCore
/
qtimer
.
h
>
12
13
#
include
<
mutex
>
14
15
QT_BEGIN_NAMESPACE
16
17
Q_STATIC_LOGGING_CATEGORY
(qLcScreenCaptureGrabber,
"qt.multimedia.ffmpeg.surfacecapturegrabber"
);
18
19
class
QSurfaceCaptureGrabber
::
GrabbingProfiler
20
{
21
public
:
22
auto
measure
()
23
{
24
m_elapsedTimer.start();
25
return
qScopeGuard([&]() {
26
const
auto
nsecsElapsed = m_elapsedTimer.nsecsElapsed();
27
++m_number;
28
m_wholeTime += nsecsElapsed;
29
30
#
ifdef
DUMP_SCREEN_CAPTURE_PROFILING
31
qDebug() <<
"screen grabbing time:"
<< nsecsElapsed <<
"avg:"
<< avgTime()
32
<<
"number:"
<< m_number;
33
#
endif
34
});
35
}
36
37
qreal
avgTime
()
const
38
{
39
return
m_number ? m_wholeTime / (m_number * 1000000.) : 0.;
40
}
41
42
qint64
number
()
const
43
{
44
return
m_number;
45
}
46
47
private
:
48
QElapsedTimer m_elapsedTimer;
49
qint64 m_wholeTime = 0;
50
qint64 m_number = 0;
51
};
52
53
struct
QSurfaceCaptureGrabber::GrabbingContext
54
{
55
GrabbingProfiler profiler;
56
QChronoTimer timer;
57
QElapsedTimer elapsedTimer;
58
qint64 lastFrameTime = 0;
59
};
60
61
class
QSurfaceCaptureGrabber::GrabbingThread :
public
QThread
62
{
63
public
:
64
GrabbingThread(QSurfaceCaptureGrabber& grabber)
65
: m_grabber(grabber)
66
{}
67
68
protected
:
69
void
run() override
70
{
71
m_grabber.initializeGrabbingContext();
72
73
if
(!m_grabber.isGrabbingContextInitialized())
74
return
;
75
76
exec();
77
m_grabber.finalizeGrabbingContext();
78
}
79
80
private
:
81
QSurfaceCaptureGrabber& m_grabber;
82
};
83
84
QSurfaceCaptureGrabber
::QSurfaceCaptureGrabber(ThreadPolicy threadPolicy)
85
{
86
setFrameRate(DefaultScreenCaptureFrameRate);
87
88
if
(threadPolicy == CreateGrabbingThread)
89
m_thread = std::make_unique<GrabbingThread>(*
this
);
90
}
91
92
void
QSurfaceCaptureGrabber
::start()
93
{
94
Q_ASSERT(thread()->isCurrentThread());
95
{
96
// Locked: pairs with the read in updateTimerInterval().
97
std
::lock_guard locker{ m_mutex };
98
m_activeRate = m_rate;
99
}
100
101
if
(m_thread)
102
m_thread->start();
103
else
if
(!isGrabbingContextInitialized())
104
initializeGrabbingContext();
105
}
106
107
QSurfaceCaptureGrabber
::~QSurfaceCaptureGrabber() =
default
;
108
109
void
QSurfaceCaptureGrabber
::setFrameRate(std::optional<qreal> rate)
110
{
111
if
(!rate)
112
rate = DefaultScreenCaptureFrameRate;
113
114
// Bound to a minimum
115
if
(*rate < MinScreenCaptureFrameRate)
116
rate = MinScreenCaptureFrameRate;
117
118
// New rate applies to the next stream only.
119
Q_ASSERT(thread()->isCurrentThread());
120
Q_ASSERT(!isGrabbingContextInitialized());
121
if
(m_rate == *rate)
122
return
;
123
124
m_rate = *rate;
125
m_activeRate = *rate;
// Also visible pre-start via activeFrameRate().
126
qCDebug(qLcScreenCaptureGrabber) <<
"Screen capture rate has been changed:"
<< *rate;
127
}
128
129
qreal
QSurfaceCaptureGrabber
::frameRate()
const
130
{
131
Q_ASSERT(thread()->isCurrentThread());
132
return
m_rate;
133
}
134
135
qreal
QSurfaceCaptureGrabber
::activeFrameRate()
const
136
{
137
// Owning thread before start(), grabbing thread after.
138
return
m_activeRate;
139
}
140
141
void
QSurfaceCaptureGrabber
::stop()
142
{
143
if
(m_thread)
144
{
145
m_thread->quit();
146
m_thread->wait();
147
}
148
else
if
(isGrabbingContextInitialized())
149
{
150
finalizeGrabbingContext();
151
}
152
}
153
154
void
QSurfaceCaptureGrabber
::updateError(QPlatformSurfaceCapture::Error error,
155
const
QString &description)
156
{
157
std::optional<QPlatformSurfaceCapture::Error> prevError;
158
{
159
std
::lock_guard locker{ m_mutex };
160
prevError = std::exchange(m_prevError, error);
161
}
162
163
if
(error != QPlatformSurfaceCapture::Error::NoError
164
|| prevError != QPlatformSurfaceCapture::Error::NoError) {
165
Q_EMIT errorUpdated(error, description);
166
}
167
168
updateTimerInterval();
169
}
170
171
void
QSurfaceCaptureGrabber
::updateTimerInterval()
172
{
173
using
namespace
std
::chrono;
174
175
std
::lock_guard locker{ m_mutex };
176
if
(!m_context)
177
return
;
178
179
const
qreal rate = m_prevError && *m_prevError != QPlatformSurfaceCapture::Error::NoError
180
? MinScreenCaptureFrameRate
181
: m_activeRate;
182
const
nanoseconds interval = round<nanoseconds>(nanoseconds(1s) / rate);
183
184
if
(m_timerInterval == interval)
185
return
;
186
187
QChronoTimer *timer = &m_context->timer;
188
if
(timer->thread()->isCurrentThread()) {
189
timer->setInterval(interval);
190
m_timerInterval = interval;
191
}
else
{
192
const
quint64 generation = m_contextGeneration;
193
QMetaObject::invokeMethod(timer, [
this
, interval, generation] {
194
setTimerInterval(interval, generation);
195
}, Qt::QueuedConnection);
196
}
197
}
198
199
void
QSurfaceCaptureGrabber
::setTimerInterval(
std
::chrono::nanoseconds interval, quint64 generation)
200
{
201
std
::lock_guard locker{ m_mutex };
202
if
(!m_context || generation != m_contextGeneration || m_timerInterval == interval)
203
return
;
204
205
m_context->timer.setInterval(interval);
206
m_timerInterval = interval;
207
}
208
209
void
QSurfaceCaptureGrabber
::initializeGrabbingContext()
210
{
211
Q_ASSERT(!isGrabbingContextInitialized());
212
qCDebug(qLcScreenCaptureGrabber) <<
"screen capture started"
;
213
214
{
215
std
::lock_guard locker{ m_mutex };
216
m_context = std::make_unique<GrabbingContext>();
217
++m_contextGeneration;
218
m_timerInterval = std::chrono::nanoseconds{ 0 };
219
}
220
m_context->timer.setTimerType(Qt::PreciseTimer);
221
updateTimerInterval();
222
223
m_context->elapsedTimer.start();
224
225
auto
doGrab = [
this
]() {
226
auto
measure = m_context->profiler.measure();
227
228
auto
frame = grabFrame();
229
230
if
(frame.isValid()) {
231
frame.setStartTime(m_context->lastFrameTime);
232
frame.setEndTime(m_context->elapsedTimer.nsecsElapsed() / 1000);
233
m_context->lastFrameTime = frame.endTime();
234
235
updateError(QPlatformSurfaceCapture::Error::NoError);
236
237
Q_EMIT frameGrabbed(frame);
238
}
239
};
240
241
doGrab();
242
243
m_context->timer.callOnTimeout(&m_context->timer, doGrab);
244
m_context->timer.start();
245
}
246
247
void
QSurfaceCaptureGrabber
::finalizeGrabbingContext()
248
{
249
Q_ASSERT(isGrabbingContextInitialized());
250
qCDebug(qLcScreenCaptureGrabber)
251
<<
"end screen capture thread; avg grabbing time:"
<< m_context->profiler.avgTime()
252
<<
"ms, grabbings number:"
<< m_context->profiler.number();
253
{
254
// Bump the generation first so queued interval updates posted from
255
// another thread are dropped instead of touching a dead timer.
256
std
::lock_guard locker{ m_mutex };
257
++m_contextGeneration;
258
m_timerInterval = std::chrono::nanoseconds{ 0 };
259
m_context.reset();
260
}
261
}
262
263
bool
QSurfaceCaptureGrabber
::isGrabbingContextInitialized()
const
264
{
265
std
::lock_guard locker{ m_mutex };
266
return
m_context !=
nullptr
;
267
}
268
269
void
QSurfaceCaptureGrabber
::injectContextToGrabbingThread(QObject *context)
270
{
271
Q_ASSERT(m_thread);
272
Q_ASSERT(QThread::currentThread() == context->thread());
273
context->moveToThread(m_thread.get());
274
}
275
276
QT_END_NAMESPACE
277
278
#
include
"moc_qsurfacecapturegrabber_p.cpp"
QSurfaceCaptureGrabber::GrabbingProfiler
Definition
qsurfacecapturegrabber.cpp:20
QSurfaceCaptureGrabber::GrabbingProfiler::number
qint64 number() const
Definition
qsurfacecapturegrabber.cpp:42
QSurfaceCaptureGrabber::GrabbingProfiler::avgTime
qreal avgTime() const
Definition
qsurfacecapturegrabber.cpp:37
QSurfaceCaptureGrabber::GrabbingProfiler::measure
auto measure()
Definition
qsurfacecapturegrabber.cpp:22
std
[33]
Definition
src_corelib_tools_qhash.cpp:421
Q_STATIC_LOGGING_CATEGORY
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
qtmultimedia
src
multimedia
qsurfacecapturegrabber.cpp
Generated on
for Qt by
1.18.0