renderer: move waveform generation into worker and mutex the waveform

Addresses scheduling issues where a backend might be closed (or even
destroyed) before it processes a waveform signal from a worker. Requires
extra multithreading code.
This commit is contained in:
itsmattkc
2020-06-05 15:18:14 +10:00
parent ddf63b39e1
commit de3288893d
7 changed files with 59 additions and 51 deletions
+6
View File
@@ -199,6 +199,11 @@ public:
return waveform_;
}
QMutex* waveform_lock()
{
return &waveform_lock_;
}
public slots:
void SetTrackName(const QString& name);
@@ -276,6 +281,7 @@ private:
rational queued_length_;
AudioVisualWaveform waveform_;
QMutex waveform_lock_;
private slots:
void BlockConnected(NodeEdgePtr edge);
+5 -34
View File
@@ -34,7 +34,8 @@ OLIVE_NAMESPACE_ENTER
RenderBackend::RenderBackend(QObject *parent) :
QObject(parent),
viewer_node_(nullptr),
update_with_graph_(false)
update_with_graph_(false),
preview_job_time_(0)
{
}
@@ -270,7 +271,6 @@ void RenderBackend::RunNextJob()
RenderWorker* worker = CreateNewWorker();
connect(worker, &RenderWorker::FinishedJob, this, &RenderBackend::WorkerFinished);
connect(worker, &RenderWorker::WaveformGenerated, this, &RenderBackend::WorkerGeneratedWaveform);
workers_.replace(i, {worker, false});
}
@@ -288,7 +288,9 @@ void RenderBackend::RunNextJob()
worker->SetVideoParams(video_params_);
worker->SetAudioParams(audio_params_);
worker->SetVideoDownloadMatrix(video_download_matrix_);
worker->SetGeneratePreviews(generate_previews_);
if (preview_job_time_) {
worker->EnablePreviewGeneration(viewer_node_->audio_playback_cache(), preview_job_time_);
}
worker->SetCopyMap(&copy_map_);
RenderTicketPtr ticket = render_queue_.front();
@@ -351,37 +353,6 @@ void RenderBackend::WorkerFinished()
}
}
void RenderBackend::WorkerGeneratedWaveform(const TrackOutput *copied_track, const AudioVisualWaveform& samples, const rational &r)
{
if (!viewer_node_) {
return;
}
TrackOutput* track = nullptr;
/*
if (!viewer_node_->audio_playback_cache()->JobIsCurrent(r)) {
return;
}
*/
QHash<Node*, Node*>::const_iterator i;
for (i=copy_map_.constBegin(); i!=copy_map_.constEnd(); i++) {
if (i.value() == copied_track) {
track = static_cast<TrackOutput*>(i.key());
break;
}
}
if (track) {
track->waveform().set_channel_count(audio_params_.channel_count());
track->waveform().OverwriteSums(samples, r);
emit track->PreviewChanged();
} else {
qDebug() << "Failed to find track";
}
}
void RenderBackend::CopyNodeInputValue(NodeInput *input)
{
// Find our copy of this parameter
+3 -5
View File
@@ -50,9 +50,9 @@ public:
update_with_graph_ = e;
}
void SetGeneratePreviews(bool e)
void EnablePreviewGeneration(qint64 job_time)
{
generate_previews_ = e;
preview_job_time_ = job_time;
}
void ClearVideoQueue();
@@ -121,13 +121,11 @@ private:
bool update_with_graph_;
bool generate_previews_;
qint64 preview_job_time_;
private slots:
void WorkerFinished();
void WorkerGeneratedWaveform(const OLIVE_NAMESPACE::TrackOutput* copied_track, const OLIVE_NAMESPACE::AudioVisualWaveform& samples, const OLIVE_NAMESPACE::rational& start);
};
OLIVE_NAMESPACE_EXIT
+36 -7
View File
@@ -35,7 +35,7 @@ RenderWorker::RenderWorker(RenderBackend* parent) :
parent_(parent),
available_(true),
audio_mode_is_preview_(false),
generate_previews_(false)
preview_cache_(nullptr)
{
}
@@ -127,12 +127,41 @@ NodeValueTable RenderWorker::GenerateBlockTable(const TrackOutput *track, const
NodeValueTable::Merge({merged_table, table});
}
if (generate_previews_) {
// Generate visual waveform in this background thread
AudioVisualWaveform visual_waveform;
visual_waveform.set_channel_count(audio_params_.channel_count());
visual_waveform.AddSamples(block_range_buffer, audio_params_.sample_rate());
emit WaveformGenerated(track, visual_waveform, range.in());
if (preview_cache_) {
// Find original track object
TrackOutput* original_track = nullptr;
QList<TimeRange> valid_ranges = preview_cache_->GetValidRanges(range, preview_job_time_);
if (!valid_ranges.isEmpty()) {
qDebug() << "Worker generated valid waveform for" << range << preview_cache_;
QHash<Node*, Node*>::const_iterator i;
for (i=copy_map_->constBegin(); i!=copy_map_->constEnd(); i++) {
if (i.value() == track) {
original_track = static_cast<TrackOutput*>(i.key());
break;
}
}
// Generate visual waveform in this background thread
if (original_track) {
AudioVisualWaveform visual_waveform;
visual_waveform.set_channel_count(audio_params_.channel_count());
visual_waveform.AddSamples(block_range_buffer, audio_params_.sample_rate());
original_track->waveform_lock()->lock();
original_track->waveform().set_channel_count(audio_params_.channel_count());
foreach (const TimeRange& r, valid_ranges) {
original_track->waveform().OverwriteSums(visual_waveform, r.in(), r.in() - range.in(), r.length());
}
original_track->waveform_lock()->unlock();
emit original_track->PreviewChanged();
}
}
}
merged_table.Push(NodeParam::kSamples, QVariant::fromValue(block_range_buffer));
+6 -4
View File
@@ -73,9 +73,10 @@ public:
copy_map_ = copy_map;
}
void SetGeneratePreviews(bool e)
void EnablePreviewGeneration(AudioPlaybackCache* cache, qint64 job_time)
{
generate_previews_ = e;
preview_cache_ = cache;
preview_job_time_ = job_time;
}
/**
@@ -132,7 +133,7 @@ signals:
void FinishedJob();
void WaveformGenerated(const OLIVE_NAMESPACE::TrackOutput* track, const OLIVE_NAMESPACE::AudioVisualWaveform& samples, const OLIVE_NAMESPACE::rational& start);
void WaveformGenerated(OLIVE_NAMESPACE::TrackOutput* track, OLIVE_NAMESPACE::AudioVisualWaveform samples, OLIVE_NAMESPACE::TimeRange start);
private:
NodeValue GetDataFromStream(StreamPtr stream, const TimeRange& input_time);
@@ -164,7 +165,8 @@ private:
bool audio_mode_is_preview_;
bool generate_previews_;
AudioPlaybackCache* preview_cache_;
qint64 preview_job_time_;
QHash<Node*, Node*>* copy_map_;
+1 -1
View File
@@ -33,7 +33,7 @@ CacheTask::CacheTask(ViewerOutput* viewer, const VideoRenderingParams& vparams,
{
SetTitle(tr("Caching \"%1\"").arg(viewer->media_name()));
backend()->SetGeneratePreviews(true);
backend()->EnablePreviewGeneration(job_time());
}
bool CacheTask::Run()
@@ -99,6 +99,8 @@ void TimelineViewBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsI
painter->setPen(QColor(64, 64, 64));
TrackOutput* track = TrackOutput::TrackFromBlock(block_);
if (track) {
QMutexLocker locker(track->waveform_lock());
AudioVisualWaveform::DrawWaveform(painter,
rect().toRect(),
this->GetScale(),