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