diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp index 78df8b438..fc8d96c7d 100644 --- a/app/common/timerange.cpp +++ b/app/common/timerange.cpp @@ -58,6 +58,11 @@ TimeRange TimeRange::CombineWith(const TimeRange &a) const return Combine(a, *this); } +bool TimeRange::Contains(const TimeRange &a) const +{ + return (a.in() >= in() && a.out() <= out()); +} + bool TimeRange::Overlap(const TimeRange &a, const TimeRange &b) { return !(a.out() < b.in() || a.in() > b.out()); @@ -85,3 +90,43 @@ uint qHash(const TimeRange &r, uint seed) { return qHash(r.in(), seed) ^ qHash(r.out(), seed); } + +void TimeRangeList::InsertTimeRange(const TimeRange &range) +{ + for (int i=0;i range.in()) { + // This element's out point overlaps the range's in, we'll trim it + (*this)[i].set_out(range.in()); + } else if (compare.in() < range.out() && compare.out() > range.out()) { + // This element's in point overlaps the range's out, we'll trim it + (*this)[i].set_in(range.out()); + } + } +} diff --git a/app/common/timerange.h b/app/common/timerange.h index fa239baed..99e565660 100644 --- a/app/common/timerange.h +++ b/app/common/timerange.h @@ -20,6 +20,7 @@ public: bool OverlapsWith(const TimeRange& a) const; TimeRange CombineWith(const TimeRange& a) const; + bool Contains(const TimeRange& a) const; static bool Overlap(const TimeRange& a, const TimeRange& b); static TimeRange Combine(const TimeRange &a, const TimeRange &b); @@ -34,4 +35,14 @@ private: uint qHash(const TimeRange& r, uint seed); +class TimeRangeList : public QList { +public: + TimeRangeList() = default; + + void InsertTimeRange(const TimeRange& range); + + void RemoveTimeRange(const TimeRange& range); + +}; + #endif // TIMERANGE_H diff --git a/app/render/backend/audiorenderbackend.cpp b/app/render/backend/audiorenderbackend.cpp index 0a2e09522..113d8f5f1 100644 --- a/app/render/backend/audiorenderbackend.cpp +++ b/app/render/backend/audiorenderbackend.cpp @@ -39,10 +39,7 @@ void AudioRenderBackend::InvalidateCache(const rational &start_range, const rati rational end_range_adj = qMin(GetSequenceLength(), end_range); // Add the range to the list - cache_queue_.append(TimeRange(start_range_adj, end_range_adj)); - - // Remove any overlaps so we don't render the same thing twice - ValidateRanges(); + cache_queue_.InsertTimeRange(TimeRange(start_range_adj, end_range_adj)); // Queue value update QueueValueUpdate(); @@ -87,26 +84,6 @@ NodeInput *AudioRenderBackend::GetDependentInput() return viewer_node()->samples_input(); } -void AudioRenderBackend::ValidateRanges() -{ - for (int i=0;i ×, const QVariant &value) +void OpenGLBackend::EmitCachedFrameReady(const QList ×, const QVariant &value, qint64 job_time) { OpenGLTextureCache::ReferencePtr ref = value.value(); OpenGLTexturePtr tex; @@ -153,7 +153,7 @@ void OpenGLBackend::EmitCachedFrameReady(const QList ×, const QVa } foreach (const rational& t, times) { - emit CachedFrameReady(t, QVariant::fromValue(tex)); + emit CachedFrameReady(t, QVariant::fromValue(tex), job_time); } } diff --git a/app/render/backend/opengl/openglbackend.h b/app/render/backend/opengl/openglbackend.h index 506e5c81a..ac6f970fe 100644 --- a/app/render/backend/opengl/openglbackend.h +++ b/app/render/backend/opengl/openglbackend.h @@ -28,7 +28,7 @@ protected: virtual void DecompileInternal() override; - virtual void EmitCachedFrameReady(const QList ×, const QVariant& value) override; + virtual void EmitCachedFrameReady(const QList ×, const QVariant& value, qint64 job_time) override; private: OpenGLTexturePtr CopyTexture(OpenGLTexturePtr input); diff --git a/app/render/backend/renderbackend.h b/app/render/backend/renderbackend.h index 247c82e31..77895a157 100644 --- a/app/render/backend/renderbackend.h +++ b/app/render/backend/renderbackend.h @@ -90,7 +90,7 @@ protected: bool WorkerIsBusy(RenderWorker* worker) const; void SetWorkerBusyState(RenderWorker* worker, bool busy); - QList cache_queue_; + TimeRangeList cache_queue_; QVector processors_; diff --git a/app/render/backend/videorenderbackend.cpp b/app/render/backend/videorenderbackend.cpp index 7ba579030..140d85d8a 100644 --- a/app/render/backend/videorenderbackend.cpp +++ b/app/render/backend/videorenderbackend.cpp @@ -159,6 +159,13 @@ void VideoRenderBackend::SetExportMode(bool enabled) export_mode_ = enabled; } +bool VideoRenderBackend::IsRendered(const rational &time) const +{ + TimeRange range(time, time); + + return !TimeIsQueued(range) && !render_job_info_.contains(range); +} + bool VideoRenderBackend::GenerateCacheIDInternal(QCryptographicHash& hash) { if (!params_.is_valid()) { @@ -250,7 +257,7 @@ bool VideoRenderBackend::CanRender() void VideoRenderBackend::ThreadCompletedFrame(NodeDependency path, qint64 job_time, QByteArray hash, QVariant value) { if (last_time_requested_ == path.in() || frame_cache_.TimeToHash(last_time_requested_) == hash) { - EmitCachedFrameReady({last_time_requested_}, value); + EmitCachedFrameReady({last_time_requested_}, value, job_time); } } @@ -258,7 +265,13 @@ void VideoRenderBackend::ThreadCompletedDownload(NodeDependency dep, qint64 job_ { SetWorkerBusyState(static_cast(sender()), false); - SetFrameHash(dep, hash, job_time); + if (SetFrameHash(dep, hash, job_time)) { + QList hashes_with_time = frame_cache()->FramesWithHash(hash); + + foreach (const rational& t, hashes_with_time) { + emit CachedTimeReady(t, job_time); + } + } // Queue up a new frame for this worker CacheNext(); @@ -269,9 +282,8 @@ void VideoRenderBackend::ThreadSkippedFrame(NodeDependency dep, qint64 job_time, SetWorkerBusyState(static_cast(sender()), false); if (SetFrameHash(dep, hash, job_time) - && last_time_requested_ == dep.in() && frame_cache_.HasHash(hash)) { - emit CachedTimeReady(dep.in()); + emit CachedTimeReady(dep.in(), job_time); } // Queue up a new frame for this worker @@ -282,9 +294,8 @@ void VideoRenderBackend::ThreadHashAlreadyExists(NodeDependency dep, qint64 job_ { SetWorkerBusyState(static_cast(sender()), false); - if (SetFrameHash(dep, hash, job_time) - && dep.in() == last_time_requested_) { - emit CachedTimeReady(dep.in()); + if (SetFrameHash(dep, hash, job_time)) { + emit CachedTimeReady(dep.in(), job_time); } // Queue up a new frame for this worker diff --git a/app/render/backend/videorenderbackend.h b/app/render/backend/videorenderbackend.h index 28515c201..e60cd388b 100644 --- a/app/render/backend/videorenderbackend.h +++ b/app/render/backend/videorenderbackend.h @@ -55,6 +55,8 @@ public: void SetExportMode(bool enabled); + bool IsRendered(const rational& time) const; + public slots: virtual void InvalidateCache(const rational &start_range, const rational &end_range) override; @@ -97,13 +99,13 @@ protected: virtual void ConnectWorkerToThis(RenderWorker* processor) override; - virtual void EmitCachedFrameReady(const QList ×, const QVariant& value) = 0; + virtual void EmitCachedFrameReady(const QList ×, const QVariant& value, qint64 job_time) = 0; bool export_mode_; signals: - void CachedFrameReady(const rational& time, QVariant value); - void CachedTimeReady(const rational& time); + void CachedFrameReady(const rational& time, QVariant value, qint64 job_time); + void CachedTimeReady(const rational& time, qint64 job_time); private: bool TimeIsQueued(const TimeRange &time) const; diff --git a/app/render/backend/videorenderframecache.cpp b/app/render/backend/videorenderframecache.cpp index 54069f69f..fa6434c11 100644 --- a/app/render/backend/videorenderframecache.cpp +++ b/app/render/backend/videorenderframecache.cpp @@ -81,6 +81,21 @@ void VideoRenderFrameCache::RemoveHashFromCurrentlyCaching(const QByteArray &has currently_caching_lock_.unlock(); } +QList VideoRenderFrameCache::FramesWithHash(const QByteArray &hash) +{ + QList times; + + QMap::const_iterator iterator; + + for (iterator=time_hash_map_.begin();iterator!=time_hash_map_.end();iterator++) { + if (iterator.value() == hash) { + times.append(iterator.key()); + } + } + + return times; +} + QString VideoRenderFrameCache::CachePathName(const QByteArray &hash) const { QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_); diff --git a/app/render/backend/videorenderframecache.h b/app/render/backend/videorenderframecache.h index 008075588..f4c9e204d 100644 --- a/app/render/backend/videorenderframecache.h +++ b/app/render/backend/videorenderframecache.h @@ -41,6 +41,8 @@ public: void RemoveHashFromCurrentlyCaching(const QByteArray& hash); + QList FramesWithHash(const QByteArray& hash); + private: QMap time_hash_map_; diff --git a/app/widget/timelinewidget/timelinescaledobject.h b/app/widget/timelinewidget/timelinescaledobject.h index 9c8296f69..c88d2bada 100644 --- a/app/widget/timelinewidget/timelinescaledobject.h +++ b/app/widget/timelinewidget/timelinescaledobject.h @@ -22,10 +22,10 @@ protected: double scale_; private: - rational timebase_; double timebase_dbl_; + }; #endif // TIMELINESCALEDOBJECT_H diff --git a/app/widget/timelinewidget/timelinewidget.cpp b/app/widget/timelinewidget/timelinewidget.cpp index 176ef7e47..2ea8d9f15 100644 --- a/app/widget/timelinewidget/timelinewidget.cpp +++ b/app/widget/timelinewidget/timelinewidget.cpp @@ -32,7 +32,7 @@ TimelineWidget::TimelineWidget(QWidget *parent) : connect(timecode_label_, SIGNAL(ValueChanged(int64_t)), this, SLOT(UpdateInternalTime(const int64_t&))); ruler_and_time_layout->addWidget(timecode_label_); - ruler_ = new TimeRuler(true); + ruler_ = new TimeRuler(true, true); connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), this, SIGNAL(TimeChanged(const int64_t&))); connect(ruler_, SIGNAL(TimeChanged(const int64_t&)), this, SLOT(UpdateInternalTime(const int64_t&))); ruler_and_time_layout->addWidget(ruler_); @@ -60,7 +60,6 @@ TimelineWidget::TimelineWidget(QWidget *parent) : tools_.replace(::Tool::kRazor, std::make_shared(this)); tools_.replace(::Tool::kSlip, std::make_shared(this)); tools_.replace(::Tool::kSlide, std::make_shared(this)); -// tools_.replace(::Tool::kHand, std::make_shared(this)); tools_.replace(::Tool::kZoom, std::make_shared(this)); tools_.replace(::Tool::kTransition, std::make_shared(this)); //tools_.replace(::Tool::kRecord, new PointerTool(this)); FIXME: Implement diff --git a/app/widget/timelinewidget/view/timelineviewbase.cpp b/app/widget/timelinewidget/view/timelineviewbase.cpp index 84452b5a5..6e31f3a26 100644 --- a/app/widget/timelinewidget/view/timelineviewbase.cpp +++ b/app/widget/timelinewidget/view/timelineviewbase.cpp @@ -14,6 +14,7 @@ TimelineViewBase::TimelineViewBase(QWidget *parent) : playhead_(0), playhead_scene_left_(-1), playhead_scene_right_(-1), + dragging_hand_(false), limit_y_axis_(false) { setScene(&scene_); diff --git a/app/widget/timeruler/timeruler.cpp b/app/widget/timeruler/timeruler.cpp index d23315025..38f05be0e 100644 --- a/app/widget/timeruler/timeruler.cpp +++ b/app/widget/timeruler/timeruler.cpp @@ -30,13 +30,14 @@ #include "config/config.h" #include "core.h" -TimeRuler::TimeRuler(bool text_visible, QWidget* parent) : +TimeRuler::TimeRuler(bool text_visible, bool cache_status_visible, QWidget* parent) : QWidget(parent), scroll_(0), + text_visible_(text_visible), centered_text_(true), scale_(1.0), time_(0), - snapping_(false) + show_cache_status_(cache_status_visible) { QFontMetrics fm = fontMetrics(); @@ -44,6 +45,7 @@ TimeRuler::TimeRuler(bool text_visible, QWidget* parent) : // Text height is used to calculate widget height text_height_ = fm.height(); + cache_status_height_ = text_height_ / 4; // Get the "minimum" space allowed between two line markers on the ruler (in screen pixels) // Mediocre but reliable way of scaling UI objects by font/DPI size @@ -53,22 +55,7 @@ TimeRuler::TimeRuler(bool text_visible, QWidget* parent) : playhead_width_ = minimum_gap_between_lines_; // Text visibility affects height, so we set that here - SetTextVisible(text_visible); -} - -void TimeRuler::SetTextVisible(bool e) -{ - text_visible_ = e; - - // Text visibility affects height, if text is visible the widget doubles in height with the top half for text and - // the bottom half for ruler markings - if (text_visible_) { - setMinimumHeight(text_height_ * 2); - } else { - setMinimumHeight(text_height_); - } - - update(); + UpdateHeight(); } const double &TimeRuler::scale() @@ -94,16 +81,20 @@ void TimeRuler::SetTimebase(const rational &r) update(); } -void TimeRuler::SetSnapping(bool snapping) -{ - snapping_ = snapping; -} - const int64_t &TimeRuler::GetTime() { return time_; } +void TimeRuler::SetCacheStatusLength(const rational &length) +{ + cache_length_ = length; + + dirty_cache_ranges_.RemoveTimeRange(TimeRange(length, RATIONAL_MAX)); + + update(); +} + void TimeRuler::SetTime(const int64_t &r) { time_ = r; @@ -118,6 +109,20 @@ void TimeRuler::SetScroll(int s) update(); } +void TimeRuler::CacheInvalidatedRange(const rational& in, const rational& out) +{ + dirty_cache_ranges_.InsertTimeRange(TimeRange(in, out)); + + update(); +} + +void TimeRuler::CacheTimeReady(const rational &time) +{ + dirty_cache_ranges_.RemoveTimeRange(TimeRange(time, time + timebase_)); + + update(); +} + void TimeRuler::paintEvent(QPaintEvent *) { // Nothing to paint if the timebase is invalid @@ -198,11 +203,16 @@ void TimeRuler::paintEvent(QPaintEvent *) // Calculate line dimensions QFontMetrics fm = p.fontMetrics(); + int line_bottom = height(); + + if (show_cache_status_) { + line_bottom -= cache_status_height_; + } + int long_height = fm.height(); int short_height = long_height/2; - int long_y = height() - long_height; - int short_y = height() - short_height; - int line_bottom = height(); + int long_y = line_bottom - long_height; + int short_y = line_bottom - short_height; // Draw long lines int last_long_unit = -1; @@ -267,12 +277,34 @@ void TimeRuler::paintEvent(QPaintEvent *) } } + // If cache status is enabled + if (show_cache_status_) { + int cache_screen_length = qMin(TimeToScreen(cache_length_), width()); + + if (cache_screen_length > 0) { + int cache_y = height() - cache_status_height_; + + p.fillRect(0, cache_y, cache_screen_length , cache_status_height_, Qt::green); + + foreach (const TimeRange& range, dirty_cache_ranges_) { + int range_left = TimeToScreen(range.in()); + int range_right = TimeToScreen(range.out()); + + if (range_left >= width() || range_right < 0) { + continue; + } + + p.fillRect(qMax(0, range_left), cache_y, qMin(width(), range_right) - range_left, cache_status_height_, Qt::red); + } + } + } + // Draw the playhead if it's on screen at the moment - int playhead_pos = qFloor(static_cast(time_) * scale_ * timebase_dbl_) - scroll_; + int playhead_pos = UnitToScreen(time_); if (playhead_pos + playhead_width_ >= 0 && playhead_pos - playhead_width_ < width()) { p.setPen(Qt::NoPen); p.setBrush(style_.PlayheadColor()); - DrawPlayhead(&p, playhead_pos, height()); + DrawPlayhead(&p, playhead_pos, line_bottom); } } @@ -317,6 +349,16 @@ int64_t TimeRuler::ScreenToUnit(int screen) return qFloor(ScreenToUnitFloat(screen)); } +int TimeRuler::UnitToScreen(int64_t unit) +{ + return qFloor(static_cast(unit) * scale_ * timebase_dbl_) - scroll_; +} + +int TimeRuler::TimeToScreen(const rational &time) +{ + return qFloor(time.toDouble() * scale_) - scroll_; +} + void TimeRuler::SeekToScreenPoint(int screen) { int64_t timestamp = qMax(0, qRound(ScreenToUnitFloat(screen))); @@ -325,3 +367,18 @@ void TimeRuler::SeekToScreenPoint(int screen) emit TimeChanged(timestamp); } + +void TimeRuler::UpdateHeight() +{ + int height = text_height_; + + if (text_visible_) { + height += text_height_; + } + + if (show_cache_status_) { + height += cache_status_height_; + } + + setFixedHeight(height); +} diff --git a/app/widget/timeruler/timeruler.h b/app/widget/timeruler/timeruler.h index a0a372dae..396913974 100644 --- a/app/widget/timeruler/timeruler.h +++ b/app/widget/timeruler/timeruler.h @@ -25,15 +25,14 @@ #include #include "common/rational.h" +#include "common/timerange.h" #include "widget/timelinewidget/view/timelineplayhead.h" class TimeRuler : public QWidget { Q_OBJECT public: - TimeRuler(bool text_visible = true, QWidget* parent = nullptr); - - void SetTextVisible(bool e); + TimeRuler(bool text_visible = true, bool cache_status_visible = false, QWidget* parent = nullptr); const double& scale(); void SetScale(const double& d); @@ -42,8 +41,6 @@ public: void SetCenteredText(bool c); - void SetSnapping(bool snapping); - const int64_t& GetTime(); public slots: @@ -51,6 +48,12 @@ public slots: void SetScroll(int s); + void CacheInvalidatedRange(const rational& in, const rational& out); + + void CacheTimeReady(const rational& time); + + void SetCacheStatusLength(const rational& length); + protected: virtual void paintEvent(QPaintEvent* e) override; @@ -64,14 +67,7 @@ signals: void TimeChanged(int64_t); private: - enum Component { - kNone, - kDay, - kHour, - kMinute, - kSecond, - kFrame - }; + void UpdateHeight(); void DrawPlayhead(QPainter* p, int x, int y); @@ -79,10 +75,16 @@ private: int64_t ScreenToUnit(int screen); + int UnitToScreen(int64_t unit); + + int TimeToScreen(const rational& time); + void SeekToScreenPoint(int screen); int text_height_; + int cache_status_height_; + int minimum_gap_between_lines_; int playhead_width_; @@ -105,7 +107,11 @@ private: TimelinePlayhead style_; - bool snapping_; + bool show_cache_status_; + + rational cache_length_; + + TimeRangeList dirty_cache_ranges_; }; diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 3e2827f46..9683d9601 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -49,7 +49,7 @@ ViewerWidget::ViewerWidget(QWidget *parent) : sizer_->SetWidget(gl_widget_); // Create time ruler - ruler_ = new TimeRuler(false); + ruler_ = new TimeRuler(false, true); layout->addWidget(ruler_); connect(ruler_, SIGNAL(TimeChanged(int64_t)), this, SLOT(RulerTimeChange(int64_t))); @@ -79,8 +79,9 @@ ViewerWidget::ViewerWidget(QWidget *parent) : // Start background renderers video_renderer_ = new OpenGLBackend(this); - connect(video_renderer_, SIGNAL(CachedFrameReady(const rational&, QVariant)), this, SLOT(RendererCachedFrame(const rational&, QVariant))); - connect(video_renderer_, SIGNAL(CachedTimeReady(const rational&)), this, SLOT(RendererCachedTime(const rational&))); + connect(video_renderer_, &VideoRenderBackend::CachedFrameReady, this, &ViewerWidget::RendererCachedFrame); + connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, this, &ViewerWidget::RendererCachedTime); + connect(video_renderer_, &VideoRenderBackend::CachedTimeReady, ruler_, &TimeRuler::CacheTimeReady); audio_renderer_ = new AudioBackend(this); } @@ -138,9 +139,11 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_man if (viewer_node_ != nullptr) { SetTimebase(0); - disconnect(viewer_node_, SIGNAL(TimebaseChanged(const rational&)), this, SLOT(SetTimebase(const rational&))); - disconnect(viewer_node_, SIGNAL(SizeChanged(int, int)), this, SLOT(SizeChangedSlot(int, int))); - disconnect(viewer_node_, SIGNAL(LengthChanged(const rational&)), this, SLOT(LengthChangedSlot(const rational&))); + disconnect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &ViewerWidget::SetTimebase); + disconnect(viewer_node_, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot); + disconnect(viewer_node_, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); + disconnect(viewer_node_, &ViewerOutput::LengthChanged, ruler_, &TimeRuler::SetCacheStatusLength); + disconnect(viewer_node_, &ViewerOutput::VideoChangedBetween, ruler_, &TimeRuler::CacheInvalidatedRange); // Effectively disables the viewer and clears the state SizeChangedSlot(0, 0); @@ -159,9 +162,11 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_man if (viewer_node_ != nullptr) { SetTimebase(viewer_node_->video_params().time_base()); - connect(viewer_node_, SIGNAL(TimebaseChanged(const rational&)), this, SLOT(SetTimebase(const rational&))); - connect(viewer_node_, SIGNAL(SizeChanged(int, int)), this, SLOT(SizeChangedSlot(int, int))); - connect(viewer_node_, SIGNAL(LengthChanged(const rational&)), this, SLOT(LengthChangedSlot(const rational&))); + connect(viewer_node_, &ViewerOutput::TimebaseChanged, this, &ViewerWidget::SetTimebase); + connect(viewer_node_, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot); + connect(viewer_node_, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot); + connect(viewer_node_, &ViewerOutput::LengthChanged, ruler_, &TimeRuler::SetCacheStatusLength); + connect(viewer_node_, &ViewerOutput::VideoChangedBetween, ruler_, &TimeRuler::CacheInvalidatedRange); SizeChangedSlot(viewer_node_->video_params().width(), viewer_node_->video_params().height()); LengthChangedSlot(viewer_node_->Length()); @@ -407,16 +412,18 @@ void ViewerWidget::PlaybackTimerUpdate() SetTime(current_time); } -void ViewerWidget::RendererCachedFrame(const rational &time, QVariant value) +void ViewerWidget::RendererCachedFrame(const rational &time, QVariant value, qint64 job_time) { if (GetTime() == time) { SetTexture(value.value()); + + frame_cache_job_time_ = job_time; } } -void ViewerWidget::RendererCachedTime(const rational &time) +void ViewerWidget::RendererCachedTime(const rational &time, qint64 job_time) { - if (GetTime() == time) { + if (GetTime() == time && job_time > frame_cache_job_time_) { UpdateTextureFromNode(GetTime()); } } diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 8c82e4be8..36bd04ec9 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -171,13 +171,15 @@ private: int playback_speed_; + qint64 frame_cache_job_time_; + private slots: void RulerTimeChange(int64_t); void PlaybackTimerUpdate(); - void RendererCachedFrame(const rational& time, QVariant value); - void RendererCachedTime(const rational& time); + void RendererCachedFrame(const rational& time, QVariant value, qint64 job_time); + void RendererCachedTime(const rational& time, qint64 job_time); void SizeChangedSlot(int width, int height);