renderer/viewer: fixed playback and live updates from value changes
This commit is contained in:
@@ -151,11 +151,7 @@ void MatrixGenerator::GizmoMove(const QPointF &p, const QVector2D &scale, const
|
||||
|
||||
gizmo_x2_dragger_.Drag(new_pos2.x());
|
||||
gizmo_y2_dragger_.Drag(new_pos2.y());
|
||||
|
||||
InvalidateVisible(position_input_, position_input_);
|
||||
}
|
||||
|
||||
InvalidateVisible(gizmo_drag_, gizmo_drag_);
|
||||
}
|
||||
|
||||
void MatrixGenerator::GizmoRelease()
|
||||
|
||||
@@ -150,8 +150,6 @@ void PolygonGenerator::GizmoMove(const QPointF &p, const QVector2D &scale, const
|
||||
|
||||
gizmo_x_dragger_.Drag(new_pos.x());
|
||||
gizmo_y_dragger_.Drag(new_pos.y());
|
||||
|
||||
InvalidateVisible(gizmo_drag_, gizmo_drag_);
|
||||
}
|
||||
|
||||
void PolygonGenerator::GizmoRelease()
|
||||
|
||||
@@ -61,9 +61,9 @@ void NodeInputDragger::Start(NodeInput *input, const rational &time, int track)
|
||||
track);
|
||||
|
||||
// We disable default signal emitting during the drag
|
||||
input_->blockSignals(true);
|
||||
//input_->blockSignals(true);
|
||||
input_->insert_keyframe(dragging_key_);
|
||||
input_->blockSignals(false);
|
||||
//input_->blockSignals(false);
|
||||
|
||||
emit input_->KeyframeAdded(dragging_key_);
|
||||
}
|
||||
@@ -76,7 +76,7 @@ void NodeInputDragger::Drag(const QVariant& value)
|
||||
|
||||
end_value_ = value;
|
||||
|
||||
input_->blockSignals(true);
|
||||
//input_->blockSignals(true);
|
||||
|
||||
if (input_->is_keyframing()) {
|
||||
dragging_key_->set_value(value);
|
||||
@@ -84,7 +84,7 @@ void NodeInputDragger::Drag(const QVariant& value)
|
||||
input_->set_standard_value(value, track_);
|
||||
}
|
||||
|
||||
input_->blockSignals(false);
|
||||
//input_->blockSignals(false);
|
||||
}
|
||||
|
||||
void NodeInputDragger::End()
|
||||
|
||||
@@ -175,19 +175,6 @@ void Node::InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput *s
|
||||
SendInvalidateCache(range, source);
|
||||
}
|
||||
|
||||
void Node::InvalidateVisible(NodeInput *from, NodeInput* source)
|
||||
{
|
||||
Q_UNUSED(from)
|
||||
|
||||
foreach (NodeParam* param, params_) {
|
||||
if (param->type() == NodeParam::kOutput) {
|
||||
foreach (NodeEdgePtr edge, param->edges()) {
|
||||
edge->input()->parentNode()->InvalidateVisible(edge->input(), source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeRange Node::InputTimeAdjustment(NodeInput *, const TimeRange &input_time) const
|
||||
{
|
||||
// Default behavior is no time adjustment at all
|
||||
|
||||
@@ -325,11 +325,6 @@ public:
|
||||
*/
|
||||
virtual void InvalidateCache(const TimeRange& range, NodeInput* from, NodeInput* source);
|
||||
|
||||
/**
|
||||
* @brief Signal through node graph to only invalidate frames that are currently visible on a ViewerWidget
|
||||
*/
|
||||
virtual void InvalidateVisible(NodeInput *from, NodeInput* source);
|
||||
|
||||
/**
|
||||
* @brief Adjusts time that should be sent to nodes connected to certain inputs.
|
||||
*
|
||||
|
||||
@@ -99,15 +99,6 @@ void ViewerOutput::InvalidateCache(const TimeRange &range, NodeInput *from, Node
|
||||
Node::InvalidateCache(range, from, source);
|
||||
}
|
||||
|
||||
void ViewerOutput::InvalidateVisible(NodeInput* from, NodeInput *source)
|
||||
{
|
||||
if (from == texture_input()) {
|
||||
emit VisibleInvalidated(source);
|
||||
}
|
||||
|
||||
Node::InvalidateVisible(from, source);
|
||||
}
|
||||
|
||||
void ViewerOutput::set_video_params(const VideoParams &video)
|
||||
{
|
||||
video_params_ = video;
|
||||
|
||||
@@ -63,7 +63,6 @@ public:
|
||||
}
|
||||
|
||||
virtual void InvalidateCache(const TimeRange &range, NodeInput *from, NodeInput* source) override;
|
||||
virtual void InvalidateVisible(NodeInput *from, NodeInput* source) override;
|
||||
|
||||
const VideoParams& video_params() const {
|
||||
return video_params_;
|
||||
@@ -115,8 +114,6 @@ signals:
|
||||
|
||||
void GraphChangedFrom(NodeInput* source);
|
||||
|
||||
void VisibleInvalidated(NodeInput* source);
|
||||
|
||||
void LengthChanged(const rational& length);
|
||||
|
||||
void SizeChanged(int width, int height);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "audioplaybackcache.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#include "common/filefunctions.h"
|
||||
@@ -42,6 +43,14 @@ void AudioPlaybackCache::SetParameters(const AudioRenderingParams ¶ms)
|
||||
return;
|
||||
}
|
||||
|
||||
params_ = params;
|
||||
|
||||
// Restart empty file so there's always "something" to play
|
||||
QFile f(filename_);
|
||||
if (f.open(QFile::WriteOnly)) {
|
||||
f.close();
|
||||
}
|
||||
|
||||
// Our current audio cache is unusable, so we truncate it automatically
|
||||
InvalidateAll();
|
||||
|
||||
|
||||
@@ -101,10 +101,8 @@ void RenderBackend::CancelQueue()
|
||||
|
||||
QFuture<QByteArray> RenderBackend::Hash(const rational &time)
|
||||
{
|
||||
RenderWorker* instance = GetInstanceFromPool();
|
||||
|
||||
return QtConcurrent::run(&thread_pool_,
|
||||
instance,
|
||||
GetInstanceFromPool(),
|
||||
&RenderWorker::Hash,
|
||||
time);
|
||||
}
|
||||
@@ -115,10 +113,8 @@ QFuture<FramePtr> RenderBackend::RenderFrame(const rational &time, bool clear_qu
|
||||
thread_pool_.clear();
|
||||
}
|
||||
|
||||
RenderWorker* instance = GetInstanceFromPool();
|
||||
|
||||
return QtConcurrent::run(&thread_pool_,
|
||||
instance,
|
||||
GetInstanceFromPool(),
|
||||
&RenderWorker::RenderFrame,
|
||||
time);
|
||||
}
|
||||
@@ -195,6 +191,12 @@ RenderWorker *RenderBackend::GetInstanceFromPool()
|
||||
if (viewer_node_) {
|
||||
instance->Init(viewer_node_);
|
||||
}
|
||||
|
||||
connect(instance,
|
||||
&RenderWorker::FinishedJob,
|
||||
this,
|
||||
&RenderBackend::WorkerFinished,
|
||||
Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
instance->SetAvailable(false);
|
||||
@@ -225,6 +227,11 @@ void RenderBackend::AudioCallback()
|
||||
*/
|
||||
}
|
||||
|
||||
void RenderBackend::WorkerFinished()
|
||||
{
|
||||
static_cast<RenderWorker*>(sender())->SetAvailable(true);
|
||||
}
|
||||
|
||||
bool RenderBackend::ConformWaitInfo::operator==(const RenderBackend::ConformWaitInfo &rhs) const
|
||||
{
|
||||
return rhs.stream == stream
|
||||
|
||||
@@ -109,6 +109,8 @@ private:
|
||||
private slots:
|
||||
void AudioCallback();
|
||||
|
||||
void WorkerFinished();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -39,7 +39,7 @@ RenderWorker::~RenderWorker()
|
||||
Close();
|
||||
}
|
||||
|
||||
QByteArray RenderWorker::Hash(const rational &time) const
|
||||
QByteArray RenderWorker::Hash(const rational &time)
|
||||
{
|
||||
if (!viewer_) {
|
||||
return QByteArray();
|
||||
@@ -55,6 +55,8 @@ QByteArray RenderWorker::Hash(const rational &time) const
|
||||
|
||||
viewer_->Hash(hasher, time);
|
||||
|
||||
emit FinishedJob();
|
||||
|
||||
return hasher.result();
|
||||
}
|
||||
|
||||
@@ -69,10 +71,6 @@ FramePtr RenderWorker::RenderFrame(const rational &time)
|
||||
|
||||
QVariant texture = table.Get(NodeParam::kTexture);
|
||||
|
||||
if (texture.isNull()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FramePtr frame = Frame::Create();
|
||||
frame->set_video_params(video_params_);
|
||||
frame->set_timestamp(time);
|
||||
@@ -86,6 +84,8 @@ FramePtr RenderWorker::RenderFrame(const rational &time)
|
||||
TextureToFrame(texture, frame, video_download_matrix_);
|
||||
}
|
||||
|
||||
emit FinishedJob();
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
*
|
||||
* SHA-1 hash or empty QByteArray if no viewer node is set.
|
||||
*/
|
||||
QByteArray Hash(const rational &time) const;
|
||||
QByteArray Hash(const rational &time);
|
||||
|
||||
/**
|
||||
* @brief Render the frame at this time
|
||||
@@ -127,6 +127,8 @@ protected:
|
||||
signals:
|
||||
void AudioConformUnavailable();
|
||||
|
||||
void FinishedJob();
|
||||
|
||||
private:
|
||||
NodeValue GetDataFromStream(StreamPtr stream, const TimeRange& input_time);
|
||||
|
||||
|
||||
@@ -174,9 +174,9 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
|
||||
false);
|
||||
} else if (!selected_keys_.isEmpty()) {
|
||||
foreach (const KeyframeItemAndTime& keypair, selected_keys_) {
|
||||
NodeInput* input_parent = keypair.key->key()->parent();
|
||||
//NodeInput* input_parent = keypair.key->key()->parent();
|
||||
|
||||
input_parent->blockSignals(true);
|
||||
//input_parent->blockSignals(true);
|
||||
|
||||
rational node_time = GetAdjustedTime(GetTimeTarget(),
|
||||
keypair.key->key()->parent()->parentNode(),
|
||||
@@ -191,9 +191,9 @@ void KeyframeViewBase::mouseMoveEvent(QMouseEvent *event)
|
||||
|
||||
// We emit a custom value changed signal while the keyframe is being dragged so only the currently viewed
|
||||
// frame gets rendered in this time
|
||||
input_parent->blockSignals(false);
|
||||
//input_parent->blockSignals(false);
|
||||
|
||||
input_parent->parentNode()->InvalidateVisible(input_parent, input_parent);
|
||||
//input_parent->parentNode()->InvalidateVisible(input_parent, input_parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,7 +352,7 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include
|
||||
new_opposing_pos = dragging_bezier_point_opposing_start_;
|
||||
}
|
||||
|
||||
NodeInput* input_parent = dragging_bezier_point_->key()->parent();
|
||||
//NodeInput* input_parent = dragging_bezier_point_->key()->parent();
|
||||
|
||||
if (undoable) {
|
||||
QUndoCommand* command = new QUndoCommand();
|
||||
@@ -360,7 +360,7 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include
|
||||
// Similar to the code in MouseRelease, we manipulated the signalling earlier and need to set the keys back to their
|
||||
// original position to allow the input to signal correctly when the undo command is pushed.
|
||||
|
||||
input_parent->blockSignals(true);
|
||||
//input_parent->blockSignals(true);
|
||||
|
||||
dragging_bezier_point_->key()->set_bezier_control(dragging_bezier_point_->mode(),
|
||||
dragging_bezier_point_start_);
|
||||
@@ -382,11 +382,11 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include
|
||||
command);
|
||||
}
|
||||
|
||||
input_parent->blockSignals(false);
|
||||
//input_parent->blockSignals(false);
|
||||
|
||||
Core::instance()->undo_stack()->push(command);
|
||||
} else {
|
||||
input_parent->blockSignals(true);
|
||||
//input_parent->blockSignals(true);
|
||||
|
||||
dragging_bezier_point_->key()->set_bezier_control(dragging_bezier_point_->mode(),
|
||||
new_bezier_pos);
|
||||
@@ -394,9 +394,9 @@ void KeyframeViewBase::ProcessBezierDrag(QPointF mouse_diff_scaled, bool include
|
||||
dragging_bezier_point_->key()->set_bezier_control(opposing_type,
|
||||
new_opposing_pos);
|
||||
|
||||
input_parent->blockSignals(false);
|
||||
//input_parent->blockSignals(false);
|
||||
|
||||
input_parent->parentNode()->InvalidateVisible(input_parent, input_parent);
|
||||
//input_parent->parentNode()->InvalidateVisible(input_parent, input_parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -233,7 +233,7 @@ void NodeParamViewWidgetBridge::ProcessSlider(SliderBase *slider, const QVariant
|
||||
|
||||
dragger_.Drag(value);
|
||||
|
||||
input_->parentNode()->InvalidateVisible(input_, input_);
|
||||
//input_->parentNode()->InvalidateVisible(input_, input_);
|
||||
|
||||
} else if (dragger_.IsStarted()) {
|
||||
|
||||
|
||||
@@ -101,12 +101,6 @@ ViewerWidget::ViewerWidget(QWidget *parent) :
|
||||
|
||||
// Start background renderers
|
||||
renderer_ = new OpenGLBackend(this);
|
||||
/*
|
||||
connect(renderer_, &RenderBackend::CachedTimeReady, this, &ViewerWidget::RendererCachedTime);
|
||||
connect(renderer_, &RenderBackend::CachedTimeReady, ruler(), &TimeRuler::CacheTimeReady);
|
||||
connect(renderer_, &RenderBackend::RangeInvalidated, ruler(), &TimeRuler::CacheInvalidatedRange);
|
||||
connect(renderer_, &RenderBackend::GeneratedFrame, this, &ViewerWidget::RendererGeneratedFrame);
|
||||
*/
|
||||
|
||||
connect(waveform_view_, &AudioWaveformView::TimeChanged, this, &ViewerWidget::SetTimeAndSignal);
|
||||
|
||||
@@ -160,9 +154,12 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
connect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot);
|
||||
connect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
|
||||
connect(n, &ViewerOutput::ParamsChanged, this, &ViewerWidget::UpdateRendererParameters);
|
||||
connect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible);
|
||||
connect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedRange);
|
||||
connect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack);
|
||||
|
||||
n->audio_playback_cache()->SetParameters(AudioRenderingParams(n->audio_params(),
|
||||
SampleFormat::kInternalFormat));
|
||||
|
||||
SizeChangedSlot(n->video_params().width(), n->video_params().height());
|
||||
LengthChangedSlot(n->GetLength());
|
||||
|
||||
@@ -191,6 +188,9 @@ void ViewerWidget::ConnectNodeInternal(ViewerOutput *n)
|
||||
waveform_view_->SetViewer(GetConnectedNode()->audio_playback_cache());
|
||||
waveform_view_->ConnectTimelinePoints(GetConnectedTimelinePoints());
|
||||
}
|
||||
|
||||
// Set texture to new texture (or null if no viewer node is available)
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
@@ -203,7 +203,7 @@ void ViewerWidget::DisconnectNodeInternal(ViewerOutput *n)
|
||||
disconnect(n, &ViewerOutput::SizeChanged, this, &ViewerWidget::SizeChangedSlot);
|
||||
disconnect(n, &ViewerOutput::LengthChanged, this, &ViewerWidget::LengthChangedSlot);
|
||||
disconnect(n, &ViewerOutput::ParamsChanged, this, &ViewerWidget::UpdateRendererParameters);
|
||||
disconnect(n, &ViewerOutput::VisibleInvalidated, this, &ViewerWidget::InvalidateVisible);
|
||||
disconnect(n->video_frame_cache(), &FrameHashCache::Invalidated, this, &ViewerWidget::ViewerInvalidatedRange);
|
||||
disconnect(n, &ViewerOutput::GraphChangedFrom, this, &ViewerWidget::UpdateStack);
|
||||
|
||||
// Effectively disables the viewer and clears the state
|
||||
@@ -270,9 +270,6 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node, ColorManager* color_man
|
||||
override_color_manager_ = color_manager;
|
||||
|
||||
TimeBasedWidget::ConnectViewerNode(node);
|
||||
|
||||
// Set texture to new texture (or null if no viewer node is available)
|
||||
ForceUpdate();
|
||||
}
|
||||
|
||||
void ViewerWidget::SetColorMenuEnabled(bool enabled)
|
||||
@@ -341,6 +338,12 @@ void ViewerWidget::SetGizmos(Node *node)
|
||||
|
||||
void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
{
|
||||
if (!FrameExistsAtTime(time)) {
|
||||
// There is definitely no frame here, we can immediately flip to showing nothing
|
||||
SetDisplayImage(nullptr, false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker locker(playback_queue_.lock());
|
||||
|
||||
@@ -348,12 +351,17 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
const ViewerPlaybackFrame& pf = playback_queue_.first();
|
||||
|
||||
if (pf.timestamp == time) {
|
||||
|
||||
// Frame was in queue, no need to decode anything
|
||||
SetDisplayImage(pf.frame, true);
|
||||
QtConcurrent::run(this, &ViewerWidget::FillPlaybackQueue);
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
// Skip this frame
|
||||
playback_queue_.removeFirst();
|
||||
RequestNextFrameForQueue();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -363,28 +371,14 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
}
|
||||
|
||||
// Frame was not in queue, will require decoding
|
||||
if (FrameExistsAtTime(time)) {
|
||||
QString frame_fn = GetCachedFilenameFromTime(time);
|
||||
QFutureWatcher<FramePtr>* watcher = new QFutureWatcher<FramePtr>();
|
||||
|
||||
if (frame_fn.isEmpty()) {
|
||||
connect(watcher,
|
||||
&QFutureWatcher<FramePtr>::finished,
|
||||
this,
|
||||
&ViewerWidget::RendererGeneratedFrame);
|
||||
|
||||
QFutureWatcher<FramePtr>* watcher = new QFutureWatcher<FramePtr>();
|
||||
connect(watcher,
|
||||
&QFutureWatcher<FramePtr>::finished,
|
||||
this,
|
||||
&ViewerWidget::RendererGeneratedFrame);
|
||||
|
||||
watcher->setFuture(renderer_->RenderFrame(time, true));
|
||||
|
||||
} else {
|
||||
|
||||
FramePtr f = DecodeCachedImage(frame_fn);
|
||||
SetDisplayImage(f, false);
|
||||
|
||||
}
|
||||
} else {
|
||||
SetDisplayImage(nullptr, false);
|
||||
}
|
||||
watcher->setFuture(renderer_->RenderFrame(time, true));
|
||||
}
|
||||
|
||||
void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
@@ -486,43 +480,19 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp
|
||||
|
||||
void ViewerWidget::FillPlaybackQueue()
|
||||
{
|
||||
playback_queue_.lock()->lock();
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->queue()->lock()->lock();
|
||||
}
|
||||
|
||||
// FIXME: Replace with an asynchronous approach
|
||||
while (playback_queue_.size() < 8) {
|
||||
// Load frame from cache
|
||||
FramePtr frame = nullptr;
|
||||
|
||||
rational rtime = Timecode::timestamp_to_time(playback_queue_next_frame_,
|
||||
timebase());
|
||||
|
||||
QString frame_fn = GetCachedFilenameFromTime(rtime);
|
||||
|
||||
if (!frame_fn.isEmpty()) {
|
||||
frame = DecodeCachedImage(frame_fn);
|
||||
}
|
||||
|
||||
ViewerPlaybackFrame f = {rtime, frame};
|
||||
|
||||
playback_queue_.append(f);
|
||||
rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_,
|
||||
timebase());
|
||||
playback_queue_next_frame_ += playback_speed_;
|
||||
QFuture<FramePtr> future = renderer_->RenderFrame(next_time, false);
|
||||
future.waitForFinished();
|
||||
playback_queue_.AppendTimewise({future.result()->timestamp(), future.result()}, playback_speed_);
|
||||
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->queue()->append(f);
|
||||
window->queue()->AppendTimewise({future.result()->timestamp(), future.result()}, playback_speed_);
|
||||
}
|
||||
|
||||
if (!playback_speed_) {
|
||||
break;
|
||||
}
|
||||
|
||||
playback_queue_next_frame_ += playback_speed_;
|
||||
}
|
||||
|
||||
foreach (ViewerWindow* window, windows_) {
|
||||
window->queue()->lock()->unlock();
|
||||
}
|
||||
playback_queue_.lock()->unlock();
|
||||
}
|
||||
|
||||
QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
|
||||
@@ -531,12 +501,12 @@ QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
|
||||
QByteArray hash = GetConnectedNode()->video_frame_cache()->GetHash(time);
|
||||
|
||||
if (!hash.isEmpty()) {
|
||||
|
||||
return GetConnectedNode()->video_frame_cache()->CachePathName(
|
||||
hash,
|
||||
PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline));
|
||||
}
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
@@ -597,6 +567,25 @@ void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
|
||||
emit LoadedBuffer(frame.get());
|
||||
}
|
||||
|
||||
void ViewerWidget::RequestNextFrameForQueue()
|
||||
{
|
||||
rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_,
|
||||
timebase());
|
||||
|
||||
if (!FrameExistsAtTime(next_time)) {
|
||||
return;
|
||||
}
|
||||
|
||||
playback_queue_next_frame_ += playback_speed_;
|
||||
|
||||
QFutureWatcher<FramePtr>* watcher = new QFutureWatcher<FramePtr>();
|
||||
connect(watcher,
|
||||
&QFutureWatcher<FramePtr>::finished,
|
||||
this,
|
||||
&ViewerWidget::RendererGeneratedFrameForQueue);
|
||||
watcher->setFuture(renderer_->RenderFrame(next_time, false));
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateStack()
|
||||
{
|
||||
if (GetConnectedNode()
|
||||
@@ -663,8 +652,8 @@ void ViewerWidget::ContextMenuSetCustomSafeMargins()
|
||||
|
||||
QMessageBox::warning(this,
|
||||
tr("Invalid custom ratio"),
|
||||
tr("Failed to parse \"%1\" into an aspect ratio. Please format a rational "
|
||||
"fraction with a ':' or a '/' separator.").arg(s),
|
||||
tr("Failed to parse \"%1\" into an aspect ratio. Please format a "
|
||||
"rational fraction with a ':' or a '/' separator.").arg(s),
|
||||
QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
@@ -688,6 +677,18 @@ void ViewerWidget::RendererGeneratedFrame()
|
||||
SetDisplayImage(frame, false);
|
||||
}
|
||||
|
||||
void ViewerWidget::RendererGeneratedFrameForQueue()
|
||||
{
|
||||
QFutureWatcher<FramePtr>* watcher = static_cast<QFutureWatcher<FramePtr>*>(sender());
|
||||
FramePtr frame = watcher->result();
|
||||
watcher->deleteLater();
|
||||
|
||||
// Ignore this signal if we've paused now
|
||||
if (IsPlaying()) {
|
||||
playback_queue_.AppendTimewise({frame->timestamp(), frame}, playback_speed_);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateRendererParameters()
|
||||
{
|
||||
RenderMode::Mode render_mode = RenderMode::kOffline;
|
||||
@@ -990,15 +991,6 @@ void ViewerWidget::PlaybackTimerUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::RendererCachedTime(const rational &time, qint64 job_time)
|
||||
{
|
||||
if (GetTime() == time && job_time > frame_cache_job_time_) {
|
||||
frame_cache_job_time_ = job_time;
|
||||
|
||||
ForceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::SizeChangedSlot(int width, int height)
|
||||
{
|
||||
sizer_->SetChildSize(width, height);
|
||||
@@ -1034,12 +1026,11 @@ void ViewerWidget::SetZoomFromMenu(QAction *action)
|
||||
sizer_->SetZoom(action->data().toInt());
|
||||
}
|
||||
|
||||
void ViewerWidget::InvalidateVisible(NodeInput* source)
|
||||
void ViewerWidget::ViewerInvalidatedRange(const TimeRange &range)
|
||||
{
|
||||
/*
|
||||
renderer_->NodeGraphChanged(source);
|
||||
renderer_->RenderFrame(GetTime());
|
||||
*/
|
||||
if (GetTime() >= range.in() && GetTime() < range.out()) {
|
||||
ForceUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -188,6 +188,8 @@ private:
|
||||
|
||||
void SetDisplayImage(FramePtr frame, bool main_only);
|
||||
|
||||
void RequestNextFrameForQueue();
|
||||
|
||||
QStackedWidget* stack_;
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
@@ -226,8 +228,6 @@ private:
|
||||
private slots:
|
||||
void PlaybackTimerUpdate();
|
||||
|
||||
void RendererCachedTime(const rational& time, qint64 job_time);
|
||||
|
||||
void SizeChangedSlot(int width, int height);
|
||||
|
||||
void LengthChangedSlot(const rational& length);
|
||||
@@ -240,7 +240,7 @@ private slots:
|
||||
|
||||
void SetZoomFromMenu(QAction* action);
|
||||
|
||||
void InvalidateVisible(NodeInput *source);
|
||||
void ViewerInvalidatedRange(const TimeRange &range);
|
||||
|
||||
void UpdateStack();
|
||||
|
||||
@@ -258,6 +258,8 @@ private slots:
|
||||
|
||||
void RendererGeneratedFrame();
|
||||
|
||||
void RendererGeneratedFrameForQueue();
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -36,7 +36,28 @@ class ViewerQueue : public QLinkedList<ViewerPlaybackFrame> {
|
||||
public:
|
||||
ViewerQueue() = default;
|
||||
|
||||
QMutex* lock() {
|
||||
void AppendTimewise(const ViewerPlaybackFrame& f, int playback_speed)
|
||||
{
|
||||
if (this->isEmpty() || f.timestamp > this->last().timestamp == playback_speed > 0) {
|
||||
this->append(f);
|
||||
} else {
|
||||
reverse_iterator i = this->rbegin();
|
||||
|
||||
while (i != this->rend()) {
|
||||
reverse_iterator next = i + 1;
|
||||
|
||||
if (next->timestamp < f.timestamp == playback_speed > 0) {
|
||||
this->insert(i.base(), f);
|
||||
break;
|
||||
} else {
|
||||
i = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QMutex* lock()
|
||||
{
|
||||
return &queue_lock_;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user