Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qffmpegframerateadapter.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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
5
6#include <QtCore/qmath.h>
7
9
10namespace QFFmpeg {
11
12void FrameRateAdapter::setRates(std::optional<double> sourceRate, double targetRate)
13{
14 if (targetRate <= 0 || (sourceRate && qFuzzyCompare(*sourceRate, targetRate))) {
15 m_slotDuration = 0;
16 m_slotMatchEpsilon = 0;
17 return;
18 }
19
20 m_slotDuration = static_cast<qint64>(VideoFrameTimeBase / targetRate);
21
22 if (sourceRate) {
23 // Fixed-rate source, expect steady period
24 m_sourceDuration = static_cast<qint64>(qRound(VideoFrameTimeBase / *sourceRate));
25
26 // Jitter tolerance: 1% of source period. Frames arriving more than this
27 // past a slot boundary belong to the next slot and should not be treated
28 // as landing on the current slot boundary.
29 constexpr double JitterFraction = 0.01;
30 m_slotMatchEpsilon = qRound(static_cast<double>(*m_sourceDuration) * JitterFraction);
31 } else {
32 // Variable-rate source
33 m_sourceDuration = std::nullopt;
34
35 // No source rate info: use a small absolute tolerance (1ms)
36 m_slotMatchEpsilon = 1000;
37 }
38}
39
40std::vector<FrameInfo> FrameRateAdapter::adapt(const QVideoFrame &frame, bool adjustTimeBase)
41{
42 std::vector<FrameInfo> result;
43
44 // Can't adapt without target frame rate
45 if (!isActive()) {
46 result.push_back({ frame, adjustTimeBase, std::nullopt });
47 return result;
48 }
49
50 const qint64 frameStart = frame.startTime();
51
52 // Can't adapt without a valid start time
53 if (frameStart == -1) {
54 result.push_back({ frame, adjustTimeBase, std::nullopt });
55 return result;
56 }
57
58 if (m_nextSlotTime < 0)
59 m_nextSlotTime = frameStart;
60
61 qint64 frameEnd = frame.endTime();
62 if (frameEnd == -1 && m_sourceDuration)
63 frameEnd = frameStart + *m_sourceDuration;
64
65 // Repeat previous frame to fill skipped slots
66 while (frameStart > m_nextSlotTime + m_slotMatchEpsilon)
67 result.push_back(emitPendingFrame());
68
69 m_pendingFrame = { frame, adjustTimeBase, m_nextSlotTime };
70 // If a source is variable-rate and the frame has no endTime, prepare frame to be flushed on EOS
71 m_pendingNeedsFlush = !m_sourceDuration && frameEnd == -1;
72
73 // Emit for current slot if frame arrival aligns with slot start
74 Q_ASSERT(frameStart <= m_nextSlotTime + m_slotMatchEpsilon);
75 if (frameStart >= m_nextSlotTime)
76 result.push_back(emitPendingFrame());
77
78 // Copy frame over all encoding slots it's presented for
79 if (frameEnd > frameStart) {
80 while (frameEnd > (m_nextSlotTime + m_slotMatchEpsilon))
81 result.push_back(emitPendingFrame());
82 }
83
84 return result;
85}
86
88{
89 if (m_pendingNeedsFlush)
90 return emitPendingFrame(false);
91
92 m_pendingFrame = std::nullopt;
93 return std::nullopt;
94}
95
97{
98 m_nextSlotTime = -1;
99 m_pendingFrame = std::nullopt;
100 m_pendingNeedsFlush = false;
101}
102
103FrameInfo FrameRateAdapter::emitPendingFrame(bool keepPending)
104{
105 Q_ASSERT(m_pendingFrame);
106 m_pendingNeedsFlush = false;
107 m_nextSlotTime += m_slotDuration;
108 if (keepPending) {
109 auto pending = *m_pendingFrame;
110 m_pendingFrame->adjustTimeBase = false;
111 m_pendingFrame->overriddenPts = m_nextSlotTime;
112 return pending;
113 }
114
115 auto pending = *std::move(m_pendingFrame);
116 m_pendingFrame = std::nullopt;
117 return pending;
118}
119
120} // namespace QFFmpeg
121
122QT_END_NAMESPACE
std::optional< FrameInfo > flush()
void setRates(std::optional< double > sourceRate, double targetRate)
std::vector< FrameInfo > adapt(const QVideoFrame &frame, bool adjustTimeBase=false)
QT_MANGLE_NAMESPACE(QMacScreenCaptureStreamDelegate) QMacScreenCaptureStreamDelegate
Combined button and popup list for selecting options.