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
20
21void Renderer::syncSoft(TimePoint tp, TrackPosition trackPos)
22{
23 QMetaObject::invokeMethod(this, [this, tp, trackPos]() {
24 m_timeController.syncSoft(tp, trackPos);
25 scheduleNextStep(true);
26 });
27}
28
30{
31 return TrackPosition(m_seekPos);
32}
33
35{
36 return TrackPosition(m_lastPosition);
37}
38
39void Renderer::setPlaybackRate(float rate)
40{
41 QMetaObject::invokeMethod(this, [this, rate]() {
42 m_timeController.setPlaybackRate(rate);
43 onPlaybackRateChanged();
44 scheduleNextStep();
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()) {
55 setForceStepDone();
56 }
57 else {
58 m_explicitNextFrameTime = SteadyClock::now();
59 scheduleNextStep();
60 }
61 });
62}
63
65{
66 return m_isStepForced;
67}
68
69void Renderer::start(const TimeController &tc)
70{
71 QMetaObject::invokeMethod(this, [this, tc]() {
72 m_timeController = tc;
73 m_started = true;
74 scheduleNextStep();
75 });
76}
77
78void Renderer::onFinalFrameReceived()
79{
80 render({});
81}
82
83void Renderer::render(Frame frame)
84{
85 const auto isFrameOutdated = frame.isValid() && frame.absoluteEnd() < seekPosition();
86
87 if (isFrameOutdated) {
88 qCDebug(qLcRenderer) << "frame outdated! absEnd:" << frame.absoluteEnd().get() << "absPts"
89 << frame.absolutePts().get() << "seekPos:" << seekPosition().get();
90 emit frameProcessed(std::move(frame));
91 return;
92 }
93
94 m_frames.enqueue(std::move(frame));
95
96 if (m_frames.size() == 1)
97 scheduleNextStep();
98}
99
101{
102 m_timeController.setPaused(isPaused());
103 PlaybackEngineObject::onPauseChanged();
104}
105
107{
108 if (m_frames.empty())
109 return false;
110 if (m_isStepForced)
111 return true;
112 if (!m_started)
113 return false;
114 return PlaybackEngineObject::canDoNextStep();
115}
116
117float Renderer::playbackRate() const
118{
119 return m_timeController.playbackRate();
120}
121
122std::chrono::milliseconds Renderer::timerInterval() const
123{
124 using namespace std::chrono_literals;
125
126 if (m_frames.empty())
127 return 0ms;
128
129 auto calculateInterval = [](const TimePoint &nextTime) {
130 using namespace std::chrono;
131
132 const milliseconds delay = duration_cast<milliseconds>(nextTime - SteadyClock::now());
133 return std::max(0ms, std::chrono::duration_cast<milliseconds>(delay));
134 };
135
136 if (m_explicitNextFrameTime)
137 return calculateInterval(*m_explicitNextFrameTime);
138
139 if (m_frames.front().isValid())
140 return calculateInterval(m_timeController.timeFromPosition(m_frames.front().absolutePts()));
141
142 if (m_lastFrameEnd > TrackPosition(0))
143 return calculateInterval(m_timeController.timeFromPosition(m_lastFrameEnd));
144
145 return 0ms;
146}
147
149{
150 if (!m_isStepForced.testAndSetOrdered(true, false))
151 return false;
152
153 m_explicitNextFrameTime.reset();
154 emit forceStepDone();
155 return true;
156}
157
159{
160 auto frame = m_frames.front();
161
162 if (setForceStepDone()) {
163 // if (frame.isValid() && frame.pts() > m_forceStepMaxPos) {
164 // scheduleNextStep(false);
165 // return;
166 // }
167 }
168
169 const auto result = renderInternal(frame);
170
171 if (result.done) {
172 m_explicitNextFrameTime.reset();
173 m_frames.dequeue();
174
175 if (frame.isValid()) {
176 m_lastPosition.storeRelease(std::max(frame.absolutePts(), lastPosition()).get());
177
178 // TODO: get rid of m_lastFrameEnd or m_seekPos
179 m_lastFrameEnd = frame.absoluteEnd();
180 m_seekPos.storeRelaxed(m_lastFrameEnd.get());
181
182 const auto loopIndex = frame.loopOffset().loopIndex;
183 if (m_loopIndex < loopIndex) {
184 m_loopIndex = loopIndex;
185 emit loopChanged(id(), frame.loopOffset().loopStartTimeUs, m_loopIndex);
186 }
187
188 emit frameProcessed(frame);
189 } else {
190 m_lastPosition.storeRelease(std::max(m_lastFrameEnd, lastPosition()).get());
191 }
192 } else {
193 m_explicitNextFrameTime = SteadyClock::now() + result.recheckInterval;
194 }
195
196 setAtEnd(result.done && !frame.isValid());
197
198 scheduleNextStep(false);
199}
200
201std::chrono::microseconds Renderer::frameDelay(const Frame &frame, TimePoint timePoint) const
202{
203 return std::chrono::duration_cast<std::chrono::microseconds>(
204 timePoint - m_timeController.timeFromPosition(frame.absolutePts()));
205}
206
207void Renderer::changeRendererTime(std::chrono::microseconds offset)
208{
209 const auto now = SteadyClock::now();
210 const auto pos = m_timeController.positionFromTime(now);
211 m_timeController.sync(now + offset, pos);
212 emit synchronized(id(), now + offset, pos);
213}
214
215} // namespace QFFmpeg
216
217QT_END_NAMESPACE
218
219#include "moc_qffmpegrenderer_p.cpp"
void syncSoft(TimePoint tp, TrackPosition trackPos)
void onPauseChanged() override
std::chrono::microseconds frameDelay(const Frame &frame, TimePoint timePoint=SteadyClock::now()) const
void doNextStep() override
bool canDoNextStep() const override
void start(const TimeController &tc)
void setPlaybackRate(float rate)
float playbackRate() const
Renderer(const TimeController &tc)
TrackPosition lastPosition() const
std::chrono::milliseconds timerInterval() const override
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
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)