renderer/viewer: fixed playback and live updates from value changes

This commit is contained in:
itsmattkc
2020-05-15 15:02:30 +10:00
parent 8795e51452
commit 2039067fdc
17 changed files with 146 additions and 148 deletions
-4
View File
@@ -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()
-2
View File
@@ -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()
+4 -4
View File
@@ -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()
-13
View File
@@ -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
-5
View File
@@ -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.
*
-9
View File
@@ -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;
-3
View File
@@ -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);
+9
View File
@@ -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 &params)
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();
+13 -6
View File
@@ -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
+2
View File
@@ -109,6 +109,8 @@ private:
private slots:
void AudioCallback();
void WorkerFinished();
};
OLIVE_NAMESPACE_EXIT
+5 -5
View File
@@ -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;
}
+3 -1
View File
@@ -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);
+10 -10
View File
@@ -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()) {
+72 -81
View File
@@ -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
+5 -3
View File
@@ -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
+22 -1
View File
@@ -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_;
}