Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qffmpegrenderer.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
5#include <qloggingcategory.h>
6
8
9namespace QFFmpeg {
10
11static Q_LOGGING_CATEGORY(qLcRenderer, "qt.multimedia.ffmpeg.renderer");
12
13Renderer::Renderer(const TimeController &tc, const std::chrono::microseconds &seekPosTimeOffset)
14 : m_timeController(tc),
15 m_lastFrameEnd(tc.currentPosition()),
16 m_lastPosition(m_lastFrameEnd),
17 m_seekPos(tc.currentPosition(-seekPosTimeOffset))
18{
19}
20
22{
23 QMetaObject::invokeMethod(this, [this, tp, trackTime]() {
24 m_timeController.syncSoft(tp, trackTime);
25 scheduleNextStep(true);
26 });
27}
28
30{
31 return m_seekPos;
32}
33
35{
36 return m_lastPosition;
37}
38
40{
41 QMetaObject::invokeMethod(this, [this, rate]() {
42 m_timeController.setPlaybackRate(rate);
45 });
46}
47
49{
50 if (m_isStepForced.testAndSetOrdered(false, true))
51 QMetaObject::invokeMethod(this, [this]() {
52 // maybe set m_forceStepMaxPos
53
54 if (isAtEnd()) {
56 }
57 else {
58 m_explicitNextFrameTime = Clock::now();
60 }
61 });
62}
63
65{
66 return m_isStepForced;
67}
68
70{
71 QMetaObject::invokeMethod(this, [this, tp, trackPos]() {
72 Q_ASSERT(m_loopIndex == 0);
73 Q_ASSERT(m_frames.empty());
74
75 m_loopIndex = 0;
76 m_lastPosition.storeRelease(trackPos);
77 m_seekPos.storeRelease(trackPos);
78
79 m_timeController.sync(tp, trackPos);
80 });
81}
82
87
89{
90 const auto isFrameOutdated = frame.isValid() && frame.absoluteEnd() < seekPosition();
91
92 if (isFrameOutdated) {
93 qCDebug(qLcRenderer) << "frame outdated! absEnd:" << frame.absoluteEnd() << "absPts"
94 << frame.absolutePts() << "seekPos:" << seekPosition();
96 return;
97 }
98
99 m_frames.enqueue(frame);
100
101 if (m_frames.size() == 1)
103}
104
110
112{
113 return !m_frames.empty() && (m_isStepForced || PlaybackEngineObject::canDoNextStep());
114}
115
117{
118 return m_timeController.playbackRate();
119}
120
122{
123 if (m_frames.empty())
124 return 0;
125
126 auto calculateInterval = [](const TimePoint &nextTime) {
127 using namespace std::chrono;
128
129 const auto delay = nextTime - Clock::now();
130 return std::max(0, static_cast<int>(duration_cast<milliseconds>(delay).count()));
131 };
132
133 if (m_explicitNextFrameTime)
134 return calculateInterval(*m_explicitNextFrameTime);
135
136 if (m_frames.front().isValid())
137 return calculateInterval(m_timeController.timeFromPosition(m_frames.front().absolutePts()));
138
139 if (m_lastFrameEnd > 0)
140 return calculateInterval(m_timeController.timeFromPosition(m_lastFrameEnd));
141
142 return 0;
143}
144
146{
147 if (!m_isStepForced.testAndSetOrdered(true, false))
148 return false;
149
150 m_explicitNextFrameTime.reset();
152 return true;
153}
154
156{
157 auto frame = m_frames.front();
158
159 if (setForceStepDone()) {
160 // if (frame.isValid() && frame.pts() > m_forceStepMaxPos) {
161 // scheduleNextStep(false);
162 // return;
163 // }
164 }
165
166 const auto result = renderInternal(frame);
167
168 if (result.done) {
169 m_explicitNextFrameTime.reset();
170 m_frames.dequeue();
171
172 if (frame.isValid()) {
173 m_lastPosition.storeRelease(std::max(frame.absolutePts(), lastPosition()));
174
175 // TODO: get rid of m_lastFrameEnd or m_seekPos
176 m_lastFrameEnd = frame.absoluteEnd();
177 m_seekPos.storeRelaxed(m_lastFrameEnd);
178
179 const auto loopIndex = frame.loopOffset().index;
180 if (m_loopIndex < loopIndex) {
181 m_loopIndex = loopIndex;
182 emit loopChanged(id(), frame.loopOffset().pos, m_loopIndex);
183 }
184
186 } else {
187 m_lastPosition.storeRelease(std::max(m_lastFrameEnd, lastPosition()));
188 }
189 } else {
190 m_explicitNextFrameTime = Clock::now() + result.recheckInterval;
191 }
192
193 setAtEnd(result.done && !frame.isValid());
194
195 scheduleNextStep(false);
196}
197
198std::chrono::microseconds Renderer::frameDelay(const Frame &frame, TimePoint timePoint) const
199{
200 return std::chrono::duration_cast<std::chrono::microseconds>(
201 timePoint - m_timeController.timeFromPosition(frame.absolutePts()));
202}
203
204void Renderer::changeRendererTime(std::chrono::microseconds offset)
205{
206 const auto now = Clock::now();
207 const auto pos = m_timeController.positionFromTime(now);
208 m_timeController.sync(now + offset, pos);
209 emit synchronized(id(), now + offset, pos);
210}
211
212} // namespace QFFmpeg
213
215
216#include "moc_qffmpegrenderer_p.cpp"
bool testAndSetOrdered(T expectedValue, T newValue) noexcept
void storeRelaxed(T newValue) noexcept
void storeRelease(T newValue) noexcept
void scheduleNextStep(bool allowDoImmediatelly=true)
int timerInterval() const override
void onPauseChanged() override
void syncSoft(TimePoint tp, qint64 trackPos)
void doNextStep() override
TimeController::TimePoint TimePoint
virtual RenderingResult renderInternal(Frame frame)=0
void frameProcessed(Frame)
void setInitialPosition(TimePoint tp, qint64 trackPos)
bool canDoNextStep() const override
void setPlaybackRate(float rate)
qint64 seekPosition() const
float playbackRate() const
std::chrono::microseconds frameDelay(const Frame &frame, TimePoint timePoint=Clock::now()) const
Renderer(const TimeController &tc, const std::chrono::microseconds &seekPosTimeOffset={})
virtual void onPlaybackRateChanged()
void changeRendererTime(std::chrono::microseconds offset)
void loopChanged(Id id, qint64 offset, int index)
qint64 lastPosition() const
bool isStepForced() const
PlaybackRate playbackRate() const
void sync(qint64 trackPos=0)
qint64 positionFromTime(TimePoint tp, bool ignorePause=false) const
TimePoint timeFromPosition(qint64 pos, bool ignorePause=false) const
void setPlaybackRate(PlaybackRate playbackRate)
void syncSoft(const TimePoint &tp, qint64 pos, const Clock::duration &fixingTime=std::chrono::seconds(4))
QPoint pos
the position of the widget within its parent widget
Definition qwidget.h:111
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLenum GLenum GLsizei count
GLenum GLuint GLintptr offset
GLuint GLenum * rate
const GLfloat * tc
GLuint64EXT * result
[6]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
long long qint64
Definition qtypes.h:60
QFrame frame
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...