16#include <QElapsedTimer>
30class PlaybackDriver :
public QObject
34 PlaybackDriver(QObject* parent =
nullptr);
39 void setFramerate(
int rate);
40 void setSpeed(qreal speed);
42 void setDropFrames(
bool drop);
46 void throttledShowFrame();
49 void updatePlaybackLoopInterval(
const int& in_fps,
const qreal& in_speed);
52 QTimer m_playbackLoop;
58PlaybackDriver::PlaybackDriver(QObject *parent)
64 m_playbackLoop.setTimerType(Qt::PreciseTimer);
65 connect( &m_playbackLoop, SIGNAL(timeout()),
this, SIGNAL(throttledShowFrame()) );
68PlaybackDriver::~PlaybackDriver()
72void PlaybackDriver::setPlaybackState(
PlaybackState newState) {
75 m_playbackLoop.start();
80 m_playbackLoop.stop();
85void PlaybackDriver::setFramerate(
int rate) {
88 updatePlaybackLoopInterval(m_fps, m_speed);
91void PlaybackDriver::setSpeed(qreal speed) {
94 updatePlaybackLoopInterval(m_fps, m_speed);
97double PlaybackDriver::speed()
102void PlaybackDriver::setDropFrames(
bool drop) {
106bool PlaybackDriver::dropFrames() {
110void PlaybackDriver::updatePlaybackLoopInterval(
const int &in_fps,
const qreal &in_speed) {
111 int loopMS = qRound( 1000.f / (qreal(in_fps) * in_speed));
112 m_playbackLoop.setInterval(loopMS);
126 static constexpr int frameStatsWindow = 50;
129 : averageTimePerFrame(frameStatsWindow)
130 , waitingForFrame(false)
131 , droppedFramesStat(frameStatsWindow)
134 timeSinceLastFrame.start();
138 timeSinceLastFrame.start();
139 averageTimePerFrame.reset(frameStatsWindow);
140 waitingForFrame =
false;
141 droppedFramesStat.reset(frameStatsWindow);
156 : driver(new PlaybackDriver())
215 const int droppedFrames =
m_d->measure.droppedFramesStat.rollingSum();
216 const int totalFrames =
217 m_d->measure.droppedFramesStat.rollingCount() +
223 const qreal avgTimePerFrame =
m_d->measure.averageTimePerFrame.rollingMeanSafe();
245 if (
m_d->measure.waitingForFrame) {
251 const int currentFrame = displayProxy->
activeFrame();
255 const int timeSinceLastFrame =
m_d->measure.timeSinceLastFrame.restart();
257 m_d->measure.averageTimePerFrame(timeSinceLastFrame);
262 if (
m_d->driver->dropFrames()) {
263 const int offset = timeSinceLastFrame - timePerFrame;
264 extraFrames = qMax(0, offset) / timePerFrame;
267 m_d->measure.droppedFramesStat(extraFrames);
270 int targetFrame = currentFrame + 1 + extraFrames;
272 targetFrame =
frameWrap(targetFrame, startFrame, endFrame);
274 if (currentFrame != targetFrame) {
276 m_d->measure.waitingForFrame = !
m_d->driver->dropFrames();
278 bool neededRefresh = displayProxy->
displayFrame(targetFrame,
false);
281 m_d->measure.waitingForFrame &= neededRefresh;
290 struct StopAndResume {
301 if (m_self->activeCanvas()) {
302 m_self->m_d->driver->setPlaybackState(m_self->activeCanvas()->animationState()->playbackState());
320 m_d->driver.data()->disconnect(
this);
326 this->disconnect(image->animationInterface());
327 image->animationInterface()->disconnect(
this);
335 displayProxy->disconnect(
this);
340 if (animationState) {
341 this->disconnect(animationState);
342 animationState->disconnect(
this);
347 StopAndResume stopResume(
this);
362 m_d->measure.reset();
365 m_d->driver->setPlaybackState(state);
379 m_d->measure.waitingForFrame =
false;
383 m_d->measure.waitingForFrame =
false;
420#include "KisPlaybackEngineQT.moc"
float value(const T *src, size_t ch)
connect(this, SIGNAL(optionsChanged()), this, SLOT(saveOptions()))
KisCanvasAnimationState * animationState() const
KisImageWSP image() const
The KisCanvasAnimationState class stores all of the canvas-specific animation state.
void sigPlaybackStateChanged(PlaybackState state)
class KisFrameDisplayProxy * displayProxy()
qreal playbackSpeed() const
void sigPlaybackSpeedChanged(qreal value)
The KisFrameDisplayProxy class sits between the KisCanvas (within its KisCanvasAnimationState) and it...
void sigFrameRefreshSkipped()
sigFrameRefreshSkipped tracks whether asynchronous "slow" refreshes are skipped due to the frame bein...
bool displayFrame(int frame, bool forceReproject)
Tell the DisplayProxy to show a new frame.
void sigFrameDisplayRefreshed()
int activeFrame() const
Gets the active frame, the frame that is intended to be shown. This should always reflect the actual ...
void sigFramerateChanged()
const KisTimeSpan & activePlaybackRange() const
activePlaybackRange
KisImageAnimationInterface * animationInterface() const
The KisPlaybackEngineQT class is an implementation of KisPlaybackEngine that drives animation playbac...
void setDropFramesMode(bool value) override
QScopedPointer< Private > m_d
KisPlaybackEngineQT(QObject *parent=nullptr)
PlaybackStats playbackStatistics() const override
void seek(int frameIndex, SeekOptionFlags flags=SEEK_FINALIZE|SEEK_PUSH_AUDIO) override
void unsetCanvas() override
void throttledDriverCallback()
throttledDriverCallback handles signals from the internal driver that drives playback within this eng...
void setCanvas(KoCanvasBase *canvas) override
boost::optional< int64_t > activeFramesPerSecond() const
Krita's base animation playback engine for producing image frame changes and associated audio.
virtual void setDropFramesMode(bool value)
int frameWrap(int frame, int startFrame, int endFrame)
class KisCanvas2 * activeCanvas() const
virtual void setCanvas(KoCanvasBase *p_canvas) override
A simple wrapper class that hides boost includes from QtCreator preventing it from crashing when one ...
A simple wrapper class that hides boost includes from QtCreator preventing it from crashing when one ...
static bool qFuzzyIsNull(half h)
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
ChildIterator< value_type, is_const > parent(const ChildIterator< value_type, is_const > &it)
Struct used to keep track of all frame time variance and accommodate for skipped frames....
KisRollingMeanAccumulatorWrapper averageTimePerFrame
QElapsedTimer timeSinceLastFrame
KisRollingSumAccumulatorWrapper droppedFramesStat
QScopedPointer< PlaybackDriver > driver
qreal droppedFramesPortion