renderer: move conform to task

Improved rendering system and user interface

Fixes #1375
This commit is contained in:
itsmattkc
2021-05-14 12:20:53 +10:00
parent a0072cbb75
commit a1345166c7
13 changed files with 159 additions and 156 deletions
+59 -23
View File
@@ -3,6 +3,7 @@
#include <QApplication>
#include <QtConcurrent/QtConcurrent>
#include "codec/conformmanager.h"
#include "node/project/project.h"
#include "render/rendermanager.h"
#include "render/renderprocessor.h"
@@ -15,7 +16,8 @@ PreviewAutoCacher::PreviewAutoCacher() :
use_custom_range_(false),
single_frame_render_(nullptr),
last_update_time_(0),
ignore_next_mouse_button_(false)
ignore_next_mouse_button_(false),
last_conform_task_(0)
{
paused_ = !Config::Current()[QStringLiteral("AutoCacheEnabled")].toBool(),
@@ -24,6 +26,8 @@ PreviewAutoCacher::PreviewAutoCacher() :
delayed_requeue_timer_.setInterval(Config::Current()[QStringLiteral("AutoCacheDelay")].toInt());
delayed_requeue_timer_.setSingleShot(true);
connect(&delayed_requeue_timer_, &QTimer::timeout, this, &PreviewAutoCacher::RequeueFrames);
connect(ConformManager::instance(), &ConformManager::ConformReady, this, &PreviewAutoCacher::ConformFinished);
}
PreviewAutoCacher::~PreviewAutoCacher()
@@ -131,7 +135,7 @@ void PreviewAutoCacher::VideoInvalidated(const TimeRange &range)
void PreviewAutoCacher::AudioInvalidated(const TimeRange &range)
{
ClearAudioQueue();
// ClearAudioQueue();
// Start jobs to re-render the audio at this range, split into 2 second chunks
invalidated_audio_.insert(range);
@@ -165,35 +169,52 @@ void PreviewAutoCacher::AudioRendered()
if (audio_tasks_.contains(watcher)) {
if (watcher->HasResult()) {
viewer_node_->audio_playback_cache()->WritePCM(audio_tasks_.value(watcher),
const TimeRange &range = audio_tasks_.value(watcher);
viewer_node_->audio_playback_cache()->WritePCM(range,
watcher->Get().value<SampleBufferPtr>(),
watcher->GetTicket()->GetJobTime());
// Retrieve visual waveforms
QVector<RenderProcessor::RenderedWaveform> waveform_list = watcher->GetTicket()->property("waveforms").value< QVector<RenderProcessor::RenderedWaveform> >();
foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) {
// Find original track
Track* track = nullptr;
bool pcm_is_usable = true;
for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) {
if (it.value() == waveform_info.track) {
track = static_cast<Track*>(it.key());
break;
}
if (watcher->GetTicket()->property("incomplete").toBool()) {
if (last_conform_task_ > watcher->GetTicket()->GetJobTime()) {
// Requeue now
viewer_node_->audio_playback_cache()->Invalidate(range, QDateTime::currentMSecsSinceEpoch());
pcm_is_usable = false;
} else {
// Wait for conform
audio_needing_conform_.insert(range);
}
}
if (track) {
QList<TimeRange> valid_ranges = viewer_node_->audio_playback_cache()->GetValidRanges(waveform_info.range,
watcher->GetTicket()->GetJobTime());
if (!valid_ranges.isEmpty()) {
// Generate visual waveform in this background thread
track->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count());
if (pcm_is_usable) {
// Retrieve visual waveforms
QVector<RenderProcessor::RenderedWaveform> waveform_list = watcher->GetTicket()->property("waveforms").value< QVector<RenderProcessor::RenderedWaveform> >();
foreach (const RenderProcessor::RenderedWaveform& waveform_info, waveform_list) {
// Find original track
Track* track = nullptr;
foreach (const TimeRange& r, valid_ranges) {
track->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
for (auto it=copy_map_.cbegin(); it!=copy_map_.cend(); it++) {
if (it.value() == waveform_info.track) {
track = static_cast<Track*>(it.key());
break;
}
}
emit track->PreviewChanged();
if (track) {
QList<TimeRange> valid_ranges = viewer_node_->audio_playback_cache()->GetValidRanges(waveform_info.range,
watcher->GetTicket()->GetJobTime());
if (!valid_ranges.isEmpty()) {
// Generate visual waveform in this background thread
track->waveform().set_channel_count(viewer_node_->GetAudioParams().channel_count());
foreach (const TimeRange& r, valid_ranges) {
track->waveform().OverwriteSums(waveform_info.waveform, r.in(), r.in() - waveform_info.range.in(), r.length());
}
emit track->PreviewChanged();
}
}
}
}
@@ -491,7 +512,7 @@ void PreviewAutoCacher::TryRender()
RenderTicketWatcher* watcher = new RenderTicketWatcher();
connect(watcher, &RenderTicketWatcher::Finished, this, &PreviewAutoCacher::AudioRendered);
audio_tasks_.insert(watcher, r);
watcher->SetTicket(RenderManager::instance()->RenderAudio(copied_viewer_node_, r, true));
watcher->SetTicket(RenderManager::instance()->RenderAudio(copied_viewer_node_, r, RenderMode::kOffline, true));
}
}
@@ -581,6 +602,18 @@ void PreviewAutoCacher::RequeueFrames()
}
}
void PreviewAutoCacher::ConformFinished()
{
last_conform_task_ = QDateTime::currentMSecsSinceEpoch();
if (viewer_node_) {
foreach (const TimeRange &range, audio_needing_conform_) {
viewer_node_->audio_playback_cache()->Invalidate(range, QDateTime::currentMSecsSinceEpoch());
}
audio_needing_conform_.clear();
}
}
void PreviewAutoCacher::IgnoreNextMouseButton()
{
ignore_next_mouse_button_ = true;
@@ -624,6 +657,9 @@ void PreviewAutoCacher::SetViewerNode(ViewerOutput *viewer_node)
// No more immediate passthroughts
video_immediate_passthroughs_.clear();
// No more audio conforms
audio_needing_conform_.clear();
// Delete all of our copied nodes
qDeleteAll(created_nodes_);
created_nodes_.clear();
+6
View File
@@ -167,6 +167,10 @@ private:
QTimer delayed_requeue_timer_;
TimeRangeList audio_needing_conform_;
qint64 last_conform_task_;
private slots:
/**
* @brief Handler for when the NodeGraph reports a video change over a certain time range
@@ -213,6 +217,8 @@ private slots:
*/
void RequeueFrames();
void ConformFinished();
};
}
+4 -3
View File
@@ -181,12 +181,12 @@ RenderTicketPtr RenderManager::RenderFrame(ViewerOutput *viewer, ColorManager* c
return ticket;
}
RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange& r, bool generate_waveforms, bool prioritize)
RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange& r, RenderMode::Mode mode, bool generate_waveforms, bool prioritize)
{
return RenderAudio(viewer, r, viewer->GetAudioParams(), generate_waveforms, prioritize);
return RenderAudio(viewer, r, viewer->GetAudioParams(), mode, generate_waveforms, prioritize);
}
RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange &r, const AudioParams &params, bool generate_waveforms, bool prioritize)
RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange &r, const AudioParams &params, RenderMode::Mode mode, bool generate_waveforms, bool prioritize)
{
// Create ticket
RenderTicketPtr ticket = std::make_shared<RenderTicket>();
@@ -194,6 +194,7 @@ RenderTicketPtr RenderManager::RenderAudio(ViewerOutput* viewer, const TimeRange
ticket->setProperty("viewer", Node::PtrToValue(viewer));
ticket->setProperty("time", QVariant::fromValue(r));
ticket->setProperty("type", kTypeAudio);
ticket->setProperty("mode", mode);
ticket->setProperty("enablewaveforms", generate_waveforms);
ticket->setProperty("aparam", QVariant::fromValue(params));
+2 -2
View File
@@ -105,8 +105,8 @@ public:
*
* This function is thread-safe.
*/
RenderTicketPtr RenderAudio(ViewerOutput* viewer, const TimeRange& r, const AudioParams& params, bool generate_waveforms, bool prioritize = false);
RenderTicketPtr RenderAudio(ViewerOutput *viewer, const TimeRange& r, bool generate_waveforms, bool prioritize = false);
RenderTicketPtr RenderAudio(ViewerOutput* viewer, const TimeRange& r, const AudioParams& params, RenderMode::Mode mode, bool generate_waveforms, bool prioritize = false);
RenderTicketPtr RenderAudio(ViewerOutput *viewer, const TimeRange& r, RenderMode::Mode mode, bool generate_waveforms, bool prioritize = false);
RenderTicketPtr SaveFrameToCache(FrameHashCache* cache, FramePtr frame, const QByteArray& hash, bool prioritize = false);
+8 -6
View File
@@ -443,13 +443,15 @@ QVariant RenderProcessor::ProcessAudioFootage(const FootageJob &stream, const Ti
if (decoder) {
const AudioParams& audio_params = ticket_->property("aparam").value<AudioParams>();
SampleBufferPtr frame = decoder->RetrieveAudio(input_time, audio_params,
stream.cache_path(),
stream.loop_mode(),
&IsCancelled());
Decoder::RetrieveAudioData status = decoder->RetrieveAudio(input_time, audio_params,
stream.cache_path(),
stream.loop_mode(),
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()));
if (frame) {
value = QVariant::fromValue(frame);
if (status.status == Decoder::kOK && status.samples) {
value = QVariant::fromValue(status.samples);
} else if (status.status == Decoder::kWaitingForConform) {
ticket_->setProperty("incomplete", true);
}
}