architecture: move loop modes to clips

This commit is contained in:
itsmattkc
2022-07-17 15:22:36 -07:00
parent 4c71a33b7b
commit 0d9727b6d2
13 changed files with 179 additions and 79 deletions
+3 -3
View File
@@ -109,7 +109,7 @@ TexturePtr Decoder::RetrieveVideo(Renderer *renderer, const rational &timecode,
return RetrieveVideoInternal(renderer, timecode, divider, cancelled);
}
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &params, const QString& cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode)
Decoder::RetrieveAudioStatus Decoder::RetrieveAudio(SampleBuffer &dest, const TimeRange &range, const AudioParams &params, const QString& cache_path, LoopMode loop_mode, RenderMode::Mode mode)
{
QMutexLocker locker(&mutex_);
@@ -280,7 +280,7 @@ bool Decoder::ConformAudioInternal(const QVector<QString> &filenames, const Audi
return false;
}
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, Footage::LoopMode loop_mode, const AudioParams &input_params)
bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange& range, LoopMode loop_mode, const AudioParams &input_params)
{
PlanarFileDevice input;
if (input.open(conform_filenames, QFile::ReadOnly)) {
@@ -290,7 +290,7 @@ bool Decoder::RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVecto
const qint64 buffer_length_in_bytes = sample_buffer.sample_count() * input_params.bytes_per_sample_per_channel();
while (write_index < buffer_length_in_bytes) {
if (loop_mode == Footage::kLoopModeLoop) {
if (loop_mode == kLoopModeLoop) {
while (read_index >= input.size()) {
read_index -= input.size();
}
+8 -3
View File
@@ -35,7 +35,6 @@ extern "C" {
#include "codec/samplebuffer.h"
#include "common/rational.h"
#include "node/block/block.h"
#include "node/project/footage/footage.h"
#include "node/project/footage/footagedescription.h"
#include "task/task.h"
@@ -72,6 +71,12 @@ public:
kIndexUnavailable
};
enum LoopMode {
kLoopModeOff,
kLoopModeLoop,
kLoopModeClamp
};
Decoder();
/**
@@ -209,7 +214,7 @@ public:
*
* This function is thread safe and can only run while the decoder is open. \see Open()
*/
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, Footage::LoopMode loop_mode, RenderMode::Mode mode);
RetrieveAudioStatus RetrieveAudio(SampleBuffer &dest, const TimeRange& range, const AudioParams& params, const QString &cache_path, LoopMode loop_mode, RenderMode::Mode mode);
/**
* @brief Determine the last time this decoder instance was used in any way
@@ -316,7 +321,7 @@ signals:
private:
void UpdateLastAccessed();
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, Footage::LoopMode loop_mode, const AudioParams &params);
bool RetrieveAudioFromConform(SampleBuffer &sample_buffer, const QVector<QString> &conform_filenames, const TimeRange &range, LoopMode loop_mode, const AudioParams &params);
CodecStream stream_;
@@ -38,12 +38,12 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
clips_(clips),
timebase_(timebase)
{
setWindowTitle(tr("Speed/Duration"));
setWindowTitle(tr("Clip Properties"));
QVBoxLayout *layout = new QVBoxLayout(this);
{
QGroupBox *speed_group = new QGroupBox();
QGroupBox *speed_group = new QGroupBox(tr("Speed/Duration"));
layout->addWidget(speed_group);
QGridLayout *speed_layout = new QGridLayout(speed_group);
@@ -72,16 +72,39 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
link_box_ = new QCheckBox(tr("Link Speed and Duration"));
link_box_->setChecked(true);
speed_layout->addWidget(link_box_, row, 0, 1, 2);
}
row++;
reverse_box_ = new QCheckBox(tr("Reverse"));
layout->addWidget(reverse_box_);
speed_layout->addWidget(reverse_box_, row, 0, 1, 2);
row++;
maintain_audio_pitch_box_ = new QCheckBox(tr("Maintain Audio Pitch"));
layout->addWidget(maintain_audio_pitch_box_);
speed_layout->addWidget(maintain_audio_pitch_box_, row, 0, 1, 2);
row++;
ripple_box_ = new QCheckBox(tr("Ripple Trailing Clips"));
layout->addWidget(ripple_box_);
speed_layout->addWidget(ripple_box_, row, 0, 1, 2);
}
{
auto loop_box = new QGroupBox(tr("Loop"));
layout->addWidget(loop_box);
auto loop_layout = new QGridLayout(loop_box);
int row = 0;
loop_layout->addWidget(new QLabel(tr("Loop:")), row, 0);
loop_combo_ = new QComboBox();
loop_combo_->addItem(tr("None"), Decoder::kLoopModeOff);
loop_combo_->addItem(tr("Loop"), Decoder::kLoopModeLoop);
loop_combo_->addItem(tr("Clamp"), Decoder::kLoopModeClamp);
loop_layout->addWidget(loop_combo_, row, 1);
}
QDialogButtonBox *btns = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
btns->setCenterButtons(true);
@@ -94,6 +117,7 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
start_duration_ = clips.first()->length();
start_reverse_ = clips.first()->reverse();
start_maintain_audio_pitch_ = clips.first()->maintain_audio_pitch();
start_loop_ = clips.first()->loop_mode();
for (int i=1; i<clips.size(); i++) {
ClipBlock *c = clips.at(i);
@@ -116,6 +140,10 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
if (start_maintain_audio_pitch_ != -1 && clip_maintain_pitch != start_maintain_audio_pitch_) {
start_maintain_audio_pitch_ = -1;
}
if (start_loop_ != -1 && c->loop_mode() != start_loop_) {
start_loop_ = -1;
}
}
if (qIsNaN(start_speed_)) {
@@ -141,6 +169,12 @@ SpeedDurationDialog::SpeedDurationDialog(const QVector<ClipBlock *> &clips, cons
} else {
maintain_audio_pitch_box_->setChecked(start_maintain_audio_pitch_);
}
if (start_loop_ == -1) {
loop_combo_->setCurrentIndex(-1);
} else {
loop_combo_->setCurrentIndex(start_loop_);
}
}
void SpeedDurationDialog::accept()
@@ -211,6 +245,12 @@ void SpeedDurationDialog::accept()
}
}
if (loop_combo_->currentIndex() != -1) {
foreach (ClipBlock *c, clips_) {
command->add_child(new NodeParamSetStandardValueCommand(NodeKeyframeTrackReference(NodeInput(c, ClipBlock::kLoopModeInput)), loop_combo_->currentData()));
}
}
Core::instance()->undo_stack()->push(command);
super::accept();
@@ -22,6 +22,7 @@
#define SPEEDDURATIONDIALOG_H
#include <QCheckBox>
#include <QComboBox>
#include <QDialog>
#include "node/block/clip/clip.h"
@@ -62,6 +63,8 @@ private:
QCheckBox *ripple_box_;
QComboBox *loop_combo_;
int start_reverse_;
int start_maintain_audio_pitch_;
@@ -70,6 +73,8 @@ private:
rational start_duration_;
int start_loop_;
rational timebase_;
private slots:
+28 -5
View File
@@ -34,6 +34,7 @@ const QString ClipBlock::kMediaInInput = QStringLiteral("media_in_in");
const QString ClipBlock::kSpeedInput = QStringLiteral("speed_in");
const QString ClipBlock::kReverseInput = QStringLiteral("reverse_in");
const QString ClipBlock::kMaintainAudioPitchInput = QStringLiteral("maintain_audio_pitch_in");
const QString ClipBlock::kLoopModeInput = QStringLiteral("loop_in");
ClipBlock::ClipBlock() :
in_transition_(nullptr),
@@ -56,6 +57,8 @@ ClipBlock::ClipBlock() :
//SetValueHintForInput(kBufferIn, ValueHint(NodeValue::kBuffer));
SetEffectInput(kBufferIn);
AddInput(kLoopModeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
}
QString ClipBlock::Name() const
@@ -89,7 +92,8 @@ void ClipBlock::set_length_and_media_out(const rational &length)
if (reverse()) {
// Calculate media_in adjustment
rational proposed_media_in = SequenceToMediaTime(this->length() - length, true);
rational proposed_media_in = SequenceToMediaTime(this->length() - length, kSTMIgnoreReverse | kSTMIgnoreLoop);
set_media_in(proposed_media_in);
}
@@ -104,7 +108,7 @@ void ClipBlock::set_length_and_media_in(const rational &length)
if (!reverse()) {
// Calculate media_in adjustment
rational proposed_media_in = SequenceToMediaTime(this->length() - length, false, true);
rational proposed_media_in = SequenceToMediaTime(this->length() - length, kSTMIgnoreSpeed | kSTMIgnoreLoop);
waveform_.TrimIn(proposed_media_in - media_in());
@@ -127,7 +131,7 @@ void ClipBlock::set_media_in(const rational &media_in)
SetStandardValue(kMediaInInput, QVariant::fromValue(media_in));
}
rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool ignore_reverse, bool ignore_speed) const
rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, uint64_t flags) const
{
// These constants are not considered "values" per se, so we don't modify them
if (sequence_time == RATIONAL_MIN || sequence_time == RATIONAL_MAX) {
@@ -136,11 +140,11 @@ rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool igno
rational media_time = sequence_time;
if (reverse() && !ignore_reverse) {
if (reverse() && !(flags & kSTMIgnoreReverse)) {
media_time = length() - media_time;
}
if (!ignore_speed) {
if (!(flags & kSTMIgnoreSpeed)) {
double speed_value = speed();
if (qIsNull(speed_value)) {
// Effectively holds the frame at the in point
@@ -153,6 +157,23 @@ rational ClipBlock::SequenceToMediaTime(const rational &sequence_time, bool igno
media_time += media_in();
/*if (!(flags & kSTMIgnoreLoop)
&& this->loop_mode() != kLoopModeOff
&& connected_viewer_
&& !connected_viewer_->GetLength().isNull()
&& (media_time < 0 || media_time >= connected_viewer_->GetLength())) {
if (loop_mode() == kLoopModeLoop) {
while (media_time < 0) {
media_time += connected_viewer_->GetLength();
}
while (media_time >= connected_viewer_->GetLength()) {
media_time -= connected_viewer_->GetLength();
}
} else if (loop_mode() == kLoopModeClamp) {
media_time = std::clamp(media_time, rational(0), connected_viewer_->GetLength()-connected_viewer_->GetVideoParams().frame_rate_as_time_base());
}
}*/
return media_time;
}
@@ -282,6 +303,8 @@ void ClipBlock::Retranslate()
SetInputName(kSpeedInput, tr("Speed"));
SetInputName(kReverseInput, tr("Reverse"));
SetInputName(kMaintainAudioPitchInput, tr("Maintain Audio Pitch"));
SetInputName(kLoopModeInput, tr("Loop"));
SetComboBoxStrings(kLoopModeInput, {tr("None"), tr("Loop"), tr("Clamp")});
}
TimeRange ClipBlock::media_range() const
+24 -1
View File
@@ -22,6 +22,7 @@
#define CLIPBLOCK_H
#include "audio/audiovisualwaveform.h"
#include "codec/decoder.h"
#include "node/block/block.h"
namespace olive {
@@ -121,17 +122,39 @@ public:
TimeRange media_range() const;
/**
* @brief Get currently set loop mode
*/
Decoder::LoopMode loop_mode() const
{
return static_cast<Decoder::LoopMode>(GetStandardValue(kLoopModeInput).toInt());
}
void set_loop_mode(Decoder::LoopMode l)
{
SetStandardValue(kLoopModeInput, int(l));
}
static const QString kBufferIn;
static const QString kMediaInInput;
static const QString kSpeedInput;
static const QString kReverseInput;
static const QString kMaintainAudioPitchInput;
static const QString kLoopModeInput;
protected:
virtual void LinkChangeEvent() override;
private:
rational SequenceToMediaTime(const rational& sequence_time, bool ignore_reverse = false, bool ignore_speed = false) const;
enum SequenceToMediaTimeFlag
{
kSTMNone,
kSTMIgnoreReverse,
kSTMIgnoreSpeed,
kSTMIgnoreLoop
};
rational SequenceToMediaTime(const rational& sequence_time, uint64_t flags = kSTMNone) const;
rational MediaToSequenceTime(const rational& media_time) const;
+7 -19
View File
@@ -37,7 +37,6 @@
namespace olive {
const QString Footage::kFilenameInput = QStringLiteral("file_in");
const QString Footage::kLoopModeInput = QStringLiteral("loop_in");
#define super ViewerOutput
@@ -49,8 +48,6 @@ Footage::Footage(const QString &filename) :
{
SetCacheTextures(true);
PrependInput(kLoopModeInput, NodeValue::kCombo, 0, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
PrependInput(kFilenameInput, NodeValue::kFile, InputFlags(kInputFlagNotConnectable | kInputFlagNotKeyframable));
Clear();
@@ -70,8 +67,6 @@ void Footage::Retranslate()
super::Retranslate();
SetInputName(kFilenameInput, tr("Filename"));
SetInputName(kLoopModeInput, tr("Loop Mode"));
SetComboBoxStrings(kLoopModeInput, {tr("None"), tr("Loop"), tr("Clamp")});
}
void Footage::InputValueChangedEvent(const QString &input, int element)
@@ -139,11 +134,6 @@ void Footage::SetValid()
valid_ = true;
}
Footage::LoopMode Footage::loop_mode() const
{
return static_cast<LoopMode>(GetStandardValue(kLoopModeInput).toInt());
}
QString Footage::filename() const
{
return GetStandardValue(kFilenameInput).toString();
@@ -263,17 +253,15 @@ void Footage::Value(const NodeValueRow &value, const NodeGlobals &globals, NodeV
// Pop filename from table
QString file = value[kFilenameInput].toString();
LoopMode loop_mode = static_cast<LoopMode>(value[kLoopModeInput].toInt());
// If the file exists and the reference is valid, push a footage job to the renderer
if (QFileInfo(file).exists()) {
if (QFileInfo::exists(file)) {
// Push length
table->Push(NodeValue::kRational, QVariant::fromValue(GetLength()), this, false, QStringLiteral("length"));
table->Push(NodeValue::kRational, QVariant::fromValue(GetLength()), this, QStringLiteral("length"));
// Push each stream as a footage job
for (int i=0; i<GetTotalStreamCount(); i++) {
Track::Reference ref = GetReferenceFromRealIndex(i);
FootageJob job(decoder_, filename(), ref.type(), GetLength(), loop_mode);
FootageJob job(decoder_, filename(), ref.type(), GetLength());
NodeValue::Type type;
@@ -339,7 +327,7 @@ bool TimeIsOutOfBounds(const rational& time, const rational& length)
return time < 0 || time >= length;
}
rational Footage::AdjustTimeByLoopMode(rational time, Footage::LoopMode loop_mode, const rational &length, VideoParams::Type type, const rational& timebase)
rational Footage::AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mode, const rational &length, VideoParams::Type type, const rational& timebase)
{
if (type == VideoParams::kVideoTypeStill) {
// No looping for still images
@@ -348,15 +336,15 @@ rational Footage::AdjustTimeByLoopMode(rational time, Footage::LoopMode loop_mod
if (TimeIsOutOfBounds(time, length)) {
switch (loop_mode) {
case kLoopModeOff:
case Decoder::kLoopModeOff:
// Return no time to indicate no frame should be shown here
time = rational::NaN;
break;
case kLoopModeClamp:
case Decoder::kLoopModeClamp:
// Clamp footage time to length
time = clamp(time, rational(0), length - timebase);
break;
case kLoopModeLoop:
case Decoder::kLoopModeLoop:
// Loop footage time around job length
do {
if (time >= length) {
+2 -13
View File
@@ -24,6 +24,7 @@
#include <QList>
#include <QDateTime>
#include "codec/decoder.h"
#include "common/rational.h"
#include "footagedescription.h"
#include "node/output/viewer/viewer.h"
@@ -44,12 +45,6 @@ class Footage : public ViewerOutput
{
Q_OBJECT
public:
enum LoopMode {
kLoopModeOff,
kLoopModeLoop,
kLoopModeClamp
};
/**
* @brief Footage Constructor
*/
@@ -101,11 +96,6 @@ public:
*/
void SetValid();
/**
* @brief Get currently set loop mode
*/
LoopMode loop_mode() const;
/**
* @brief Return the current filename of this Footage object
*/
@@ -183,7 +173,7 @@ public:
virtual Node *GetConnectedSampleOutput() override;
static rational AdjustTimeByLoopMode(rational time, LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase);
static rational AdjustTimeByLoopMode(rational time, Decoder::LoopMode loop_mode, const rational& length, VideoParams::Type type, const rational &timebase);
virtual void LoadFinishedEvent() override;
@@ -191,7 +181,6 @@ public:
virtual qint64 mod_time() const override;
static const QString kFilenameInput;
static const QString kLoopModeInput;
protected:
virtual void InputValueChangedEvent(const QString &input, int element) override;
+12 -2
View File
@@ -21,6 +21,7 @@
#include "traverser.h"
#include "node.h"
#include "node/block/clip/clip.h"
#include "render/job/footagejob.h"
#include "render/rendermanager.h"
@@ -30,6 +31,12 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
{
NodeValueDatabase database;
// HACK: Pick up loop mode from clips
Decoder::LoopMode old_loop_mode = loop_mode_;
if (const ClipBlock *clip = dynamic_cast<const ClipBlock*>(node)) {
loop_mode_ = clip->loop_mode();
}
// We need to insert tables into the database for each input
foreach (const QString& input, node->inputs()) {
if (IsCancelled()) {
@@ -39,6 +46,8 @@ NodeValueDatabase NodeTraverser::GenerateDatabase(const Node* node, const TimeRa
database.Insert(input, ProcessInput(node, input, range));
}
loop_mode_ = old_loop_mode;
return database;
}
@@ -260,7 +269,8 @@ NodeValueTable NodeTraverser::ProcessInput(const Node* node, const QString& inpu
NodeTraverser::NodeTraverser() :
cancel_(nullptr),
transform_(nullptr)
transform_(nullptr),
loop_mode_(Decoder::kLoopModeOff)
{
}
@@ -436,7 +446,7 @@ void NodeTraverser::ResolveJobs(NodeValue &val, const TimeRange &range)
if (job.type() == Track::kVideo) {
rational footage_time = Footage::AdjustTimeByLoopMode(range.in(), job.loop_mode(), job.length(), job.video_params().video_type(), job.video_params().frame_rate_as_time_base());
rational footage_time = Footage::AdjustTimeByLoopMode(range.in(), loop_mode_, job.length(), job.video_params().video_type(), job.video_params().frame_rate_as_time_base());
TexturePtr tex;
+4
View File
@@ -149,6 +149,8 @@ protected:
return block_stack_.empty() ? nullptr : block_stack_.back();
}
Decoder::LoopMode loop_mode() const { return loop_mode_; }
private:
void PreProcessRow(const TimeRange &range, NodeValueRow &row);
@@ -166,6 +168,8 @@ private:
std::list<Block*> block_stack_;
Decoder::LoopMode loop_mode_;
};
}
+3 -17
View File
@@ -29,17 +29,15 @@ class FootageJob
{
public:
FootageJob() :
type_(Track::kNone),
loop_mode_(Footage::kLoopModeOff)
type_(Track::kNone)
{
}
FootageJob(const QString& decoder, const QString& filename, Track::Type type, const rational& length, Footage::LoopMode loop_mode) :
FootageJob(const QString& decoder, const QString& filename, Track::Type type, const rational& length) :
decoder_(decoder),
filename_(filename),
type_(type),
length_(length),
loop_mode_(loop_mode)
length_(length)
{
}
@@ -98,16 +96,6 @@ public:
length_ = length;
}
Footage::LoopMode loop_mode() const
{
return loop_mode_;
}
void set_loop_mode(Footage::LoopMode loop_mode)
{
loop_mode_ = loop_mode;
}
private:
QString decoder_;
@@ -123,8 +111,6 @@ private:
rational length_;
Footage::LoopMode loop_mode_;
};
}
+1 -1
View File
@@ -503,7 +503,7 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination, const Foota
Decoder::RetrieveAudioStatus status = decoder->RetrieveAudio(destination,
input_time, audio_params,
stream.cache_path(),
stream.loop_mode(),
loop_mode(),
static_cast<RenderMode::Mode>(ticket_->property("mode").toInt()));
if (status == Decoder::kWaitingForConform) {
@@ -531,20 +531,47 @@ void TimelineView::DrawBlock(QPainter *painter, bool foreground, Block *block, q
// Draw zebra stripes and markers
if (clip->connected_viewer()) {
if (!clip->connected_viewer()->GetLength().isNull()) {
painter->setPen(shadow_color);
if (clip->media_in() < 0) {
qreal zebra_right = TimeToScene(clip->in() - clip->media_in());
switch (clip->loop_mode()) {
case Decoder::kLoopModeOff:
// Draw stripes for sections of clip < 0
qreal zebra_right = TimeToScene(-clip->media_in());
if (zebra_right > GetTimelineLeftBound()) {
DrawZebraStripes(painter, QRectF(block_left, block_top, zebra_right, block_height));
DrawZebraStripes(painter, QRectF(block_left, block_top, zebra_right - block_left, block_height));
}
break;
case Decoder::kLoopModeLoop:
for (qreal i=zebra_right; i>block_left; i-=TimeToScene(clip->connected_viewer()->GetLength())) {
painter->drawLine(i, block_top, i, block_top + block_height);
}
break;
case Decoder::kLoopModeClamp:
painter->drawLine(zebra_right, block_top, zebra_right, block_top + block_height);
break;
}
}
if (clip->length() + clip->media_in() > clip->connected_viewer()->GetLength()) {
// Draw stripes for sections for clip > clip length
qreal zebra_left = TimeToScene(clip->out() - (clip->media_in() + clip->length() - clip->connected_viewer()->GetLength()));
switch (clip->loop_mode()) {
case Decoder::kLoopModeOff:
// Draw stripes for sections for clip > clip length
if (zebra_left < GetTimelineRightBound()) {
DrawZebraStripes(painter, QRectF(zebra_left, block_top, block_right - zebra_left, block_height));
}
break;
case Decoder::kLoopModeLoop:
for (qreal i=zebra_left; i<block_right; i+=TimeToScene(clip->connected_viewer()->GetLength())) {
painter->drawLine(i, block_top, i, block_top + block_height);
}
break;
case Decoder::kLoopModeClamp:
painter->drawLine(zebra_left, block_top, zebra_left, block_top + block_height);
break;
}
}
}