project: replaced proxy with pre-cache

Removed proxy task and replaced with a true honest-to-god pre-cache for
footage. This footage is pre-cached to a sequence and therefore 100% ready
for use in it once the task is done.
This commit is contained in:
itsmattkc
2020-06-08 23:17:51 +10:00
parent 50b4c1e456
commit 9095dfa463
31 changed files with 223 additions and 665 deletions
+1 -6
View File
@@ -57,7 +57,7 @@ void Decoder::set_stream(StreamPtr fs)
stream_ = fs;
}
FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/, const int &/*divider*/, bool /*use_proxies*/)
FramePtr Decoder::RetrieveVideo(const rational &/*timecode*/, const int &/*divider*/)
{
return nullptr;
}
@@ -175,11 +175,6 @@ QString Decoder::GetConformedFilename(const AudioParams &params)
return index_fn;
}
bool Decoder::ProxyVideo(const QAtomicInt *, int )
{
return false;
}
bool Decoder::ConformAudio(const QAtomicInt *, const AudioParams& )
{
return false;
+1 -6
View File
@@ -138,7 +138,7 @@ public:
* A FramePtr of valid data at this timecode or nullptr if there was nothing to retrieve at the provided timecode or
* the media could not be opened.
*/
virtual FramePtr RetrieveVideo(const rational& timecode, const int& divider, bool use_proxies);
virtual FramePtr RetrieveVideo(const rational& timecode, const int& divider);
/**
* @brief Retrieve video frame
@@ -210,11 +210,6 @@ public:
*/
static DecoderPtr CreateFromID(const QString& id);
/**
* @brief VIDEO ONLY: Produce a compressed EXR proxy with the specified divider
*/
virtual bool ProxyVideo(const QAtomicInt* cancelled, int divider);
/**
* @brief AUDIO ONLY: Produces a complete PCM extraction of the audio stream
*
+1 -175
View File
@@ -135,7 +135,7 @@ bool FFmpegDecoder::Open()
return true;
}
FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divider, bool use_proxies)
FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divider)
{
QMutexLocker locker(&mutex_);
@@ -152,51 +152,6 @@ FramePtr FFmpegDecoder::RetrieveVideo(const rational &timecode, const int &divid
VideoStreamPtr vs = std::static_pointer_cast<VideoStream>(stream());
if (use_proxies && vs->using_proxy()) {
QString proxy_fn = GetProxyFilename(vs->using_proxy());
int64_t index_ts = vs->get_closest_timestamp_in_frame_index(target_ts);
if (target_ts > -1) {
// Use this timestamp instead - even if we fall through to decoding manually, it'll be more
// accurate than the one we calculated earlier
target_ts = index_ts;
QString frame_filename = GetProxyFrameFilename(target_ts, vs->using_proxy());
if (QFileInfo::exists(frame_filename)) {
auto in = OIIO::ImageInput::open(frame_filename.toStdString());
if (in) {
FramePtr copy = Frame::Create();
copy->set_video_params(VideoParams(vs->width(),
vs->height(),
native_pix_fmt_,
vs->using_proxy()));
copy->set_timestamp(Timecode::timestamp_to_time(target_ts, time_base_));
copy->set_sample_aspect_ratio(aspect_ratio_);
copy->allocate();
// We're running one "decoder" per thread already, no need to spawn more than that
in->threads(1);
in->read_image(PixelFormat::GetOIIOTypeDesc(native_pix_fmt_),
copy->data(),
OIIO::AutoStride,
copy->linesize_bytes());
in->close();
#if OIIO_VERSION < 10903
OIIO::ImageInput::destroy(in);
#endif
return copy;
}
}
}
}
FFmpegDecoderInstance* working_instance = nullptr;
FFmpegFramePool::ElementPtr return_frame = nullptr;
@@ -669,135 +624,6 @@ void SaveCacheFrame(FFmpegDecoder* decoder,
av_frame_free(&frame);
}
bool FFmpegDecoder::ProxyVideo(const QAtomicInt *cancelled, int divider)
{
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(stream());
QString proxy_filename = GetProxyFilename(divider);
if (QFileInfo::exists(proxy_filename)) {
// A proxy of this type already exists so we can do nothing
QFile index_file(proxy_filename);
if (index_file.open(QFile::ReadOnly)) {
QVector<int64_t> index(index_file.size() / sizeof(int64_t));
index_file.read(reinterpret_cast<char*>(index.data()),
index_file.size());
index_file.close();
video_stream->set_proxy(divider, index);
return true;
}
}
// Iterate each frame and transcode it to EXR
FFmpegDecoderInstance instance(stream()->footage()->filename().toUtf8(), stream()->index());
int ret;
AVPixelFormat src_fmt = static_cast<AVPixelFormat>(instance.stream()->codecpar->format);
AVPixelFormat ideal_fmt = FFmpegCommon::GetCompatiblePixelFormat(src_fmt);
PixelFormat::Format native_fmt = GetNativePixelFormat(ideal_fmt);
int divided_width = GetScaledDimension(instance.stream()->codecpar->width, divider);
int divided_height = GetScaledDimension(instance.stream()->codecpar->height, divider);
SwsContext* scaler = sws_getContext(instance.stream()->codecpar->width,
instance.stream()->codecpar->height,
src_fmt,
divided_width,
divided_height,
ideal_fmt,
SWS_FAST_BILINEAR,
nullptr,
nullptr,
0);
AVPacket* pkt = av_packet_alloc();
QVector<int64_t> frame_index;
QVector< QFuture<void> > futures;
int finished_futures = 0;
VideoParams converted_params(divided_width,
divided_height,
native_fmt);
bool succeeded = false;
while (true) {
if (cancelled && *cancelled) {
break;
}
AVFrame* frame = av_frame_alloc();
ret = instance.GetFrame(pkt, frame);
// Handle errors
if (ret < 0) {
if (ret == AVERROR_EOF) {
succeeded = true;
} else {
char err_str[50];
av_strerror(ret, err_str, 50);
qWarning() << "Failed to proxy:" << ret << err_str;
}
av_frame_free(&frame);
break;
}
frame_index.append(frame->pts);
QFuture<void> future = QtConcurrent::run(SaveCacheFrame,
this,
scaler,
frame,
converted_params,
GetProxyFrameFilename(frame->pts, divider));
futures.append(future);
while (finished_futures < futures.size()) {
if (!futures.at(finished_futures).isFinished()) {
SignalProcessingProgress(frame_index.at(finished_futures));
break;
}
finished_futures++;
}
}
// Wait for all conversions to finish
for ( ; finished_futures<futures.size(); finished_futures++) {
futures[finished_futures].waitForFinished();
SignalProcessingProgress(frame_index.at(finished_futures));
}
// If succeeded, update the video stream's proxy state
if (succeeded) {
QFile index_output(proxy_filename);
if (index_output.open(QFile::WriteOnly)) {
index_output.write(reinterpret_cast<const char*>(frame_index.constData()),
frame_index.size() * sizeof(int64_t));
index_output.close();
}
video_stream->set_proxy(divider, frame_index);
}
sws_freeContext(scaler);
av_packet_free(&pkt);
return succeeded;
}
bool FFmpegDecoder::ConformAudio(const QAtomicInt *cancelled, const AudioParams &p)
{
// Iterate through each audio frame and extract the PCM data
+1 -2
View File
@@ -134,7 +134,7 @@ public:
virtual bool Probe(Footage *f, const QAtomicInt *cancelled) override;
virtual bool Open() override;
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider, bool use_proxies) override;
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
virtual SampleBufferPtr RetrieveAudio(const rational &timecode, const rational &length, const AudioParams& params) override;
virtual void Close() override;
@@ -143,7 +143,6 @@ public:
virtual bool SupportsVideo() override;
virtual bool SupportsAudio() override;
virtual bool ProxyVideo(const QAtomicInt* cancelled, int divider) override;
virtual bool ConformAudio(const QAtomicInt* cancelled, const AudioParams& p) override;
private:
+1 -1
View File
@@ -154,7 +154,7 @@ bool OIIODecoder::Open()
return true;
}
FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider, bool /*use_proxies*/)
FramePtr OIIODecoder::RetrieveVideo(const rational &timecode, const int& divider)
{
QMutexLocker locker(&mutex_);
+1 -1
View File
@@ -46,7 +46,7 @@ public:
virtual bool Probe(Footage *f, const QAtomicInt* cancelled) override;
virtual bool Open() override;
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider, bool use_proxies) override;
virtual FramePtr RetrieveVideo(const rational &timecode, const int& divider) override;
virtual void Close() override;
virtual bool SupportsVideo() override;
-2
View File
@@ -29,7 +29,6 @@
#include "tabs/preferencesgeneraltab.h"
#include "tabs/preferencesbehaviortab.h"
#include "tabs/preferencesappearancetab.h"
#include "tabs/preferencesqualitytab.h"
#include "tabs/preferencesdisktab.h"
#include "tabs/preferencesaudiotab.h"
#include "tabs/preferenceskeyboardtab.h"
@@ -54,7 +53,6 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, QMenuBar* main_menu_bar) :
AddTab(new PreferencesGeneralTab(), tr("General"));
AddTab(new PreferencesAppearanceTab(), tr("Appearance"));
AddTab(new PreferencesBehaviorTab(), tr("Behavior"));
AddTab(new PreferencesQualityTab(), tr("Quality"));
AddTab(new PreferencesDiskTab(), tr("Disk"));
AddTab(new PreferencesAudioTab(), tr("Audio"));
AddTab(new PreferencesKeyboardTab(main_menu_bar), tr("Keyboard"));
@@ -24,8 +24,6 @@ set(OLIVE_SOURCES
dialog/preferences/tabs/preferencesdisktab.cpp
dialog/preferences/tabs/preferencesappearancetab.h
dialog/preferences/tabs/preferencesappearancetab.cpp
dialog/preferences/tabs/preferencesqualitytab.h
dialog/preferences/tabs/preferencesqualitytab.cpp
dialog/preferences/tabs/preferencesaudiotab.h
dialog/preferences/tabs/preferencesaudiotab.cpp
dialog/preferences/tabs/preferenceskeyboardtab.h
@@ -1,135 +0,0 @@
/***
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 "preferencesqualitytab.h"
#include <QGroupBox>
#include <QLabel>
#include <QVBoxLayout>
#include "audio/sampleformat.h"
#include "render/colormanager.h"
OLIVE_NAMESPACE_ENTER
PreferencesQualityTab::PreferencesQualityTab()
{
QVBoxLayout* layout = new QVBoxLayout(this);
QHBoxLayout* profile_layout = new QHBoxLayout();
profile_layout->setMargin(0);
profile_layout->addWidget(new QLabel(tr("Profile:")));
QComboBox* profile_combobox = new QComboBox();
profile_combobox->addItem(tr("Preview (Offline)"));
profile_combobox->addItem(tr("Export (Online)"));
profile_layout->addWidget(profile_combobox);
layout->addLayout(profile_layout);
quality_stack_ = new QStackedWidget();
offline_group_ = new PreferencesQualityGroup(tr("Offline Quality"));
offline_group_->SetBitDepth(PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOffline));
offline_group_->ocio_method()->setCurrentIndex(ColorManager::GetOCIOMethodForMode(RenderMode::kOffline));
quality_stack_->addWidget(offline_group_);
online_group_ = new PreferencesQualityGroup(tr("Online Quality"));
online_group_->SetBitDepth(PixelFormat::instance()->GetConfiguredFormatForMode(RenderMode::kOnline));
online_group_->ocio_method()->setCurrentIndex(ColorManager::GetOCIOMethodForMode(RenderMode::kOnline));
quality_stack_->addWidget(online_group_);
layout->addWidget(quality_stack_);
connect(profile_combobox, SIGNAL(currentIndexChanged(int)), quality_stack_, SLOT(setCurrentIndex(int)));
}
void PreferencesQualityTab::Accept()
{
ColorManager::SetOCIOMethodForMode(RenderMode::kOffline, static_cast<ColorManager::OCIOMethod>(offline_group_->ocio_method()->currentIndex()));
ColorManager::SetOCIOMethodForMode(RenderMode::kOnline, static_cast<ColorManager::OCIOMethod>(online_group_->ocio_method()->currentIndex()));
PixelFormat::instance()->SetConfiguredFormatForMode(RenderMode::kOffline, static_cast<PixelFormat::Format>(offline_group_->bit_depth_combobox()->currentData().toInt()));
PixelFormat::instance()->SetConfiguredFormatForMode(RenderMode::kOnline, static_cast<PixelFormat::Format>(online_group_->bit_depth_combobox()->currentData().toInt()));
}
PreferencesQualityGroup::PreferencesQualityGroup(const QString &title, QWidget *parent) :
QGroupBox(title, parent)
{
QVBoxLayout* quality_outer_layout = new QVBoxLayout(this);
QGroupBox* video_group = new QGroupBox(tr("Video"));
QGridLayout* video_layout = new QGridLayout(video_group);
quality_outer_layout->addWidget(video_group);
int row = 0;
video_layout->addWidget(new QLabel(tr("Pixel Format:")), row, 0);
bit_depth_combobox_ = new QComboBox();
// Populate with bit depths
for (int i=0;i<PixelFormat::PIX_FMT_COUNT;i++) {
PixelFormat::Format pix_fmt = static_cast<PixelFormat::Format>(i);
// We always render with an alpha channel internally
if (PixelFormat::FormatHasAlphaChannel(pix_fmt)
&& PixelFormat::FormatIsFloat(pix_fmt)) {
bit_depth_combobox_->addItem(PixelFormat::GetName(pix_fmt),
i);
}
}
video_layout->addWidget(bit_depth_combobox_, row, 1);
row++;
video_layout->addWidget(new QLabel(tr("OpenColorIO Method:")), row, 0);
ocio_method_ = new QComboBox();
ocio_method_->addItem(tr("Fast"));
ocio_method_->addItem(tr("Accurate"));
video_layout->addWidget(ocio_method_, row, 1);
quality_outer_layout->addStretch();
}
void PreferencesQualityGroup::SetBitDepth(PixelFormat::Format f)
{
for (int i=0;i<bit_depth_combobox_->count();i++) {
if (bit_depth_combobox_->itemData(i) == f) {
bit_depth_combobox_->setCurrentIndex(i);
break;
}
}
}
QComboBox *PreferencesQualityGroup::bit_depth_combobox()
{
return bit_depth_combobox_;
}
QComboBox *PreferencesQualityGroup::ocio_method()
{
return ocio_method_;
}
OLIVE_NAMESPACE_EXIT
@@ -1,72 +0,0 @@
/***
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 PREFERENCESQUALITYTAB_H
#define PREFERENCESQUALITYTAB_H
#include <QComboBox>
#include <QDoubleSpinBox>
#include <QGroupBox>
#include <QStackedWidget>
#include "render/pixelformat.h"
#include "preferencestab.h"
OLIVE_NAMESPACE_ENTER
class PreferencesQualityGroup : public QGroupBox
{
Q_OBJECT
public:
PreferencesQualityGroup(const QString& title, QWidget* parent = nullptr);
void SetBitDepth(PixelFormat::Format f);
QComboBox* bit_depth_combobox();
QComboBox* ocio_method();
private:
QComboBox* bit_depth_combobox_;
QComboBox* ocio_method_;
};
class PreferencesQualityTab : public PreferencesTab
{
Q_OBJECT
public:
PreferencesQualityTab();
virtual void Accept() override;
private:
QStackedWidget* quality_stack_;
PreferencesQualityGroup* offline_group_;
PreferencesQualityGroup* online_group_;
};
OLIVE_NAMESPACE_EXIT
#endif // PREFERENCESQUALITYTAB_H
+20
View File
@@ -1,3 +1,23 @@
/***
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 SEQUENCEPARAM_H
#define SEQUENCEPARAM_H
+3 -39
View File
@@ -28,9 +28,7 @@ OLIVE_NAMESPACE_ENTER
VideoStream::VideoStream() :
start_time_(0),
is_image_sequence_(false),
is_generating_proxy_(false),
using_proxy_(0)
is_image_sequence_(false)
{
set_type(kVideo);
}
@@ -73,42 +71,7 @@ void VideoStream::set_image_sequence(bool e)
is_image_sequence_ = e;
}
bool VideoStream::is_generating_proxy()
{
QMutexLocker locker(proxy_access_lock());
return is_generating_proxy_;
}
bool VideoStream::try_start_proxy()
{
QMutexLocker locker(proxy_access_lock());
if (is_generating_proxy_) {
return false;
}
is_generating_proxy_ = true;
return true;
}
int VideoStream::using_proxy()
{
QMutexLocker locker(proxy_access_lock());
return using_proxy_;
}
void VideoStream::set_proxy(const int &divider, const QVector<int64_t> &index)
{
QMutexLocker locker(proxy_access_lock());
using_proxy_ = divider;
frame_index_ = index;
is_generating_proxy_ = false;
}
/*
int64_t VideoStream::get_closest_timestamp_in_frame_index(const rational &time)
{
// Get rough approximation of what the timestamp would be in this timebase
@@ -143,6 +106,7 @@ int64_t VideoStream::get_closest_timestamp_in_frame_index(int64_t timestamp)
return -1;
}
*/
/*
void VideoStream::clear_frame_index()
+3 -11
View File
@@ -47,9 +47,10 @@ public:
bool is_image_sequence() const;
void set_image_sequence(bool e);
/*
int64_t get_closest_timestamp_in_frame_index(const rational& time);
int64_t get_closest_timestamp_in_frame_index(int64_t timestamp);
/*
void clear_frame_index();
void append_frame_index(const int64_t& ts);
bool is_frame_index_ready();
@@ -59,24 +60,15 @@ public:
bool save_frame_index(const QString& s);
*/
bool is_generating_proxy();
bool try_start_proxy();
int using_proxy();
void set_proxy(const int& divider, const QVector<int64_t>& index);
private:
rational frame_rate_;
QVector<int64_t> frame_index_;
//QVector<int64_t> frame_index_;
int64_t start_time_;
bool is_image_sequence_;
bool is_generating_proxy_;
int using_proxy_;
};
using VideoStreamPtr = std::shared_ptr<VideoStream>;
+3 -1
View File
@@ -52,7 +52,9 @@ void AudioPlaybackCache::SetParameters(const AudioParams &params)
// Our current audio cache is unusable, so we truncate it automatically
TimeRange invalidate_range(0, NoLockGetLength());
NoLockInvalidate(invalidate_range);
if (invalidate_range.in() != invalidate_range.out()) {
NoLockInvalidate(invalidate_range);
}
locker.unlock();
+1 -2
View File
@@ -41,8 +41,7 @@ void OpenGLWorker::TextureToFrame(const QVariant &texture, FramePtr frame, const
NodeValue OpenGLWorker::FrameToTexture(DecoderPtr decoder, StreamPtr stream, const TimeRange &range) const
{
FramePtr frame = decoder->RetrieveVideo(range.in(),
video_params().divider(),
render_mode() == RenderMode::kOffline);
video_params().divider());
NodeValue value;
+1 -1
View File
@@ -127,7 +127,7 @@ QFuture<QList<QByteArray> > RenderBackend::Hash(const QList<rational> &times)
hasher.addData(reinterpret_cast<const char*>(&video_params_.format()), sizeof(PixelFormat::Format));
hasher.addData(reinterpret_cast<const char*>(&render_mode_), sizeof(RenderMode::Mode));
copied_viewer_node_->Hash(hasher, t);
copied_viewer_node_->texture_input()->get_connected_node()->Hash(hasher, t);
hashes.append(hasher.result());
}
+2 -2
View File
@@ -62,6 +62,8 @@ public:
void ClearVideoQueue();
void ProcessUpdateQueue();
/**
* @brief Asynchronously generate a hash at a given time
*/
@@ -98,8 +100,6 @@ private:
void RunNextJob();
void ProcessUpdateQueue();
ViewerOutput* viewer_node_;
// VIDEO MEMBERS
-1
View File
@@ -18,7 +18,6 @@ add_subdirectory(cache)
add_subdirectory(conform)
add_subdirectory(export)
add_subdirectory(project)
add_subdirectory(proxy)
add_subdirectory(render)
set(OLIVE_SOURCES
+2
View File
@@ -18,5 +18,7 @@ set(OLIVE_SOURCES
${OLIVE_SOURCES}
task/cache/cache.h
task/cache/cache.cpp
task/cache/footagecache.h
task/cache/footagecache.cpp
PARENT_SCOPE
)
+1 -2
View File
@@ -36,10 +36,9 @@ public:
const AudioParams &aparams,
bool in_out_only);
public slots:
protected:
virtual bool Run() override;
protected:
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) override;
virtual void FrameDownloaded(const QByteArray& hash, const std::list<rational>& times) override;
+53
View File
@@ -0,0 +1,53 @@
/***
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 "footagecache.h"
#include "common/timecodefunctions.h"
OLIVE_NAMESPACE_ENTER
FootageCacheTask::FootageCacheTask(VideoStreamPtr footage, Sequence *sequence) :
CacheTask(new ViewerOutput(), sequence->video_params(), sequence->audio_params(), false),
footage_(footage)
{
viewer()->set_video_params(sequence->video_params());
viewer()->set_audio_params(sequence->audio_params());
backend()->SetVideoParams(sequence->video_params());
backend()->SetAudioParams(sequence->audio_params());
video_node_ = new VideoInput();
video_node_->SetFootage(footage);
NodeParam::ConnectEdge(video_node_->output(), viewer()->texture_input());
SetTitle(tr("Pre-caching %1:%2").arg(footage->footage()->filename(),
QString::number(footage->index())));
backend()->ProcessUpdateQueue();
}
FootageCacheTask::~FootageCacheTask()
{
delete viewer();
delete video_node_;
}
OLIVE_NAMESPACE_EXIT
+13 -11
View File
@@ -18,29 +18,31 @@
***/
#ifndef PROXYTASK_H
#define PROXYTASK_H
#ifndef FOOTAGECACHETASK_H
#define FOOTAGECACHETASK_H
#include "project/item/footage/videostream.h"
#include "task/task.h"
#include "cache.h"
#include "node/input/media/video/video.h"
#include "project/item/footage/footage.h"
#include "project/item/sequence/sequence.h"
OLIVE_NAMESPACE_ENTER
class ProxyTask : public Task
class FootageCacheTask : public CacheTask
{
Q_OBJECT
public:
ProxyTask(VideoStreamPtr stream, int divider);
FootageCacheTask(VideoStreamPtr footage, Sequence* sequence);
public slots:
virtual bool Run() override;
virtual ~FootageCacheTask() override;
private:
VideoStreamPtr stream_;
VideoStreamPtr footage_;
int divider_;
VideoInput* video_node_;
};
OLIVE_NAMESPACE_EXIT
#endif // PROXYTASK_H
#endif // FOOTAGECACHETASK_H
+1 -1
View File
@@ -32,7 +32,7 @@ class ConformTask : public Task
public:
ConformTask(AudioStreamPtr stream, const AudioParams& params);
public slots:
protected:
virtual bool Run() override;
private:
+1 -2
View File
@@ -35,10 +35,9 @@ class ExportTask : public RenderTask
public:
ExportTask(ViewerOutput *viewer_node, ColorManager *color_manager, const ExportParams &params);
public slots:
protected:
virtual bool Run() override;
protected:
virtual QFuture<void> DownloadFrame(FramePtr frame, const QByteArray &hash) override;
virtual void FrameDownloaded(const QByteArray& hash, const std::list<rational>& times) override;
+1 -1
View File
@@ -42,7 +42,7 @@ public:
return command_;
}
public slots:
protected:
virtual bool Run() override;
private:
+1 -1
View File
@@ -37,7 +37,7 @@ public:
return projects_;
}
public slots:
protected:
virtual bool Run() override;
private:
+1 -1
View File
@@ -37,7 +37,7 @@ public:
return project_;
}
public slots:
protected:
virtual bool Run() override;
private:
-22
View File
@@ -1,22 +0,0 @@
# 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/proxy/proxy.h
task/proxy/proxy.cpp
PARENT_SCOPE
)
-62
View File
@@ -1,62 +0,0 @@
/***
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 "proxy.h"
#include "codec/decoder.h"
OLIVE_NAMESPACE_ENTER
ProxyTask::ProxyTask(VideoStreamPtr stream, int divider) :
stream_(stream),
divider_(divider)
{
if (divider_ == 1) {
SetTitle(tr("Generating full resolution proxy %1:%2").arg(stream_->footage()->filename(),
QString::number(stream_->index())));
} else {
SetTitle(tr("Generating 1/%1 resolution proxy %2:%3").arg(QString::number(divider),
stream_->footage()->filename(),
QString::number(stream_->index())));
}
}
bool ProxyTask::Run()
{
if (stream_->footage()->decoder().isEmpty()) {
SetError(tr("Failed to find decoder to conform audio stream"));
return false;
} else {
DecoderPtr decoder = Decoder::CreateFromID(stream_->footage()->decoder());
decoder->set_stream(stream_);
connect(decoder.get(), &Decoder::IndexProgress, this, &ProxyTask::ProgressChanged);
if (decoder->ProxyVideo(&IsCancelled(), divider_)) {
return true;
} else {
SetError(tr("Failed to generate proxy"));
return false;
}
}
}
OLIVE_NAMESPACE_EXIT
+108 -99
View File
@@ -31,7 +31,7 @@
#include "core.h"
#include "dialog/footageproperties/footageproperties.h"
#include "dialog/sequence/sequence.h"
#include "task/proxy/proxy.h"
#include "task/cache/footagecache.h"
#include "task/taskmanager.h"
#include "widget/menu/menu.h"
#include "widget/menu/menushared.h"
@@ -240,121 +240,142 @@ void ProjectExplorer::ShowContextMenu()
Menu menu;
Menu new_menu;
// FIXME: Support for multiple items and items other than Footage
QList<Item*> selected_items = SelectedItems();
context_menu_items_ = SelectedItems();
if (selected_items.isEmpty()) {
if (context_menu_items_.isEmpty()) {
// Items to show if no items are selected
// "New" menu
new_menu.setTitle(tr("&New"));
MenuShared::instance()->AddItemsForNewMenu(&new_menu);
menu.addMenu(&new_menu);
menu.addSeparator();
// FIXME: These are both duplicates of items from MainMenu, is there any way to re-use the code?
// "Import" action
QAction* import_action = menu.addAction(tr("&Import..."));
connect(import_action, &QAction::triggered, Core::instance(), &Core::DialogImportShow);
menu.addSeparator();
// Project properties action
QAction* project_properties = menu.addAction(tr("&Project Properties..."));
connect(project_properties, &QAction::triggered, Core::instance(), &Core::DialogProjectPropertiesShow);
} else {
context_menu_item_ = selected_items.first();
if (context_menu_item_->type() == Item::kFolder) {
// Actions to add when only one item is selected
if (context_menu_items_.size() == 1) {
Item* context_menu_item = context_menu_items_.first();
QAction* open_in_new_tab = menu.addAction(tr("Open in New Tab"));
connect(open_in_new_tab, &QAction::triggered, this, &ProjectExplorer::OpenContextMenuItemInNewTab);
switch (context_menu_item->type()) {
case Item::kFolder:
{
QAction* open_in_new_tab = menu.addAction(tr("Open in New Tab"));
connect(open_in_new_tab, &QAction::triggered, this, &ProjectExplorer::OpenContextMenuItemInNewTab);
QAction* open_in_new_window = menu.addAction(tr("Open in New Window"));
connect(open_in_new_window, &QAction::triggered, this, &ProjectExplorer::OpenContextMenuItemInNewWindow);
menu.addSeparator();
} else if (context_menu_item_->type() == Item::kFootage) {
QString reveal_text;
QAction* open_in_new_window = menu.addAction(tr("Open in New Window"));
connect(open_in_new_window, &QAction::triggered, this, &ProjectExplorer::OpenContextMenuItemInNewWindow);
break;
}
case Item::kFootage:
{
QString reveal_text;
#if defined(Q_OS_WINDOWS)
reveal_text = tr("Reveal in Explorer");
reveal_text = tr("Reveal in Explorer");
#elif defined(Q_OS_MAC)
reveal_text = tr("Reveal in Finder");
reveal_text = tr("Reveal in Finder");
#else
reveal_text = tr("Reveal in File Manager");
#endif
reveal_text = tr("Reveal in File Manager");
#endif
QAction* reveal_action = menu.addAction(reveal_text);
connect(reveal_action, &QAction::triggered, this, &ProjectExplorer::RevealSelectedFootage);
QAction* reveal_action = menu.addAction(reveal_text);
connect(reveal_action, &QAction::triggered, this, &ProjectExplorer::RevealSelectedFootage);
break;
}
case Item::kSequence:
break;
}
menu.addSeparator();
}
Footage* f = static_cast<Footage*>(context_menu_item_);
bool all_items_are_footage = true;
bool all_items_have_video_streams = true;
bool all_items_are_footage_or_sequence = true;
if (f->HasStreamsOfType(Stream::kVideo)) {
Menu* proxy_menu = new Menu(tr("Proxy"), &menu);
menu.addMenu(proxy_menu);
foreach (Item* i, context_menu_items_) {
if (i->type() == Item::kFootage && !static_cast<Footage*>(i)->HasStreamsOfType(Stream::kVideo)) {
all_items_have_video_streams = false;
}
VideoStreamPtr video_stream = std::static_pointer_cast<VideoStream>(f->get_first_stream_of_type(Stream::kVideo));
if (i->type() != Item::kFootage) {
all_items_are_footage = false;
}
if (video_stream->is_generating_proxy()) {
// Prevent multiple proxy actions from occurring at once
QAction* cant_proxy_action = proxy_menu->addAction(tr("Proxy being generated..."));
cant_proxy_action->setEnabled(false);
} else {
proxy_menu->addAction(tr("(None)"))->setData(0);
proxy_menu->addSeparator();
proxy_menu->addAction(tr("Full"))->setData(1);
proxy_menu->addAction(tr("1/2"))->setData(2);
proxy_menu->addAction(tr("1/4"))->setData(4);
proxy_menu->addAction(tr("1/8"))->setData(8);
foreach (QAction* a, proxy_menu->actions()) {
a->setCheckable(true);
if (a->data() == video_stream->using_proxy()) {
a->setChecked(true);
}
}
connect(proxy_menu, &Menu::triggered, this, &ProjectExplorer::ContextMenuStartProxy);
}
menu.addSeparator();
if (i->type() != Item::kFootage && i->type() != Item::kSequence) {
all_items_are_footage_or_sequence = false;
}
}
QAction* properties_action = menu.addAction(tr("P&roperties"));
if (all_items_are_footage && all_items_have_video_streams) {
Menu* proxy_menu = new Menu(tr("Proxy"), &menu);
menu.addMenu(proxy_menu);
if (context_menu_item_->type() == Item::kFootage) {
connect(properties_action, &QAction::triggered, this, &ProjectExplorer::ShowFootagePropertiesDialog);
} else if (context_menu_item_->type() == Item::kSequence) {
connect(properties_action, &QAction::triggered, this, &ProjectExplorer::ShowSequencePropertiesDialog);
QList<ItemPtr> sequences = project()->get_items_of_type(Item::kSequence);
if (sequences.isEmpty()) {
QAction* a = proxy_menu->addAction(tr("No sequences exist in project"));
a->setEnabled(false);
} else {
foreach (ItemPtr i, sequences) {
QAction* a = proxy_menu->addAction(tr("For \"%1\"").arg(i->name()));
a->setData(Node::PtrToValue(i.get()));
}
connect(proxy_menu, &Menu::triggered, this, &ProjectExplorer::ContextMenuStartProxy);
}
}
if (context_menu_items_.size() == 1) {
menu.addSeparator();
QAction* properties_action = menu.addAction(tr("P&roperties"));
connect(properties_action, &QAction::triggered, this, &ProjectExplorer::ShowItemPropertiesDialog);
}
}
menu.exec(QCursor::pos());
}
void ProjectExplorer::ShowFootagePropertiesDialog()
void ProjectExplorer::ShowItemPropertiesDialog()
{
// FIXME: Support for multiple items
FootagePropertiesDialog fpd(this, static_cast<Footage*>(context_menu_item_));
fpd.exec();
}
Item* sel = context_menu_items_.first();
void ProjectExplorer::ShowSequencePropertiesDialog()
{
// FIXME: Support for multiple items
SequenceDialog sd(static_cast<Sequence*>(context_menu_item_), SequenceDialog::kExisting, this);
sd.exec();
switch (sel->type()) {
case Item::kFootage:
{
// FIXME: Support for multiple items
FootagePropertiesDialog fpd(this, static_cast<Footage*>(sel));
fpd.exec();
break;
}
case Item::kFolder:
{
// FIXME: Rename dialog probably
break;
}
case Item::kSequence:
{
// FIXME: Support for multiple items
SequenceDialog sd(static_cast<Sequence*>(sel), SequenceDialog::kExisting, this);
sd.exec();
break;
}
}
}
void ProjectExplorer::RevealSelectedFootage()
{
Footage* footage = static_cast<Footage*>(context_menu_item_);
Footage* footage = static_cast<Footage*>(context_menu_items_.first());
#if defined(Q_OS_WINDOWS)
// Explorer
@@ -379,45 +400,33 @@ void ProjectExplorer::RevealSelectedFootage()
void ProjectExplorer::OpenContextMenuItemInNewTab()
{
Core::instance()->main_window()->FolderOpen(project(), context_menu_item_, false);
Core::instance()->main_window()->FolderOpen(project(), context_menu_items_.first(), false);
}
void ProjectExplorer::OpenContextMenuItemInNewWindow()
{
Core::instance()->main_window()->FolderOpen(project(), context_menu_item_, true);
Core::instance()->main_window()->FolderOpen(project(), context_menu_items_.first(), true);
}
void ProjectExplorer::ContextMenuStartProxy(QAction *a)
{
// Find video stream
VideoStreamPtr video_stream = nullptr;
QList<VideoStreamPtr> video_streams;
foreach (StreamPtr s, static_cast<Footage*>(context_menu_item_)->streams()) {
if (s->type() == Stream::kVideo) {
video_stream = std::static_pointer_cast<VideoStream>(s);
break;
// To get here, the `context_menu_items_` must be all kFootage
foreach (Item* i, context_menu_items_) {
VideoStreamPtr s = std::static_pointer_cast<VideoStream>(static_cast<Footage*>(i)->get_first_stream_of_type(Stream::kVideo));
if (s) {
video_streams.append(s);
}
}
if (!video_stream) {
return;
}
Sequence* sequence = Node::ValueToPtr<Sequence>(a->data());
int chosen_proxy_setting = a->data().toInt();
if (chosen_proxy_setting != video_stream->using_proxy()) {
if (!a->data().toInt()) {
// 0 means disable the proxy
video_stream->set_proxy(0, QVector<int64_t>());
} else if (video_stream->try_start_proxy()) {
// Start a background task for proxying
ProxyTask* proxy_task = new ProxyTask(video_stream, a->data().toInt());
TaskManager::instance()->AddTask(proxy_task);
}
// Start a background task for proxying
foreach (VideoStreamPtr video_stream, video_streams) {
FootageCacheTask* proxy_task = new FootageCacheTask(video_stream, sequence);
TaskManager::instance()->AddTask(proxy_task);
}
}
+2 -4
View File
@@ -144,7 +144,7 @@ private:
QTimer rename_timer_;
Item* context_menu_item_;
QList<Item*> context_menu_items_;
private slots:
void ItemClickedSlot(const QModelIndex& index);
@@ -161,9 +161,7 @@ private slots:
void ShowContextMenu();
void ShowFootagePropertiesDialog();
void ShowSequencePropertiesDialog();
void ShowItemPropertiesDialog();
void RevealSelectedFootage();