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)
19{
20}
21
23{
24 return TrackPosition(m_seekPos);
25}
26
28{
29 return TrackPosition(m_lastPosition);
30}
31
32void Renderer::setPlaybackRate(float rate)
33{
34 invokePriorityMethod([this, rate]() {
35 m_timeController.setPlaybackRate(rate);
37 scheduleNextStep();
38 });
39}
40
42{
43 if (m_isStepForced.testAndSetOrdered(false, true))
44 invokePriorityMethod([this]() {
45 // maybe set m_forceStepMaxPos
46
47 if (isAtEnd()) {
49 }
50 else {
51 m_explicitNextFrameTime = SteadyClock::now();
52 scheduleNextStep();
53 }
54 });
55}
56
58{
59 return m_isStepForced;
60}
61
62void Renderer::setTimeController(const TimeController &tc)
63{
64 Q_ASSERT(tc.isStarted());
65 invokePriorityMethod([this, tc]() {
66 m_timeController = tc;
67 scheduleNextStep();
68 });
69}
70
71void Renderer::onFinalFrameReceived(PlaybackEngineObjectID sourceID)
72{
73 if (checkSessionID(sourceID.sessionID))
74 render({});
75}
76
77void Renderer::render(Frame frame)
78{
79 if (frame.isValid() && !checkSessionID(frame.sourceID().sessionID)) {
80 qCDebug(qLcRenderer) << "Frame session outdated. Source id:" << frame.sourceID() << "current id:" << id();
81 // else don't need to report
82 return;
83 }
84
85 const bool frameOutdated = frame.isValid() && frame.absoluteEnd() < seekPosition();
86
87 if (frameOutdated) {
88 qCDebug(qLcRenderer) << "frame outdated! absEnd:" << frame.absoluteEnd().get() << "absPts"
89 << frame.absolutePts().get() << "seekPos:" << seekPosition().get();
90
91 emit frameProcessed(std::move(frame));
92 return;
93 }
94
95 m_frames.enqueue(std::move(frame));
96
97 if (m_frames.size() == 1)
98 scheduleNextStep();
99}
100
102{
103 m_timeController.setPaused(isPaused());
104 PlaybackEngineObject::onPauseChanged();
105}
106
108{
109 if (m_frames.empty())
110 return false;
111 // do the step even if the TC is not started;
112 // may be changed if the case is found.
113 if (m_isStepForced)
114 return true;
115 if (!m_timeController.isStarted())
116 return false;
117 return PlaybackEngineObject::canDoNextStep();
118}
119
120float Renderer::playbackRate() const
121{
122 return m_timeController.playbackRate();
123}
124
126{
127 using namespace std::chrono_literals;
128
129 if (m_frames.empty())
130 return PlaybackEngineObject::nextTimePoint();
131
132 if (m_explicitNextFrameTime)
133 return *m_explicitNextFrameTime;
134
135 if (m_frames.front().isValid())
136 return m_timeController.timeFromPosition(m_frames.front().absolutePts());
137
138 if (m_lastFrameEnd > TrackPosition(0))
139 return m_timeController.timeFromPosition(m_lastFrameEnd);
140
141 return PlaybackEngineObject::nextTimePoint();
142}
143
145{
146 if (!m_isStepForced.testAndSetOrdered(true, false))
147 return false;
148
149 m_explicitNextFrameTime.reset();
150 emit forceStepDone();
151 return true;
152}
153
155{
156 Frame frame = m_frames.front();
157
158 if (setForceStepDone()) {
159 // if (frame.isValid() && frame.pts() > m_forceStepMaxPos) {
160 // scheduleNextStep();
161 // return;
162 // }
163 }
164
165 const auto result = renderInternal(frame);
166 const bool frameIsValid = frame.isValid();
167
168 if (result.done) {
169 m_explicitNextFrameTime.reset();
170 m_frames.dequeue();
171
172 if (frameIsValid) {
173 m_lastPosition.storeRelease(std::max(frame.absolutePts(), lastPosition()).get());
174
175 // TODO: get rid of m_lastFrameEnd or m_seekPos
176 m_lastFrameEnd = frame.absoluteEnd();
177 m_seekPos.storeRelaxed(m_lastFrameEnd.get());
178
179 const auto loopIndex = frame.loopOffset().loopIndex;
180 if (m_loopIndex < loopIndex) {
181 m_loopIndex = loopIndex;
182 emit loopChanged(id(), frame.loopOffset().loopStartTimeUs, m_loopIndex);
183 }
184
185 emit frameProcessed(std::move(frame));
186 } else {
187 m_lastPosition.storeRelease(std::max(m_lastFrameEnd, lastPosition()).get());
188 }
189 } else {
190 m_explicitNextFrameTime = SteadyClock::now() + result.recheckInterval;
191 }
192
193 setAtEnd(result.done && !frameIsValid);
194
195 scheduleNextStep();
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 = SteadyClock::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
214QT_END_NAMESPACE
215
216#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)
float playbackRate() const
TimePoint nextTimePoint() const override
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,...)