render: improved overall flow
This commit is contained in:
@@ -115,6 +115,17 @@ public:
|
||||
|
||||
bool contains(const TimeRange& range, bool in_inclusive = true, bool out_inclusive = true) const;
|
||||
|
||||
bool contains(const rational &r) const
|
||||
{
|
||||
for (const TimeRange &range : array_) {
|
||||
if (range.Contains(r)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isEmpty() const
|
||||
{
|
||||
return array_.isEmpty();
|
||||
|
||||
@@ -260,6 +260,7 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu
|
||||
|
||||
NodeTraverser::NodeTraverser() :
|
||||
cancel_(nullptr),
|
||||
heard_cancel_(false),
|
||||
transform_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -128,11 +128,17 @@ protected:
|
||||
|
||||
QVector2D GenerateResolution() const;
|
||||
|
||||
bool IsCancelled() const
|
||||
bool IsCancelled()
|
||||
{
|
||||
return cancel_ && *cancel_;
|
||||
bool c = cancel_ && *cancel_;
|
||||
if (c) {
|
||||
heard_cancel_ = true;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
bool HeardCancel() const { return heard_cancel_; }
|
||||
|
||||
const QAtomicInt *GetCancelPointer() const
|
||||
{
|
||||
return cancel_;
|
||||
@@ -155,6 +161,7 @@ private:
|
||||
AudioParams audio_params_;
|
||||
|
||||
const QAtomicInt *cancel_;
|
||||
bool heard_cancel_;
|
||||
|
||||
const Node *transform_start_;
|
||||
const Node *transform_now_;
|
||||
|
||||
@@ -49,16 +49,19 @@ public:
|
||||
void ValidateTimestamp(const int64_t &ts);
|
||||
void ValidateTime(const rational &time);
|
||||
|
||||
/**
|
||||
* @brief Return the path of the cached image at this time
|
||||
*/
|
||||
QString CachePathName(const int64_t &time) const;
|
||||
QString CachePathName(const rational &time) const
|
||||
bool IsFrameCached(const rational &time) const
|
||||
{
|
||||
return CachePathName(ToTimestamp(time, Timecode::kFloor));
|
||||
return GetValidatedRanges().contains(time);
|
||||
}
|
||||
|
||||
static QString CachePathName(const QString& cache_path, const QUuid &cache_id, const int64_t &time);
|
||||
QString GetValidCacheFilename(const rational &time) const
|
||||
{
|
||||
if (IsFrameCached(time)) {
|
||||
return CachePathName(time);
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
static bool SaveCacheFrame(const QString& filename, FramePtr frame);
|
||||
bool SaveCacheFrame(const int64_t &time, FramePtr frame) const;
|
||||
@@ -71,6 +74,17 @@ private:
|
||||
rational ToTime(const int64_t &ts) const;
|
||||
int64_t ToTimestamp(const rational &ts, Timecode::Rounding rounding = Timecode::kRound) const;
|
||||
|
||||
/**
|
||||
* @brief Return the path of the cached image at this time
|
||||
*/
|
||||
QString CachePathName(const int64_t &time) const;
|
||||
QString CachePathName(const rational &time) const
|
||||
{
|
||||
return CachePathName(ToTimestamp(time, Timecode::kRound));
|
||||
}
|
||||
|
||||
static QString CachePathName(const QString& cache_path, const QUuid &cache_id, const int64_t &time);
|
||||
|
||||
rational timebase_;
|
||||
|
||||
QUuid uuid_;
|
||||
|
||||
@@ -59,6 +59,8 @@ public:
|
||||
|
||||
void Invalidate(const TimeRange& r, bool signal = true);
|
||||
|
||||
const TimeRangeList &GetValidatedRanges() const { return validated_; }
|
||||
|
||||
public slots:
|
||||
void InvalidateAll();
|
||||
|
||||
|
||||
@@ -429,7 +429,7 @@ void PreviewAutoCacher::StartCachingAudioRange(const TimeRange &range)
|
||||
void PreviewAutoCacher::SetPlayhead(const rational &playhead)
|
||||
{
|
||||
cache_range_ = TimeRange(playhead - OLIVE_CONFIG("DiskCacheBehind").value<rational>(),
|
||||
playhead + OLIVE_CONFIG("DiskCacheAhead").value<rational>());
|
||||
playhead + OLIVE_CONFIG("DiskCacheAhead").value<rational>());
|
||||
|
||||
RequeueFrames();
|
||||
}
|
||||
@@ -546,19 +546,10 @@ void PreviewAutoCacher::TryRender()
|
||||
|
||||
if (single_frame_render_) {
|
||||
// Check if already caching this
|
||||
rational time = single_frame_render_->property("time").value<rational>();
|
||||
RenderTicketWatcher* watcher;
|
||||
if ((watcher = video_tasks_.key(time))) {
|
||||
video_immediate_passthroughs_[watcher].append(single_frame_render_);
|
||||
} else if ((watcher = video_download_tasks_.key(time))) {
|
||||
single_frame_render_->Finish(watcher->property("frame"));
|
||||
} else {
|
||||
watcher = RenderFrame(single_frame_render_->property("time").value<rational>(),
|
||||
RenderTicketPriority(single_frame_render_->property("priority").toInt()),
|
||||
!viewer_node_->GetVideoAutoCacheEnabled());
|
||||
|
||||
video_immediate_passthroughs_[watcher].append(single_frame_render_);
|
||||
}
|
||||
RenderTicketWatcher *watcher = RenderFrame(single_frame_render_->property("time").value<rational>(),
|
||||
RenderTicketPriority(single_frame_render_->property("priority").toInt()),
|
||||
!viewer_node_->GetVideoAutoCacheEnabled());
|
||||
video_immediate_passthroughs_[watcher].append(single_frame_render_);
|
||||
|
||||
single_frame_render_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ void RenderProcessor::Run()
|
||||
texture = render_ctx_->InterlaceTexture(top, bottom, GetCacheVideoParams());
|
||||
}
|
||||
|
||||
if (ticket_->IsCancelled()) {
|
||||
if (HeardCancel()) {
|
||||
// Finish cancelled ticket with nothing since we can't guarantee the frame we generated
|
||||
// is actually "complete
|
||||
ticket_->Finish();
|
||||
@@ -208,7 +208,7 @@ void RenderProcessor::Run()
|
||||
ticket_->setProperty("waveform", QVariant::fromValue(vis));
|
||||
}
|
||||
|
||||
if (ticket_->IsCancelled()) {
|
||||
if (HeardCancel()) {
|
||||
ticket_->Finish();
|
||||
} else {
|
||||
ticket_->Finish(QVariant::fromValue(samples));
|
||||
|
||||
@@ -420,7 +420,14 @@ FramePtr ViewerWidget::DecodeCachedImage(const QString &cache_path, const QUuid
|
||||
void ViewerWidget::DecodeCachedImage(RenderTicketPtr ticket, const QString &cache_path, const QUuid &cache_id, const int64_t& time)
|
||||
{
|
||||
ticket->Start();
|
||||
ticket->Finish(QVariant::fromValue(DecodeCachedImage(cache_path, cache_id, time)));
|
||||
|
||||
FramePtr f = DecodeCachedImage(cache_path, cache_id, time);
|
||||
|
||||
if (f) {
|
||||
ticket->Finish(QVariant::fromValue(f));
|
||||
} else {
|
||||
ticket->Finish();
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewerWidget::ShouldForceWaveform() const
|
||||
@@ -606,14 +613,6 @@ void ViewerWidget::ReceivedAudioBufferForScrubbing()
|
||||
SampleBuffer samples = watcher->Get().value<SampleBuffer>();
|
||||
if (samples.is_allocated()) {
|
||||
if (samples.audio_params().channel_count() > 0) {
|
||||
/* Fade code
|
||||
const int kFadeSz = qMin(200, samples->sample_count()/4);
|
||||
for (int i=0; i<kFadeSz; i++) {
|
||||
float amt = float(i)/float(kFadeSz);
|
||||
samples->transform_volume_for_sample(i, amt);
|
||||
samples->transform_volume_for_sample(samples->sample_count() - i - 1, amt);
|
||||
}*/
|
||||
|
||||
AudioProcessor::Buffer buf;
|
||||
int r = audio_processor_.Convert(samples.to_raw_ptrs().data(), samples.sample_count(), &buf);
|
||||
|
||||
@@ -880,7 +879,7 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp
|
||||
QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
|
||||
{
|
||||
if (FrameExistsAtTime(time)) {
|
||||
return GetConnectedNode()->video_frame_cache()->CachePathName(time);
|
||||
return GetConnectedNode()->video_frame_cache()->GetValidCacheFilename(time);
|
||||
}
|
||||
|
||||
return QString();
|
||||
@@ -923,7 +922,7 @@ void ViewerWidget::RequestNextFrameForQueue(RenderTicketPriority priority, bool
|
||||
|
||||
RenderTicketPtr ViewerWidget::GetFrame(const rational &t, RenderTicketPriority priority)
|
||||
{
|
||||
QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(t);
|
||||
QString cache_fn = GetConnectedNode()->video_frame_cache()->GetValidCacheFilename(t);
|
||||
|
||||
if (!QFileInfo::exists(cache_fn)) {
|
||||
// Frame hasn't been cached, start render job
|
||||
@@ -1062,17 +1061,7 @@ void ViewerWidget::RendererGeneratedFrame()
|
||||
}
|
||||
}
|
||||
|
||||
// If the frame we received is not the most recent frame we're waiting for (i.e. if nonqueue_watchers_
|
||||
// is not empty), we discard the frame - because otherwise if frames are taking a while (i.e.
|
||||
// uncached frames) it can be a little disconcerting to the user for a bunch of frames to
|
||||
// slowly go by - UNLESS the time it took to make this frame was fairly short (under 100ms here),
|
||||
// in which case, we DO show it because it can be disconcerting in the other direction to see
|
||||
// a scrub just jump to the final frame without seeing the frames in between. It's just a
|
||||
// little nicer to get to see that in that situation, so we handle both.
|
||||
qint64 frame_start_time = ticket->property("start").value<qint64>();
|
||||
if (nonqueue_watchers_.isEmpty() || (QDateTime::currentMSecsSinceEpoch() - frame_start_time < 100)) {
|
||||
SetDisplayImage(ticket->Get());
|
||||
}
|
||||
SetDisplayImage(ticket->Get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user