cache: added cache task that can be manually invoked
This commit is contained in:
@@ -106,6 +106,8 @@ void ViewerOutput::set_video_params(const VideoParams &video)
|
||||
{
|
||||
video_params_ = video;
|
||||
|
||||
video_frame_cache_.SetTimebase(video_params_.time_base());
|
||||
|
||||
emit SizeChanged(video_params_.width(), video_params_.height());
|
||||
emit TimebaseChanged(video_params_.time_base());
|
||||
emit ParamsChanged();
|
||||
|
||||
@@ -116,6 +116,11 @@ void FrameHashCache::SaveCacheFrame(const QByteArray& hash,
|
||||
}
|
||||
}
|
||||
|
||||
void FrameHashCache::SaveCacheFrame(const QByteArray &hash, FramePtr frame)
|
||||
{
|
||||
SaveCacheFrame(hash, frame->data(), frame->video_params());
|
||||
}
|
||||
|
||||
void FrameHashCache::LengthChangedEvent(const rational &old, const rational &newlen)
|
||||
{
|
||||
if (newlen < old) {
|
||||
@@ -174,8 +179,8 @@ bool FrameHashCache::SaveCacheFrame(const QString &filename, char *data, const V
|
||||
// Attempt to keep this write to one thread
|
||||
out->threads(1);
|
||||
|
||||
out->open(fn_std, OIIO::ImageSpec(vparam.width(),
|
||||
vparam.height(),
|
||||
out->open(fn_std, OIIO::ImageSpec(vparam.effective_width(),
|
||||
vparam.effective_height(),
|
||||
PixelFormat::ChannelCount(vparam.format()),
|
||||
PixelFormat::GetOIIOTypeDesc(vparam.format())));
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ public:
|
||||
|
||||
static bool SaveCacheFrame(const QString& filename, char *data, const VideoRenderingParams &vparam);
|
||||
static void SaveCacheFrame(const QByteArray& hash, char *data, const VideoRenderingParams &vparam);
|
||||
static void SaveCacheFrame(const QByteArray& hash, FramePtr frame);
|
||||
|
||||
static QString GetFormatExtension(const PixelFormat::Format& f);
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class PlaybackCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
public:
|
||||
PlaybackCache();
|
||||
|
||||
@@ -50,11 +50,11 @@ public:
|
||||
}
|
||||
|
||||
signals:
|
||||
void Invalidated(const TimeRange& r);
|
||||
void Invalidated(const OLIVE_NAMESPACE::TimeRange& r);
|
||||
|
||||
void Validated(const TimeRange& r);
|
||||
void Validated(const OLIVE_NAMESPACE::TimeRange& r);
|
||||
|
||||
void LengthChanged(const rational& r);
|
||||
void LengthChanged(const OLIVE_NAMESPACE::rational& r);
|
||||
|
||||
protected:
|
||||
void Validate(const TimeRange& r);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
add_subdirectory(cache)
|
||||
add_subdirectory(conform)
|
||||
add_subdirectory(proxy)
|
||||
|
||||
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
# Olive - Non-Linear Video Editor
|
||||
# Copyright (C) 2019 Olive Team
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
set(OLIVE_SOURCES
|
||||
${OLIVE_SOURCES}
|
||||
task/cache/cache.h
|
||||
task/cache/cache.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
Vendored
+174
@@ -0,0 +1,174 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#include "cache.h"
|
||||
|
||||
#include <QLinkedList>
|
||||
|
||||
#include "common/timecodefunctions.h"
|
||||
#include "project/item/sequence/sequence.h"
|
||||
#include "render/backend/opengl/openglbackend.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
CacheTask::CacheTask(ViewerOutput* viewer, int divider, bool in_out_only) :
|
||||
viewer_(viewer),
|
||||
in_out_only_(in_out_only),
|
||||
divider_(divider)
|
||||
{
|
||||
SetTitle(tr("Caching \"%1\"").arg(viewer_->media_name()));
|
||||
}
|
||||
|
||||
struct TimeHashFuturePair {
|
||||
rational time;
|
||||
QFuture<QByteArray> hash_future;
|
||||
};
|
||||
|
||||
struct HashFrameFuturePair {
|
||||
QByteArray hash;
|
||||
QFuture<FramePtr> frame_future;
|
||||
};
|
||||
|
||||
struct HashDownloadFuturePair {
|
||||
QByteArray hash;
|
||||
QFuture<void> download_future;
|
||||
};
|
||||
|
||||
void CacheTask::Action()
|
||||
{
|
||||
OpenGLBackend backend;
|
||||
|
||||
RenderMode::Mode mode = RenderMode::kOffline;
|
||||
PixelFormat::Format format = PixelFormat::instance()->GetConfiguredFormatForMode(mode);
|
||||
|
||||
backend.SetViewerNode(viewer_);
|
||||
backend.SetPixelFormat(format);
|
||||
backend.SetMode(mode);
|
||||
backend.SetDivider(divider_);
|
||||
backend.SetSampleFormat(SampleFormat::kInternalFormat);
|
||||
|
||||
// Get list of invalidated ranges
|
||||
TimeRangeList range_to_cache = viewer_->video_frame_cache()->GetInvalidatedRanges();
|
||||
|
||||
// If we're caching only in-out, limit the range to that
|
||||
if (in_out_only_) {
|
||||
Sequence* s = static_cast<Sequence*>(viewer_->parent());
|
||||
|
||||
if (s->workarea()->enabled()) {
|
||||
range_to_cache = range_to_cache.Intersects(s->workarea()->range());
|
||||
}
|
||||
}
|
||||
// Get hashes for each frame
|
||||
QLinkedList<TimeHashFuturePair> hash_list;
|
||||
|
||||
while (!range_to_cache.isEmpty()) {
|
||||
const TimeRange& range = range_to_cache.first();
|
||||
|
||||
const rational& timebase = viewer_->video_params().time_base();
|
||||
|
||||
rational time = range.in();
|
||||
rational snapped = Timecode::snap_time_to_timebase(time, timebase);
|
||||
rational next;
|
||||
|
||||
if (snapped > time) {
|
||||
next = snapped;
|
||||
snapped -= timebase;
|
||||
} else {
|
||||
next = snapped + timebase;
|
||||
}
|
||||
|
||||
hash_list.append({snapped, backend.Hash(snapped)});
|
||||
range_to_cache.RemoveTimeRange(TimeRange(snapped, next));
|
||||
}
|
||||
|
||||
// Determine any duplicates
|
||||
QMap< QByteArray, QLinkedList<rational> > times_to_render;
|
||||
foreach (const TimeHashFuturePair& i, hash_list) {
|
||||
times_to_render[i.hash_future.result()].append(i.time);
|
||||
}
|
||||
|
||||
// Render all frames necessary
|
||||
QLinkedList<HashFrameFuturePair> render_lookup_table;
|
||||
{
|
||||
QMap< QByteArray, QLinkedList<rational> >::const_iterator i;
|
||||
for (i=times_to_render.constBegin(); i!=times_to_render.constEnd(); i++) {
|
||||
const QByteArray& hash = i.key();
|
||||
|
||||
render_lookup_table.append({hash, backend.RenderFrame(i.value().first(), false)});
|
||||
}
|
||||
}
|
||||
|
||||
OIIO::TypeDesc output_desc = PixelFormat::GetOIIOTypeDesc(format);
|
||||
OIIO::ImageSpec output_spec(viewer_->video_params().width() / divider_,
|
||||
viewer_->video_params().height() / divider_,
|
||||
PixelFormat::ChannelCount(format),
|
||||
output_desc);
|
||||
|
||||
// Start downloading frames that have finished
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
QLinkedList<HashDownloadFuturePair> download_futures;
|
||||
|
||||
// Iterators
|
||||
QLinkedList<HashFrameFuturePair>::iterator i;
|
||||
QLinkedList<HashDownloadFuturePair>::iterator j;
|
||||
|
||||
while (!render_lookup_table.isEmpty() || !download_futures.isEmpty()) {
|
||||
i = render_lookup_table.begin();
|
||||
|
||||
while (i != render_lookup_table.end()) {
|
||||
if (i->frame_future.isFinished()) {
|
||||
FramePtr f = i->frame_future.result();
|
||||
|
||||
// Start multithreaded download here
|
||||
download_futures.append({i->hash,
|
||||
QtConcurrent::run(FrameHashCache::SaveCacheFrame, i->hash, f)});
|
||||
|
||||
i = render_lookup_table.erase(i);
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
j = download_futures.begin();
|
||||
|
||||
while (j != download_futures.end()) {
|
||||
if (j->download_future.isFinished()) {
|
||||
// Place it in the cache
|
||||
const QLinkedList<rational>& times_with_hash = times_to_render.value(j->hash);
|
||||
foreach (const rational& t, times_with_hash) {
|
||||
viewer_->video_frame_cache()->SetHash(t, j->hash);
|
||||
}
|
||||
|
||||
// Signal process
|
||||
counter++;
|
||||
emit ProgressChanged(qRound(100.0 * static_cast<double>(counter) / static_cast<double>(render_lookup_table.size())));
|
||||
|
||||
j = download_futures.erase(j);
|
||||
} else {
|
||||
j++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
/***
|
||||
|
||||
Olive - Non-Linear Video Editor
|
||||
Copyright (C) 2019 Olive Team
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
***/
|
||||
|
||||
#ifndef CACHETASK_H
|
||||
#define CACHETASK_H
|
||||
|
||||
#include "node/output/viewer/viewer.h"
|
||||
#include "task/task.h"
|
||||
|
||||
OLIVE_NAMESPACE_ENTER
|
||||
|
||||
class CacheTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CacheTask(ViewerOutput* viewer, int divider, bool in_out_only);
|
||||
|
||||
protected:
|
||||
virtual void Action() override;
|
||||
|
||||
private:
|
||||
ViewerOutput* viewer_;
|
||||
|
||||
bool in_out_only_;
|
||||
|
||||
int divider_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
#endif // CACHETASK_H
|
||||
@@ -340,6 +340,45 @@ void ViewerWidget::SetGizmos(Node *node)
|
||||
display_widget_->SetGizmos(node);
|
||||
}
|
||||
|
||||
FramePtr DecodeCachedImage(const QString &fn, const rational& time)
|
||||
{
|
||||
FramePtr frame = nullptr;
|
||||
|
||||
if (!fn.isEmpty() && QFileInfo::exists(fn)) {
|
||||
auto input = OIIO::ImageInput::open(fn.toStdString());
|
||||
|
||||
if (input) {
|
||||
|
||||
PixelFormat::Format image_format = PixelFormat::OIIOFormatToOliveFormat(input->spec().format,
|
||||
input->spec().nchannels == kRGBAChannels);
|
||||
|
||||
frame = Frame::Create();
|
||||
frame->set_timestamp(time);
|
||||
frame->set_video_params(VideoRenderingParams(input->spec().width,
|
||||
input->spec().height,
|
||||
image_format));
|
||||
|
||||
frame->allocate();
|
||||
|
||||
input->read_image(input->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
|
||||
input->close();
|
||||
|
||||
#if OIIO_VERSION < 10903
|
||||
OIIO::ImageInput::destroy(input);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
{
|
||||
if (!FrameExistsAtTime(time)) {
|
||||
@@ -348,9 +387,8 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
QMutexLocker locker(playback_queue_.lock());
|
||||
|
||||
// Check playback queue for a frame
|
||||
if (IsPlaying()) {
|
||||
while (!playback_queue_.isEmpty()) {
|
||||
const ViewerPlaybackFrame& pf = playback_queue_.first();
|
||||
|
||||
@@ -368,13 +406,11 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsPlaying()) {
|
||||
qDebug() << "Playback queue couldn't keep up - falling back to realtime decoding";
|
||||
}
|
||||
|
||||
// Frame was not in queue, will require decoding
|
||||
// Frame was not in queue, will require rendering or decoding from cache
|
||||
QFutureWatcher<FramePtr>* watcher = new QFutureWatcher<FramePtr>();
|
||||
|
||||
connect(watcher,
|
||||
@@ -382,7 +418,7 @@ void ViewerWidget::UpdateTextureFromNode(const rational& time)
|
||||
this,
|
||||
&ViewerWidget::RendererGeneratedFrame);
|
||||
|
||||
watcher->setFuture(renderer_->RenderFrame(time, true));
|
||||
watcher->setFuture(GetFrame(time, true));
|
||||
}
|
||||
|
||||
void ViewerWidget::PlayInternal(int speed, bool in_to_out_only)
|
||||
@@ -485,11 +521,11 @@ void ViewerWidget::SetColorTransform(const ColorTransform &transform, ViewerDisp
|
||||
void ViewerWidget::FillPlaybackQueue()
|
||||
{
|
||||
// FIXME: Replace with an asynchronous approach
|
||||
while (playback_queue_.size() < 8) {
|
||||
while (playback_queue_.size() < 16) {
|
||||
rational next_time = Timecode::timestamp_to_time(playback_queue_next_frame_,
|
||||
timebase());
|
||||
playback_queue_next_frame_ += playback_speed_;
|
||||
QFuture<FramePtr> future = renderer_->RenderFrame(next_time, false);
|
||||
QFuture<FramePtr> future = GetFrame(next_time, false);
|
||||
future.waitForFinished();
|
||||
playback_queue_.AppendTimewise({future.result()->timestamp(), future.result()}, playback_speed_);
|
||||
|
||||
@@ -507,7 +543,7 @@ QString ViewerWidget::GetCachedFilenameFromTime(const rational &time)
|
||||
if (!hash.isEmpty()) {
|
||||
return GetConnectedNode()->video_frame_cache()->CachePathName(
|
||||
hash,
|
||||
PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline));
|
||||
GetCurrentPixelFormat());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,45 +555,6 @@ bool ViewerWidget::FrameExistsAtTime(const rational &time)
|
||||
return GetConnectedNode() && time < GetConnectedNode()->GetLength();
|
||||
}
|
||||
|
||||
FramePtr ViewerWidget::DecodeCachedImage(const QString &fn)
|
||||
{
|
||||
FramePtr frame = nullptr;
|
||||
|
||||
if (!fn.isEmpty() && QFileInfo::exists(fn)) {
|
||||
auto input = OIIO::ImageInput::open(fn.toStdString());
|
||||
|
||||
if (input) {
|
||||
|
||||
PixelFormat::Format image_format = PixelFormat::OIIOFormatToOliveFormat(input->spec().format,
|
||||
input->spec().nchannels == kRGBAChannels);
|
||||
|
||||
frame = Frame::Create();
|
||||
|
||||
frame->set_video_params(VideoRenderingParams(input->spec().width,
|
||||
input->spec().height,
|
||||
image_format));
|
||||
|
||||
frame->allocate();
|
||||
|
||||
input->read_image(input->spec().format,
|
||||
frame->data(),
|
||||
OIIO::AutoStride,
|
||||
frame->linesize_bytes());
|
||||
|
||||
input->close();
|
||||
|
||||
#if OIIO_VERSION < 10903
|
||||
OIIO::ImageInput::destroy(input);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
qWarning() << "OIIO Error:" << OIIO::geterror().c_str();
|
||||
}
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
void ViewerWidget::SetDisplayImage(FramePtr frame, bool main_only)
|
||||
{
|
||||
display_widget_->SetImage(frame);
|
||||
@@ -587,7 +584,27 @@ void ViewerWidget::RequestNextFrameForQueue()
|
||||
&QFutureWatcher<FramePtr>::finished,
|
||||
this,
|
||||
&ViewerWidget::RendererGeneratedFrameForQueue);
|
||||
watcher->setFuture(renderer_->RenderFrame(next_time, false));
|
||||
watcher->setFuture(GetFrame(next_time, false));
|
||||
}
|
||||
|
||||
PixelFormat::Format ViewerWidget::GetCurrentPixelFormat() const
|
||||
{
|
||||
return PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline);
|
||||
}
|
||||
|
||||
QFuture<FramePtr> ViewerWidget::GetFrame(const rational &t, bool clear_render_queue)
|
||||
{
|
||||
QByteArray cached_hash = GetConnectedNode()->video_frame_cache()->GetHash(t);
|
||||
if (cached_hash.isEmpty()) {
|
||||
// Frame hasn't been cached, start render job
|
||||
return renderer_->RenderFrame(t, clear_render_queue);
|
||||
} else {
|
||||
// Frame has been cached, grab the frame
|
||||
QString cache_fn = GetConnectedNode()->video_frame_cache()->CachePathName(cached_hash,
|
||||
GetCurrentPixelFormat());
|
||||
|
||||
return QtConcurrent::run(DecodeCachedImage, cache_fn, t);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewerWidget::UpdateStack()
|
||||
@@ -699,7 +716,7 @@ void ViewerWidget::UpdateRendererParameters()
|
||||
|
||||
renderer_->SetDivider(divider_);
|
||||
renderer_->SetMode(render_mode);
|
||||
renderer_->SetPixelFormat(PixelFormat::instance()->GetConfiguredFormatForMode(render_mode));
|
||||
renderer_->SetPixelFormat(GetCurrentPixelFormat());
|
||||
|
||||
display_widget_->SetVideoParams(GetConnectedNode()->video_params());
|
||||
|
||||
@@ -859,11 +876,7 @@ void ViewerWidget::Pause()
|
||||
window->Pause();
|
||||
}
|
||||
|
||||
{
|
||||
playback_queue_.lock()->lock();
|
||||
playback_queue_.clear();
|
||||
playback_queue_.lock()->unlock();
|
||||
}
|
||||
playback_queue_.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,12 +182,14 @@ private:
|
||||
|
||||
bool FrameExistsAtTime(const rational& time);
|
||||
|
||||
FramePtr DecodeCachedImage(const QString& fn);
|
||||
|
||||
void SetDisplayImage(FramePtr frame, bool main_only);
|
||||
|
||||
void RequestNextFrameForQueue();
|
||||
|
||||
PixelFormat::Format GetCurrentPixelFormat() const;
|
||||
|
||||
QFuture<FramePtr> GetFrame(const rational& t, bool clear_render_queue);
|
||||
|
||||
QStackedWidget* stack_;
|
||||
|
||||
ViewerSizer* sizer_;
|
||||
@@ -240,7 +242,7 @@ private slots:
|
||||
|
||||
void SetZoomFromMenu(QAction* action);
|
||||
|
||||
void ViewerInvalidatedRange(const TimeRange &range);
|
||||
void ViewerInvalidatedRange(const OLIVE_NAMESPACE::TimeRange &range);
|
||||
|
||||
void UpdateStack();
|
||||
|
||||
|
||||
@@ -56,14 +56,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
QMutex* lock()
|
||||
{
|
||||
return &queue_lock_;
|
||||
}
|
||||
|
||||
private:
|
||||
QMutex queue_lock_;
|
||||
|
||||
};
|
||||
|
||||
OLIVE_NAMESPACE_EXIT
|
||||
|
||||
@@ -77,7 +77,6 @@ void ViewerWindow::Pause()
|
||||
{
|
||||
disconnect(display_widget_, &ViewerDisplayWidget::frameSwapped, this, &ViewerWindow::UpdateFromQueue);
|
||||
|
||||
QMutexLocker locker(queue_.lock());
|
||||
queue_.clear();
|
||||
}
|
||||
|
||||
@@ -103,7 +102,6 @@ void ViewerWindow::UpdateFromQueue()
|
||||
|
||||
rational time = Timecode::timestamp_to_time(t, playback_timebase_);
|
||||
|
||||
QMutexLocker locker(queue_.lock());
|
||||
while (!queue_.isEmpty()) {
|
||||
const ViewerPlaybackFrame& pf = queue_.first();
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@
|
||||
#include "config/config.h"
|
||||
#include "core.h"
|
||||
#include "dialog/actionsearch/actionsearch.h"
|
||||
#include "dialog/task/task.h"
|
||||
#include "panel/panelmanager.h"
|
||||
#include "task/cache/cache.h"
|
||||
#include "tool/tool.h"
|
||||
#include "ui/style/style.h"
|
||||
#include "undo/undostack.h"
|
||||
@@ -611,7 +613,16 @@ void MainMenu::OpenRecentItemTriggered()
|
||||
|
||||
void MainMenu::SequenceCacheTriggered()
|
||||
{
|
||||
qDebug() << "STUB";
|
||||
TimeBasedPanel* p = PanelManager::instance()->MostRecentlyFocused<TimeBasedPanel>();
|
||||
|
||||
if (p && p->GetConnectedViewer()) {
|
||||
// FIXME: Hardcoded divider...
|
||||
// FIXME: Consider preventing caching the footage viewer
|
||||
CacheTask* task = new CacheTask(p->GetConnectedViewer(), 2, false);
|
||||
|
||||
TaskDialog* dialog = new TaskDialog(task, tr("Caching Sequence"), parentWidget());
|
||||
dialog->open();
|
||||
}
|
||||
}
|
||||
|
||||
void MainMenu::SequenceCacheInOutTriggered()
|
||||
|
||||
Reference in New Issue
Block a user