timeline: added thumbnail modes that affect how much is rendered

This commit is contained in:
itsmattkc
2022-09-06 16:06:26 -07:00
parent 00f256f16f
commit 571be3d28d
12 changed files with 159 additions and 81 deletions
+6 -1
View File
@@ -312,6 +312,11 @@ TimeRangeListFrameIterator::TimeRangeListFrameIterator(const TimeRangeList &list
UpdateIndexIfNecessary();
}
rational TimeRangeListFrameIterator::Snap(const rational &r) const
{
return Timecode::snap_time_to_timebase(r, timebase_, Timecode::kFloor);
}
bool TimeRangeListFrameIterator::GetNext(rational *out)
{
if (!HasNext()) {
@@ -368,7 +373,7 @@ void TimeRangeListFrameIterator::UpdateIndexIfNecessary()
range_index_++;
if (range_index_ < list_.size()) {
current_ = Timecode::snap_time_to_timebase(list_.at(range_index_).in(), timebase_, Timecode::kCeil);
current_ = Snap(list_.at(range_index_).in());
}
}
}
+2
View File
@@ -207,6 +207,8 @@ public:
TimeRangeListFrameIterator();
TimeRangeListFrameIterator(const TimeRangeList &list, const rational &timebase);
rational Snap(const rational &r) const;
bool GetNext(rational *out);
bool HasNext() const;
+4
View File
@@ -32,6 +32,7 @@
#include "common/filefunctions.h"
#include "common/xmlutils.h"
#include "core.h"
#include "timeline/timelinecommon.h"
#include "ui/colorcoding.h"
#include "ui/style/style.h"
#include "window/mainwindow/mainwindow.h"
@@ -104,6 +105,9 @@ void Config::SetDefaults()
SetEntryInternal(QStringLiteral("ReassocLinToNonLin"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("PreviewNonFloatDontAskAgain"), NodeValue::kBoolean, false);
SetEntryInternal(QStringLiteral("TimelineThumbnailMode"), NodeValue::kInt, Timeline::kThumbnailOn);
SetEntryInternal(QStringLiteral("TimelineWaveformMode"), NodeValue::kInt, Timeline::kWaveformsEnabled);
SetEntryInternal(QStringLiteral("DefaultVideoTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultAudioTransition"), NodeValue::kText, QStringLiteral("org.olivevideoeditor.Olive.crossdissolve"));
SetEntryInternal(QStringLiteral("DefaultTransitionLength"), NodeValue::kRational, QVariant::fromValue(rational(1)));
+56 -10
View File
@@ -20,6 +20,7 @@
#include "clip.h"
#include "config/config.h"
#include "node/output/track/track.h"
#include "node/output/viewer/viewer.h"
#include "widget/slider/floatslider.h"
@@ -108,12 +109,14 @@ void ClipBlock::set_length_and_media_in(const rational &length)
return;
}
if (!reverse()) {
// Calculate media_in adjustment
set_media_in(SequenceToMediaTime(this->length() - length, kSTMIgnoreLoop));
}
rational old_length = this->length();
super::set_length_and_media_in(length);
if (!reverse()) {
// Calculate media_in adjustment
set_media_in(SequenceToMediaTime(old_length - length, kSTMIgnoreLoop));
}
}
rational ClipBlock::media_in() const
@@ -124,6 +127,8 @@ rational ClipBlock::media_in() const
void ClipBlock::set_media_in(const rational &media_in)
{
SetStandardValue(kMediaInInput, QVariant::fromValue(media_in));
RequestInvalidatedFromConnected();
}
void ClipBlock::SetAutocache(bool e)
@@ -208,16 +213,22 @@ void ClipBlock::RequestRangeFromConnected(const TimeRange &range)
if (type == Track::kVideo || type == Track::kAudio) {
if (Node *connected = GetConnectedOutput(kBufferIn)) {
TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
TimeRange max_range = media_range();
if (type == Track::kVideo) {
// Handle thumbnails
RequestRangeForCache(connected->thumbnail_cache(), max_range, range, true, true);
RequestRangeForCache(connected->thumbnail_cache(), max_range, range, true, false);
{
TimeRange thumb_range = range;
if (GetAdjustedThumbnailRange(&thumb_range)) {
emit connected->thumbnail_cache()->Request(thumb_range);
}
}
// Handle video cache
RequestRangeForCache(connected->video_frame_cache(), max_range, range, true, IsAutocaching());
} else if (type == Track::kAudio) {
// Handle waveforms
RequestRangeForCache(connected->waveform_cache(), max_range, range, true, true);
RequestRangeForCache(connected->waveform_cache(), max_range, range, true, (OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled));
// Handle audio cache
RequestRangeForCache(connected->audio_playback_cache(), max_range, range, true, IsAutocaching());
@@ -232,10 +243,13 @@ void ClipBlock::RequestInvalidatedFromConnected()
if (type == Track::kVideo || type == Track::kAudio) {
if (Node *connected = GetConnectedOutput(kBufferIn)) {
TimeRange max_range = InputTimeAdjustment(kBufferIn, -1, TimeRange(0, length()));
TimeRange max_range = media_range();
if (type == Track::kVideo) {
// Handle thumbnails
RequestInvalidatedForCache(connected->thumbnail_cache(), max_range);
TimeRange thumb_range = max_range;
if (GetAdjustedThumbnailRange(&thumb_range)) {
RequestInvalidatedForCache(connected->thumbnail_cache(), thumb_range);
}
// Handle video cache
if (IsAutocaching()) {
@@ -243,7 +257,9 @@ void ClipBlock::RequestInvalidatedFromConnected()
}
} else if (type == Track::kAudio) {
// Handle waveforms
RequestInvalidatedForCache(connected->waveform_cache(), max_range);
if (OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled) {
RequestInvalidatedForCache(connected->waveform_cache(), max_range);
}
// Handle audio cache
if (IsAutocaching()) {
@@ -280,6 +296,34 @@ void ClipBlock::RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange
}
}
bool ClipBlock::GetAdjustedThumbnailRange(TimeRange *r) const
{
switch (static_cast<Timeline::ThumbnailMode>(OLIVE_CONFIG("TimelineThumbnailMode").toInt())) {
case Timeline::kThumbnailOff:
// Don't cache any range
return false;
case Timeline::kThumbnailInOut:
{
// Only cache in point
rational in = this->media_range().in();
if (r->Contains(in)) {
// Cache only the in point
*r = TimeRange(in, in + thumbnail_cache()->GetTimebase());
return true;
} else {
// Cache nothing
return false;
}
}
case Timeline::kThumbnailOn:
// Cache entire range
return true;
}
// Fallback
return true;
}
void ClipBlock::InvalidateCache(const TimeRange& range, const QString& from, int element, InvalidateCacheOptions options)
{
Q_UNUSED(element)
@@ -379,8 +423,10 @@ void ClipBlock::InputValueChangedEvent(const QString &input, int element)
if (Node *connected = GetConnectedOutput(kBufferIn)) {
if (type == Track::kVideo) {
emit connected->video_frame_cache()->CancelAll();
//emit connected->thumbnail_cache()->CancelAll();
} else if (type == Track::kAudio) {
emit connected->audio_playback_cache()->CancelAll();
//emit connected->waveform_cache()->CancelAll();
}
}
}
+2
View File
@@ -232,6 +232,8 @@ private:
void RequestRangeForCache(PlaybackCache *cache, const TimeRange &max_range, const TimeRange &range, bool invalidate, bool request);
void RequestInvalidatedForCache(PlaybackCache *cache, const TimeRange &max_range);
bool GetAdjustedThumbnailRange(TimeRange *r) const;
QVector<Block*> block_links_;
TransitionBlock* in_transition_;
+1
View File
@@ -93,6 +93,7 @@ public:
ThumbnailCache(QObject* parent = nullptr) :
FrameHashCache(parent)
{
SetTimebase(rational(1, 10));
}
};
+10 -4
View File
@@ -459,8 +459,16 @@ void PreviewAutoCacher::StartCachingRange(const TimeRange &range, TimeRangeList
void PreviewAutoCacher::StartCachingVideoRange(PlaybackCache *cache, const TimeRange &range)
{
Node *node = cache->parent();
pending_video_jobs_.push_back({node, cache, range, TimeRangeListFrameIterator({range}, viewer_node_->GetVideoParams().frame_rate_as_time_base())});
video_cache_data_[cache].job_tracker.insert(range, graph_changed_time_);
rational using_tb;
if (ThumbnailCache *thumbs = dynamic_cast<ThumbnailCache*>(cache)) {
using_tb = thumbs->GetTimebase();
} else {
using_tb = viewer_node_->GetVideoParams().frame_rate_as_time_base();
}
TimeRangeListFrameIterator iterator({range}, using_tb);
pending_video_jobs_.push_back({node, cache, range, iterator});
video_cache_data_[cache].job_tracker.insert(TimeRange(iterator.Snap(range.in()), range.out()), graph_changed_time_);
TryRender();
}
@@ -679,8 +687,6 @@ RenderTicketWatcher* PreviewAutoCacher::RenderFrame(Node *node, const rational&
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;
wave_cache->SetTimebase(rational(1, 10));
} else {
frame_cache->SetTimebase(viewer_node_->GetVideoParams().frame_rate_as_time_base());
}
+11
View File
@@ -38,6 +38,17 @@ public:
kTrimOut
};
enum ThumbnailMode {
kThumbnailOff,
kThumbnailInOut,
kThumbnailOn
};
enum WaveformMode {
kWaveformsDisabled,
kWaveformsEnabled
};
static bool IsATrimMode(MovementMode mode) {return mode == kTrimIn || mode == kTrimOut;}
struct EditToInfo {
+16 -12
View File
@@ -1140,14 +1140,20 @@ void TimelineWidget::ShowContextMenu()
toggle_audio_units->setChecked(use_audio_time_units_);
connect(toggle_audio_units, &QAction::triggered, this, &TimelineWidget::SetUseAudioTimeUnits);
QAction* show_thumbnails = menu.addAction(tr("Show Thumbnails"));
show_thumbnails->setCheckable(true);
show_thumbnails->setChecked(views_.first()->view()->GetShowThumbnails());
connect(show_thumbnails, &QAction::triggered, this, &TimelineWidget::SetViewThumbnailsEnabled);
{
Menu *thumbnail_menu = new Menu(tr("Show Thumbnails"), &menu);
menu.addMenu(thumbnail_menu);
thumbnail_menu->AddActionWithData(tr("Disabled"), Timeline::kThumbnailOff, OLIVE_CONFIG("TimelineThumbnailMode"));
thumbnail_menu->AddActionWithData(tr("Only At In/Out Points"), Timeline::kThumbnailInOut, OLIVE_CONFIG("TimelineThumbnailMode"));
thumbnail_menu->AddActionWithData(tr("Enabled"), Timeline::kThumbnailOn, OLIVE_CONFIG("TimelineThumbnailMode"));
connect(thumbnail_menu, &Menu::triggered, this, &TimelineWidget::SetViewThumbnailsEnabled);
}
QAction* show_waveforms = menu.addAction(tr("Show Waveforms"));
show_waveforms->setCheckable(true);
show_waveforms->setChecked(views_.first()->view()->GetShowWaveforms());
show_waveforms->setChecked(OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled);
connect(show_waveforms, &QAction::triggered, this, &TimelineWidget::SetViewWaveformsEnabled);
menu.addSeparator();
@@ -1211,16 +1217,14 @@ void TimelineWidget::AddableObjectChanged()
void TimelineWidget::SetViewWaveformsEnabled(bool e)
{
foreach (TimelineAndTrackView* tview, views_) {
tview->view()->SetShowWaveforms(e);
}
OLIVE_CONFIG("TimelineWaveformMode") = e ? Timeline::kWaveformsEnabled : Timeline::kWaveformsDisabled;
UpdateViewports();
}
void TimelineWidget::SetViewThumbnailsEnabled(bool e)
void TimelineWidget::SetViewThumbnailsEnabled(QAction *action)
{
foreach (TimelineAndTrackView* tview, views_) {
tview->view()->SetShowThumbnails(e);
}
OLIVE_CONFIG("TimelineThumbnailMode") = action->data();
UpdateViewports();
}
void TimelineWidget::FrameRateChanged()
+1 -1
View File
@@ -423,7 +423,7 @@ private slots:
void SetViewWaveformsEnabled(bool e);
void SetViewThumbnailsEnabled(bool e);
void SetViewThumbnailsEnabled(QAction *action);
void FrameRateChanged();
+48 -28
View File
@@ -46,8 +46,6 @@ TimelineView::TimelineView(Qt::Alignment vertical_alignment, QWidget *parent) :
ghosts_(nullptr),
show_beam_cursor_(false),
connected_track_list_(nullptr),
show_thumbnails_(true),
show_waveforms_(true),
transition_overlay_out_(nullptr),
transition_overlay_in_(nullptr)
{
@@ -486,7 +484,6 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
int text_height = fm.height();
int text_padding = text_height/4; // This ties into the track minimum height being 1.5
int text_total_height = text_height + text_padding + text_padding;
Q_UNUSED(text_total_height)
if (foreground) {
painter->setBrush(Qt::NoBrush);
@@ -524,43 +521,52 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
QRect preview_rect = r.toRect();
// Draw clip thumbnails
if (clip->GetTrackType() == Track::kVideo && show_thumbnails_ && preview_rect.height() > r.height()/3) {
if (clip->GetTrackType() == Track::kVideo
&& OLIVE_CONFIG("TimelineThumbnailMode").toInt() != Timeline::kThumbnailOff
&& preview_rect.height() > r.height()/3) {
if (const FrameHashCache *thumbs = clip->thumbnails()) {
// Start thumbnails underneath clip name
preview_rect.adjust(0, text_total_height, 0, 0);
QRect thumb_rect;
painter->setClipRect(preview_rect);
painter->setRenderHint(QPainter::SmoothPixmapTransform);
painter->setClipRect(preview_rect);
Sequence *s = clip->track()->sequence();
int width = s->GetVideoParams().width();
int height = s->GetVideoParams().height();
int start;
if (height > 0) { // Prevent divide by zero/invalid params
double scale = double(preview_rect.height())/double(height);
thumb_rect.setWidth(width * scale);
start = (preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width() + qFloor(block_in);
} else {
start = preview_rect.left();
}
if (OLIVE_CONFIG("TimelineThumbnailMode") == Timeline::kThumbnailOn) {
for (int i=start; i<preview_rect.right(); i+=thumb_rect.width()+1) {
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()) {
QImage img;
if (img.load(thumbnail, "jpg")) {
double scale = double(preview_rect.height())/double(img.height());
thumb_rect = QRect(i, preview_rect.top(), img.width() * scale, preview_rect.height());
painter->drawImage(thumb_rect, img);
}
Sequence *s = clip->track()->sequence();
int width = s->GetVideoParams().width();
int height = s->GetVideoParams().height();
int start;
if (height > 0) { // Prevent divide by zero/invalid params
double scale = double(preview_rect.height())/double(height);
thumb_rect.setWidth(width * scale);
start = (((preview_rect.left() - int(qFloor(block_in))) / thumb_rect.width()) * thumb_rect.width()) + qFloor(block_in);
} else {
start = preview_rect.left();
}
for (int i=start; i<preview_rect.right(); i+=thumb_rect.width()+1) {
rational time_here = SceneToTime(i - block_in, GetScale(), connected_track_list_->parent()->GetVideoParams().frame_rate_as_time_base()) + media_in;
DrawThumbnail(painter, thumbs, time_here, i, preview_rect, &thumb_rect);
}
} else {
rational time = clip->media_range().in();
time = Timecode::snap_time_to_timebase(time, thumbs->GetTimebase(), Timecode::kFloor);
DrawThumbnail(painter, thumbs, time, block_left, preview_rect, &thumb_rect);
}
painter->setClipping(false);
}
}
// Draw waveform
if (clip->GetTrackType() == Track::kAudio && show_waveforms_) {
if (clip->GetTrackType() == Track::kAudio
&& OLIVE_CONFIG("TimelineWaveformMode").toInt() == Timeline::kWaveformsEnabled) {
if (const AudioWaveformCache *wave = clip->waveform()) {
rational waveform_start = SceneToTime(block_left - block_in, GetScale(), connected_track_list_->parent()->GetAudioParams().sample_rate_as_time_base()) + media_in;
painter->setPen(shadow_color);
@@ -733,6 +739,20 @@ qreal TimelineView::GetTimelineRightBound() const
return GetTimelineLeftBound() + viewport()->width();
}
void TimelineView::DrawThumbnail(QPainter *painter, const FrameHashCache *thumbs, const rational &time, int x, const QRect &preview_rect, QRect *thumb_rect) const
{
QString thumbnail = thumbs->GetValidCacheFilename(time);
if (!thumbnail.isEmpty()) {
QImage img;
if (img.load(thumbnail, "jpg")) {
double scale = double(preview_rect.height())/double(img.height());
*thumb_rect = QRect(x, preview_rect.top(), img.width() * scale, preview_rect.height());
painter->drawImage(*thumb_rect, img);
}
}
}
int TimelineView::GetTrackY(int track_index) const
{
if (!connected_track_list_ || !connected_track_list_->GetTrackCount()) {
+2 -25
View File
@@ -73,28 +73,6 @@ public:
Block* GetItemAtScenePos(const rational& time, int track_index) const;
bool GetShowWaveforms() const
{
return show_waveforms_;
}
void SetShowWaveforms(bool e)
{
show_waveforms_ = e;
viewport()->update();
}
bool GetShowThumbnails() const
{
return show_thumbnails_;
}
void SetShowThumbnails(bool e)
{
show_thumbnails_ = e;
viewport()->update();
}
signals:
void MousePressed(TimelineViewMouseEvent* event);
void MouseMoved(TimelineViewMouseEvent* event);
@@ -154,6 +132,8 @@ private:
qreal GetTimelineRightBound() const;
void DrawThumbnail(QPainter *painter, const FrameHashCache *thumbs, const rational &time, int x, const QRect &preview_rect, QRect *thumb_rect) const;
QHash<Track::Reference, TimeRangeList>* selections_;
QVector<TimelineViewGhostItem*>* ghosts_;
@@ -164,9 +144,6 @@ private:
TrackList* connected_track_list_;
bool show_thumbnails_;
bool show_waveforms_;
ClipBlock *transition_overlay_out_;
ClipBlock *transition_overlay_in_;