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
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
6#include <QtCore/qloggingcategory.h>
7
9
10namespace QFFmpeg {
11
12Q_STATIC_LOGGING_CATEGORY(qLcRenderer, "qt.multimedia.ffmpeg.renderer");
13
14Renderer::Renderer(const PlaybackEngineObjectID &id, const TimeController &tc)
16{
17}
18
20{
21 return TrackPosition(m_sessionCtx.seekPos);
22}
23
25{
26 return TrackPosition(m_sessionCtx.lastPosition);
27}
28
29void Renderer::setPlaybackRate(float rate)
30{
31 invokePriorityMethod([this, rate]() {
32 m_sessionCtx.timeController.setPlaybackRate(rate);
34 scheduleNextStep();
35 });
36}
37
39{
40 if (m_isStepForced.testAndSetOrdered(false, true))
41 invokePriorityMethod([this]() {
42 // maybe set m_forceStepMaxPos
43
44 if (isAtEnd()) {
46 }
47 else {
48 m_sessionCtx.explicitNextFrameTime = SteadyClock::now();
49 scheduleNextStep();
50 }
51 });
52}
53
55{
56 return m_isStepForced;
57}
58
59void Renderer::setTimeController(const TimeController &tc)
60{
61 Q_ASSERT(tc.isStarted());
62 invokePriorityMethod([this, tc]() {
63 m_sessionCtx.timeController = tc;
64 scheduleNextStep();
65 });
66}
67
68void Renderer::seek(quint64 sessionId, const TimeController &tc, const LoopOffset &offset)
69{
70 updateSession(sessionId, [this, tc, offset]() {
71 m_sessionCtx = { tc, offset.loopIndex };
72 // don't clean m_isStepForced, otherwise a single frame might not be forced on pause
73
75 });
76}
77
78void Renderer::onFinalFrameReceived(PlaybackEngineObjectID sourceID)
79{
80 if (checkSessionID(sourceID.sessionID))
81 render({});
82}
83
84void Renderer::render(Frame frame)
85{
86 if (frame.isValid() && !checkSessionID(frame.sourceID().sessionID)) {
87 qCDebug(qLcRenderer) << "Frame session outdated. Source id:" << frame.sourceID() << "current id:" << id();
88 // else don't need to report
89 return;
90 }
91
92 const bool frameOutdated = frame.isValid() && frame.absoluteEnd() < seekPosition();
93
94 if (frameOutdated) {
95 qCDebug(qLcRenderer) << "frame outdated! absEnd:" << frame.absoluteEnd().get() << "absPts"
96 << frame.absolutePts().get() << "seekPos:" << seekPosition().get();
97
98 emit frameProcessed(std::move(frame));
99 return;
100 }
101
102 m_sessionCtx.frames.enqueue(std::move(frame));
103
104 if (m_sessionCtx.frames.size() == 1)
105 scheduleNextStep();
106}
107
109{
110 m_sessionCtx.timeController.setPaused(isPaused());
111 PlaybackEngineObject::onPauseChanged();
112}
113
115{
116 if (m_sessionCtx.frames.empty())
117 return false;
118 // do the step even if the TC is not started;
119 // may be changed if the case is found.
120 if (m_isStepForced)
121 return true;
122 if (!m_sessionCtx.timeController.isStarted())
123 return false;
124 return PlaybackEngineObject::canDoNextStep();
125}
126
127float Renderer::playbackRate() const
128{
129 return m_sessionCtx.timeController.playbackRate();
130}
131
133{
134 using namespace std::chrono_literals;
135
136 if (m_sessionCtx.frames.empty())
137 return PlaybackEngineObject::nextTimePoint();
138
139 if (m_sessionCtx.explicitNextFrameTime)
140 return *m_sessionCtx.explicitNextFrameTime;
141
142 if (m_sessionCtx.frames.front().isValid())
143 return m_sessionCtx.timeController.timeFromPosition(
144 m_sessionCtx.frames.front().absolutePts());
145
146 if (m_sessionCtx.lastFrameEnd > TrackPosition(0))
147 return m_sessionCtx.timeController.timeFromPosition(m_sessionCtx.lastFrameEnd);
148
149 return PlaybackEngineObject::nextTimePoint();
150}
151
153{
154 if (!m_isStepForced.testAndSetOrdered(true, false))
155 return false;
156
157 m_sessionCtx.explicitNextFrameTime.reset();
158 emit forceStepDone();
159 return true;
160}
161
163{
164 Frame frame = m_sessionCtx.frames.front();
165
166 if (setForceStepDone()) {
167 // if (frame.isValid() && frame.pts() > m_forceStepMaxPos) {
168 // scheduleNextStep();
169 // return;
170 // }
171 }
172
173 const auto result = renderInternal(frame);
174 const bool frameIsValid = frame.isValid();
175
176 if (result.done) {
177 m_sessionCtx.explicitNextFrameTime.reset();
178 m_sessionCtx.frames.dequeue();
179
180 if (frameIsValid) {
181 m_sessionCtx.lastPosition.storeRelease(
182 std::max(frame.absolutePts(), lastPosition()).get());
183
184 // TODO: get rid of m_lastFrameEnd or m_seekPos
185 m_sessionCtx.lastFrameEnd = frame.absoluteEnd();
186 m_sessionCtx.seekPos.storeRelaxed(m_sessionCtx.lastFrameEnd.get());
187
188 const auto loopIndex = frame.loopOffset().loopIndex;
189 if (m_sessionCtx.loopIndex < loopIndex) {
190 m_sessionCtx.loopIndex = loopIndex;
191 emit loopChanged(id(), frame.loopOffset().loopStartTimeUs, m_sessionCtx.loopIndex);
192 }
193
194 emit frameProcessed(std::move(frame));
195 } else {
196 m_sessionCtx.lastPosition.storeRelease(
197 std::max(m_sessionCtx.lastFrameEnd, lastPosition()).get());
198 }
199 } else {
200 m_sessionCtx.explicitNextFrameTime = SteadyClock::now() + result.recheckInterval;
201 }
202
203 setAtEnd(result.done && !frameIsValid);
204
205 scheduleNextStep();
206}
207
208std::chrono::microseconds Renderer::frameDelay(const Frame &frame, TimePoint timePoint) const
209{
210 return std::chrono::duration_cast<std::chrono::microseconds>(
211 timePoint - m_sessionCtx.timeController.timeFromPosition(frame.absolutePts()));
212}
213
214void Renderer::changeRendererTime(std::chrono::microseconds offset)
215{
216 const auto now = SteadyClock::now();
217 const auto pos = m_sessionCtx.timeController.positionFromTime(now);
218 m_sessionCtx.timeController.sync(now + offset, pos);
219 emit synchronized(id(), now + offset, pos);
220}
221
222} // namespace QFFmpeg
223
224QT_END_NAMESPACE
225
226#include "moc_qffmpegrenderer_p.cpp"
void onPauseChanged() override
std::chrono::microseconds frameDelay(const Frame &frame, TimePoint timePoint=SteadyClock::now()) const
Renderer(const PlaybackEngineObjectID &id, const TimeController &tc)
void doNextStep() override
bool canDoNextStep() const override
void setTimeController(const TimeController &tc)
void setPlaybackRate(float rate)
void seek(quint64 sessionId, const TimeController &tc, const LoopOffset &offset)
float playbackRate() const
TimePoint nextTimePoint() const override
virtual void seekInternal()
TrackPosition lastPosition() const
virtual void onPlaybackRateChanged()
void changeRendererTime(std::chrono::microseconds offset)
bool isStepForced() const
TrackPosition seekPosition() const
Q_STATIC_LOGGING_CATEGORY(qLCAndroidVideoDevices, "qt.multimedia.ffmpeg.android.videoDevices")
QT_MANGLE_NAMESPACE(QMacScreenCaptureStreamDelegate) QMacScreenCaptureStreamDelegate
Combined button and popup list for selecting options.