added second frame cache for thumbnails
This commit is contained in:
@@ -187,7 +187,7 @@ void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int
|
||||
if (Node *connected = GetConnectedOutput(from, element)) {
|
||||
TimeRange max_range = InputTimeAdjustment(from, element, TimeRange(0, length()));
|
||||
if (type == Track::kVideo) {
|
||||
emit connected->video_frame_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly);
|
||||
emit connected->thumbnail_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly);
|
||||
} else if (type == Track::kAudio) {
|
||||
emit connected->audio_playback_cache()->Request(range.Intersected(max_range), PlaybackCache::kPreviewsOnly);
|
||||
}
|
||||
@@ -250,7 +250,7 @@ void ClipBlock::InputConnectedEvent(const QString &input, int element, Node *out
|
||||
super::InputConnectedEvent(input, element, output);
|
||||
|
||||
if (input == kBufferIn) {
|
||||
connect(output->video_frame_cache(), &FrameHashCache::ThumbnailsUpdated, this, &Block::PreviewChanged);
|
||||
connect(output->thumbnail_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged);
|
||||
connect(output->audio_playback_cache(), &AudioPlaybackCache::WaveformUpdated, this, &Block::PreviewChanged);
|
||||
}
|
||||
}
|
||||
@@ -260,7 +260,7 @@ void ClipBlock::InputDisconnectedEvent(const QString &input, int element, Node *
|
||||
super::InputDisconnectedEvent(input, element, output);
|
||||
|
||||
if (input == kBufferIn) {
|
||||
disconnect(output->video_frame_cache(), &FrameHashCache::ThumbnailsUpdated, this, &Block::PreviewChanged);
|
||||
disconnect(output->thumbnail_cache(), &FrameHashCache::Validated, this, &Block::PreviewChanged);
|
||||
disconnect(output->audio_playback_cache(), &AudioPlaybackCache::WaveformUpdated, this, &Block::PreviewChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
const FrameHashCache *thumbnails()
|
||||
{
|
||||
if (Node *n = GetConnectedOutput(kBufferIn)) {
|
||||
return n->video_frame_cache();
|
||||
return n->thumbnail_cache();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ Node::Node() :
|
||||
AddInput(kEnabledInput, NodeValue::kBoolean, true);
|
||||
|
||||
video_cache_ = new FrameHashCache(this);
|
||||
thumbnail_cache_ = new FrameHashCache(this);
|
||||
audio_cache_ = new AudioPlaybackCache(this);
|
||||
}
|
||||
|
||||
@@ -936,6 +937,7 @@ void Node::InvalidateCache(const TimeRange &range, const QString &from, int elem
|
||||
TimeRange vr = range.Intersected(GetVideoCacheRange());
|
||||
if (vr.length() != 0) {
|
||||
video_frame_cache()->Invalidate(vr);
|
||||
thumbnail_cache()->Invalidate(vr);
|
||||
}
|
||||
TimeRange ar = range.Intersected(GetAudioCacheRange());
|
||||
if (ar.length() != 0) {
|
||||
|
||||
@@ -226,6 +226,11 @@ public:
|
||||
return video_cache_;
|
||||
}
|
||||
|
||||
FrameHashCache* thumbnail_cache() const
|
||||
{
|
||||
return thumbnail_cache_;
|
||||
}
|
||||
|
||||
AudioPlaybackCache* audio_playback_cache() const
|
||||
{
|
||||
return audio_cache_;
|
||||
@@ -1402,6 +1407,7 @@ private:
|
||||
QString effect_input_;
|
||||
|
||||
FrameHashCache *video_cache_;
|
||||
FrameHashCache *thumbnail_cache_;
|
||||
|
||||
AudioPlaybackCache *audio_cache_;
|
||||
|
||||
|
||||
@@ -340,47 +340,6 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, const FramePtr fram
|
||||
QImage img(reinterpret_cast<const uchar*>(frame->data()), frame->width(), frame->height(), frame->linesize_bytes(), fmt);
|
||||
|
||||
return img.save(filename, "jpg");
|
||||
|
||||
|
||||
/*
|
||||
qDebug() << "hello?" << filename;
|
||||
|
||||
// Integer types are stored in JPG
|
||||
QString tmp = filename;
|
||||
tmp.append(QStringLiteral(".jpg"));
|
||||
|
||||
std::string tmp_std = tmp.toStdString();
|
||||
auto out = OIIO::ImageOutput::create(tmp_std);
|
||||
if (!out) {
|
||||
qDebug() << "fail create";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto fmt = OIIOUtils::GetOIIOBaseTypeFromFormat(frame->format());
|
||||
qDebug() << "writing" << fmt;
|
||||
if (!out->open(tmp_std, OIIO::ImageSpec(frame->width(), frame->height(), frame->channel_count(), fmt))) {
|
||||
qDebug() << "fail open";
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = out->write_image(fmt, frame->data(), OIIO::AutoStride, frame->linesize_bytes());
|
||||
out->close();
|
||||
|
||||
if (ret) {
|
||||
QFile f(filename);
|
||||
if (f.exists()) {
|
||||
f.remove();
|
||||
}
|
||||
ret = QFile::rename(tmp, filename);
|
||||
if (!ret) {
|
||||
qDebug() << "fail rename from" << tmp << "to" << filename;
|
||||
}
|
||||
} else {
|
||||
qDebug() << "fail write";
|
||||
}
|
||||
|
||||
return ret;
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,9 +65,6 @@ public:
|
||||
FramePtr LoadCacheFrame(const int64_t &time) const;
|
||||
static FramePtr LoadCacheFrame(const QString& fn);
|
||||
|
||||
signals:
|
||||
void ThumbnailsUpdated();
|
||||
|
||||
private:
|
||||
rational ToTime(const int64_t &ts) const;
|
||||
int64_t ToTimestamp(const rational &ts, Timecode::Rounding rounding = Timecode::kRound) const;
|
||||
|
||||
@@ -83,11 +83,18 @@ RenderTicketPtr PreviewAutoCacher::GetRangeOfAudio(TimeRange range, RenderTicket
|
||||
return RenderAudio(copied_viewer_node_->GetConnectedSampleOutput(), range, PlaybackCache::kCacheOnly, priority);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::VideoInvalidatedFromCache(const TimeRange &range, PlaybackCache::RequestType type)
|
||||
void PreviewAutoCacher::VideoInvalidatedFromCache(const TimeRange &range)
|
||||
{
|
||||
FrameHashCache *cache = static_cast<FrameHashCache*>(sender());
|
||||
|
||||
VideoInvalidatedFromNode(cache->parent(), range, type);
|
||||
VideoInvalidatedFromNode(cache->parent(), range, PlaybackCache::kCacheOnly);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::ThumbnailsInvalidatedFromCache(const TimeRange &range)
|
||||
{
|
||||
FrameHashCache *cache = static_cast<FrameHashCache*>(sender());
|
||||
|
||||
VideoInvalidatedFromNode(cache->parent(), range, PlaybackCache::kPreviewsOnly);
|
||||
}
|
||||
|
||||
void PreviewAutoCacher::AudioInvalidatedFromCache(const TimeRange &range, PlaybackCache::RequestType type)
|
||||
@@ -162,17 +169,9 @@ void PreviewAutoCacher::VideoRendered()
|
||||
if (it != video_tasks_.end()) {
|
||||
// Assume that a "result" is a fully completed image and a non-result is a cancelled ticket
|
||||
if (watcher->HasResult()) {
|
||||
PlaybackCache::RequestType type = PlaybackCache::RequestType(watcher->property("type").toInt());
|
||||
|
||||
if (type == PlaybackCache::kCacheOnly) {
|
||||
if (watcher->GetTicket()->property("cached").toBool()) {
|
||||
if (FrameHashCache *cache = Node::ValueToPtr<FrameHashCache>(watcher->property("cache"))) {
|
||||
cache->ValidateTime(it.value());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (watcher->GetTicket()->property("cached").toBool()) {
|
||||
if (FrameHashCache *cache = Node::ValueToPtr<FrameHashCache>(watcher->property("cache"))) {
|
||||
emit cache->ThumbnailsUpdated();
|
||||
cache->ValidateTime(it.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -336,6 +335,11 @@ void PreviewAutoCacher::ConnectToNodeCache(Node *node)
|
||||
this,
|
||||
&PreviewAutoCacher::VideoInvalidatedFromCache);
|
||||
|
||||
connect(node->thumbnail_cache(),
|
||||
&PlaybackCache::Request,
|
||||
this,
|
||||
&PreviewAutoCacher::ThumbnailsInvalidatedFromCache);
|
||||
|
||||
connect(node->audio_playback_cache(),
|
||||
&PlaybackCache::Request,
|
||||
this,
|
||||
@@ -368,6 +372,11 @@ void PreviewAutoCacher::DisconnectFromNodeCache(Node *node)
|
||||
this,
|
||||
&PreviewAutoCacher::VideoInvalidatedFromCache);
|
||||
|
||||
disconnect(node->thumbnail_cache(),
|
||||
&PlaybackCache::Request,
|
||||
this,
|
||||
&PreviewAutoCacher::ThumbnailsInvalidatedFromCache);
|
||||
|
||||
disconnect(node->audio_playback_cache(),
|
||||
&PlaybackCache::Request,
|
||||
this,
|
||||
@@ -606,7 +615,15 @@ void PreviewAutoCacher::TryRender()
|
||||
// We want this hash, if we're not already rendering, start render now
|
||||
if (!render_task) {
|
||||
// Don't render any hash more than once
|
||||
RenderFrame(copy, t, d.type, RenderTicketPriority::kNormal, d.node->video_frame_cache());
|
||||
FrameHashCache *using_cache;
|
||||
|
||||
if (d.type == PlaybackCache::kCacheOnly) {
|
||||
using_cache = d.node->video_frame_cache();
|
||||
} else {
|
||||
using_cache = d.node->thumbnail_cache();
|
||||
}
|
||||
|
||||
RenderFrame(copy, t, d.type, RenderTicketPriority::kNormal, using_cache);
|
||||
}
|
||||
|
||||
emit SignalCacheProxyTaskProgress(double(d.iterator.frame_index()) / double(d.iterator.size()));
|
||||
@@ -658,14 +675,17 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
|
||||
copied_color_manager_);
|
||||
|
||||
if (cache) {
|
||||
cache->SetTimebase(viewer_node_->GetVideoParams().frame_rate_as_time_base());
|
||||
rvp.AddCache(cache);
|
||||
|
||||
if (type == PlaybackCache::kPreviewsOnly) {
|
||||
rvp.video_params.set_divider(VideoParams::GetDividerForTargetResolution(rvp.video_params.width(), rvp.video_params.height(), 160, 120));
|
||||
rvp.force_color_output = display_color_processor_;
|
||||
rvp.force_format = VideoParams::kFormatUnsigned8;
|
||||
|
||||
cache->SetTimebase(rational(1, 10));
|
||||
} else {
|
||||
cache->SetTimebase(viewer_node_->GetVideoParams().frame_rate_as_time_base());
|
||||
}
|
||||
|
||||
rvp.AddCache(cache);
|
||||
}
|
||||
|
||||
rvp.priority = priority;
|
||||
|
||||
@@ -227,7 +227,8 @@ private slots:
|
||||
/**
|
||||
* @brief Handler for when the NodeGraph reports a video change over a certain time range
|
||||
*/
|
||||
void VideoInvalidatedFromCache(const olive::TimeRange &range, olive::PlaybackCache::RequestType type);
|
||||
void VideoInvalidatedFromCache(const olive::TimeRange &range);
|
||||
void ThumbnailsInvalidatedFromCache(const olive::TimeRange &range);
|
||||
|
||||
/**
|
||||
* @brief Handler for when the NodeGraph reports a audio change over a certain time range
|
||||
|
||||
@@ -64,6 +64,9 @@ rational TimeScaledObject::SceneToTime(const double &x, const double &x_scale, c
|
||||
|
||||
if (round) {
|
||||
rounded_x_mvmt = qRound64(unscaled_time);
|
||||
} else if (unscaled_time < 0) {
|
||||
// "floor" to zero
|
||||
rounded_x_mvmt = qCeil(unscaled_time);
|
||||
} else {
|
||||
rounded_x_mvmt = qFloor(unscaled_time);
|
||||
}
|
||||
|
||||
@@ -529,11 +529,20 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
|
||||
painter->setClipRect(preview_rect);
|
||||
painter->setRenderHint(QPainter::SmoothPixmapTransform);
|
||||
for (int i=preview_rect.left(); i<preview_rect.right(); i+=thumb_rect.width()+1) {
|
||||
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base()) + media_in;
|
||||
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetVideoParams().frame_rate_as_time_base()) + media_in;
|
||||
QString thumbnail = thumbs->GetValidCacheFilename(time_here);
|
||||
|
||||
if (thumbnail.isEmpty()) {
|
||||
break;
|
||||
// Jump ahead to next frame, ensuring that frame width > 0 for optimization
|
||||
if (thumb_rect.width() == 0 && clip->track() && clip->track()->sequence()) {
|
||||
Sequence *s = clip->track()->sequence();
|
||||
int width = s->GetVideoParams().width();
|
||||
int height = s->GetVideoParams().height();
|
||||
if (height > 0) { // Prevent divide by zero/invalid params
|
||||
double scale = double(preview_rect.height())/double(height);
|
||||
thumb_rect.setWidth(width * scale);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QImage img;
|
||||
if (img.load(thumbnail, "jpg")) {
|
||||
|
||||
Reference in New Issue
Block a user