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