diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp index 333879e67..bb816f296 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.cpp @@ -21,7 +21,9 @@ #include "videostreamproperties.h" #include +#include #include +#include #include namespace OCIO = OCIO_NAMESPACE::v1; @@ -35,12 +37,16 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : QGridLayout* video_layout = new QGridLayout(this); video_layout->setMargin(0); - video_layout->addWidget(new QLabel(tr("Color Space:")), 0, 0); + int row = 0; + + video_layout->addWidget(new QLabel(tr("Color Space:")), row, 0); video_color_space_ = new QComboBox(); OCIO::ConstConfigRcPtr config = stream->footage()->project()->color_manager()->GetConfig(); int number_of_colorspaces = config->getNumColorSpaces(); + video_color_space_->addItem(tr("Default (%1)").arg(stream->footage()->project()->default_input_colorspace())); + for (int i=0;igetColorSpaceNameByIndex(i); @@ -49,22 +55,91 @@ VideoStreamProperties::VideoStreamProperties(ImageStreamPtr stream) : video_color_space_->setCurrentText(stream_->colorspace()); - video_layout->addWidget(video_color_space_, 0, 1); + video_layout->addWidget(video_color_space_, row, 1); + + row++; video_premultiply_alpha_ = new QCheckBox(tr("Premultiplied Alpha")); video_premultiply_alpha_->setChecked(stream_->premultiplied_alpha()); - video_layout->addWidget(video_premultiply_alpha_, 1, 0, 1, 2); + video_layout->addWidget(video_premultiply_alpha_, row, 0, 1, 2); + + row++; + + if (IsImageSequence(stream.get())) { + QGroupBox* imgseq_group = new QGroupBox(tr("Image Sequence")); + QGridLayout* imgseq_layout = new QGridLayout(imgseq_group); + + int imgseq_row = 0; + + VideoStream* video_stream = static_cast(stream.get()); + + imgseq_layout->addWidget(new QLabel(tr("Start Index:")), imgseq_row, 0); + + imgseq_start_time_ = new IntegerSlider(); + imgseq_start_time_->SetMinimum(0); + imgseq_start_time_->SetValue(video_stream->start_time()); + imgseq_layout->addWidget(imgseq_start_time_, imgseq_row, 1); + + imgseq_row++; + + imgseq_layout->addWidget(new QLabel(tr("End Index:")), imgseq_row, 0); + + imgseq_end_time_ = new IntegerSlider(); + imgseq_end_time_->SetMinimum(0); + imgseq_end_time_->SetValue(video_stream->start_time() + video_stream->duration()); + imgseq_layout->addWidget(imgseq_end_time_, imgseq_row, 1); + + video_layout->addWidget(imgseq_group, row, 0, 1, 2); + } } void VideoStreamProperties::Accept(QUndoCommand *parent) { + QString set_colorspace; + + if (video_color_space_->currentIndex() > 0) { + set_colorspace = video_color_space_->currentText(); + } + if (video_premultiply_alpha_->isChecked() != stream_->premultiplied_alpha() - || video_color_space_->currentText() != stream_->colorspace()) { + || set_colorspace != stream_->colorspace(false)) { + new VideoStreamChangeCommand(stream_, video_premultiply_alpha_->isChecked(), - video_color_space_->currentText(), + set_colorspace, parent); } + + if (IsImageSequence(stream_.get())) { + VideoStreamPtr video_stream = std::static_pointer_cast(stream_); + + if (video_stream->start_time() != imgseq_start_time_->GetValue()) { + new ImageSequenceChangeCommand(video_stream, + imgseq_start_time_->GetValue(), + imgseq_end_time_->GetValue() - imgseq_start_time_->GetValue(), + parent); + } + } +} + +bool VideoStreamProperties::SanityCheck() +{ + if (IsImageSequence(stream_.get())) { + if (imgseq_start_time_->GetValue() >= imgseq_end_time_->GetValue()) { + QMessageBox::critical(this, + tr("Invalid Configuration"), + tr("Image sequence end index must be a value higher than the start index."), + QMessageBox::Ok); + return false; + } + } + + return true; +} + +bool VideoStreamProperties::IsImageSequence(ImageStream *stream) +{ + return (stream->type() == Stream::kVideo && static_cast(stream)->is_image_sequence()); } VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(ImageStreamPtr stream, @@ -81,7 +156,7 @@ VideoStreamProperties::VideoStreamChangeCommand::VideoStreamChangeCommand(ImageS void VideoStreamProperties::VideoStreamChangeCommand::redo_internal() { old_premultiplied_ = stream_->premultiplied_alpha(); - old_colorspace_ = stream_->colorspace(); + old_colorspace_ = stream_->colorspace(false); stream_->set_premultiplied_alpha(new_premultiplied_); stream_->set_colorspace(new_colorspace_); @@ -92,3 +167,26 @@ void VideoStreamProperties::VideoStreamChangeCommand::undo_internal() stream_->set_premultiplied_alpha(old_premultiplied_); stream_->set_colorspace(old_colorspace_); } + +VideoStreamProperties::ImageSequenceChangeCommand::ImageSequenceChangeCommand(VideoStreamPtr video_stream, int64_t start_index, int64_t duration, QUndoCommand *parent) : + UndoCommand(parent), + video_stream_(video_stream), + new_start_index_(start_index), + new_duration_(duration) +{ +} + +void VideoStreamProperties::ImageSequenceChangeCommand::redo_internal() +{ + old_start_index_ = video_stream_->start_time(); + video_stream_->set_start_time(new_start_index_); + + old_duration_ = video_stream_->duration(); + video_stream_->set_duration(new_duration_); +} + +void VideoStreamProperties::ImageSequenceChangeCommand::undo_internal() +{ + video_stream_->set_start_time(old_start_index_); + video_stream_->set_duration(old_duration_); +} diff --git a/app/dialog/footageproperties/streamproperties/videostreamproperties.h b/app/dialog/footageproperties/streamproperties/videostreamproperties.h index e59bfb84f..183ab7e32 100644 --- a/app/dialog/footageproperties/streamproperties/videostreamproperties.h +++ b/app/dialog/footageproperties/streamproperties/videostreamproperties.h @@ -27,6 +27,7 @@ #include "project/item/footage/videostream.h" #include "streamproperties.h" #include "undo/undocommand.h" +#include "widget/slider/integerslider.h" class VideoStreamProperties : public StreamProperties { @@ -35,7 +36,11 @@ public: virtual void Accept(QUndoCommand* parent) override; + virtual bool SanityCheck() override; + private: + static bool IsImageSequence(ImageStream* stream); + /** * @brief Attached video stream */ @@ -51,6 +56,16 @@ private: */ QComboBox* video_color_space_; + /** + * @brief Sets the start index for image sequences + */ + IntegerSlider* imgseq_start_time_; + + /** + * @brief Sets the end index for image sequences + */ + IntegerSlider* imgseq_end_time_; + class VideoStreamChangeCommand : public UndoCommand { public: VideoStreamChangeCommand(ImageStreamPtr stream, @@ -70,6 +85,29 @@ private: bool old_premultiplied_; QString old_colorspace_; + + }; + + class ImageSequenceChangeCommand : public UndoCommand { + public: + ImageSequenceChangeCommand(VideoStreamPtr video_stream, + int64_t start_index, + int64_t duration, + QUndoCommand* parent = nullptr); + + protected: + virtual void redo_internal() override; + virtual void undo_internal() override; + + private: + VideoStreamPtr video_stream_; + + int64_t new_start_index_; + int64_t old_start_index_; + + int64_t new_duration_; + int64_t old_duration_; + }; }; diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 29189a479..ea27b8c35 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -69,22 +69,18 @@ void MediaInput::FootageChanged() return; } - if (connected_footage_ != nullptr) { - if (connected_footage_->type() == Stream::kImage || connected_footage_->type() == Stream::kVideo) { - disconnect(connected_footage_.get(), SIGNAL(ColorSpaceChanged()), this, SLOT(FootageColorSpaceChanged())); - } + if (connected_footage_) { + disconnect(connected_footage_.get(), &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); } connected_footage_ = new_footage; - if (connected_footage_ != nullptr) { - if (connected_footage_->type() == Stream::kImage || connected_footage_->type() == Stream::kVideo) { - connect(connected_footage_.get(), SIGNAL(ColorSpaceChanged()), this, SLOT(FootageColorSpaceChanged())); - } + if (connected_footage_) { + connect(connected_footage_.get(), &Stream::ParametersChanged, this, &MediaInput::FootageParametersChanged); } } -void MediaInput::FootageColorSpaceChanged() +void MediaInput::FootageParametersChanged() { InvalidateCache(0, RATIONAL_MAX, footage_input_); } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 0253cc231..a19459cae 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -48,7 +48,7 @@ protected: private slots: void FootageChanged(); - void FootageColorSpaceChanged(); + void FootageParametersChanged(); }; diff --git a/app/project/item/footage/imagestream.cpp b/app/project/item/footage/imagestream.cpp index 4cc187623..855c0a806 100644 --- a/app/project/item/footage/imagestream.cpp +++ b/app/project/item/footage/imagestream.cpp @@ -91,11 +91,13 @@ bool ImageStream::premultiplied_alpha() const void ImageStream::set_premultiplied_alpha(bool e) { premultiplied_alpha_ = e; + + emit ParametersChanged(); } -const QString &ImageStream::colorspace() const +const QString &ImageStream::colorspace(bool default_if_empty) const { - if (colorspace_.isEmpty()) { + if (colorspace_.isEmpty() && default_if_empty) { return footage()->project()->default_input_colorspace(); } else { return colorspace_; @@ -106,7 +108,7 @@ void ImageStream::set_colorspace(const QString &color) { colorspace_ = color; - emit ColorSpaceChanged(); + emit ParametersChanged(); } void ImageStream::ColorConfigChanged() @@ -123,13 +125,13 @@ void ImageStream::ColorConfigChanged() } // Either way, the color calculation has likely changed so we signal here - emit ColorSpaceChanged(); + emit ParametersChanged(); } void ImageStream::DefaultColorSpaceChanged() { // If no colorspace is set, this stream uses the default color space and it's just changed if (colorspace_.isEmpty()) { - emit ColorSpaceChanged(); + emit ParametersChanged(); } } diff --git a/app/project/item/footage/imagestream.h b/app/project/item/footage/imagestream.h index 952b0c432..7c6adad2c 100644 --- a/app/project/item/footage/imagestream.h +++ b/app/project/item/footage/imagestream.h @@ -43,12 +43,9 @@ public: bool premultiplied_alpha() const; void set_premultiplied_alpha(bool e); - const QString& colorspace() const; + const QString& colorspace(bool default_if_empty = true) const; void set_colorspace(const QString& color); -signals: - void ColorSpaceChanged(); - protected: virtual void FootageSetEvent(Footage*) override; diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index c7f273271..fc9bb8456 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -107,6 +107,8 @@ const int64_t &Stream::duration() const void Stream::set_duration(const int64_t &duration) { duration_ = duration; + + emit ParametersChanged(); } bool Stream::enabled() const diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 0a0a7a319..b3c774604 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -115,6 +115,8 @@ protected: signals: void IndexChanged(); + void ParametersChanged(); + private: Footage* footage_; diff --git a/app/project/item/footage/videostream.cpp b/app/project/item/footage/videostream.cpp index 5a0fb5df4..36d0f7fd5 100644 --- a/app/project/item/footage/videostream.cpp +++ b/app/project/item/footage/videostream.cpp @@ -27,7 +27,8 @@ const int64_t VideoStream::kEndTimestamp = AV_NOPTS_VALUE; VideoStream::VideoStream() : - start_time_(0) + start_time_(0), + is_image_sequence_(false) { set_type(kVideo); } @@ -57,6 +58,17 @@ const int64_t &VideoStream::start_time() const void VideoStream::set_start_time(const int64_t &start_time) { start_time_ = start_time; + emit ParametersChanged(); +} + +bool VideoStream::is_image_sequence() const +{ + return is_image_sequence_; +} + +void VideoStream::set_image_sequence(bool e) +{ + is_image_sequence_ = e; } int64_t VideoStream::get_closest_timestamp_in_frame_index(const rational &time) diff --git a/app/project/item/footage/videostream.h b/app/project/item/footage/videostream.h index 3fed4bfc4..05ec4ac95 100644 --- a/app/project/item/footage/videostream.h +++ b/app/project/item/footage/videostream.h @@ -25,6 +25,7 @@ class VideoStream : public ImageStream { + Q_OBJECT public: VideoStream(); @@ -43,6 +44,9 @@ public: const int64_t& start_time() const; void set_start_time(const int64_t& start_time); + 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(); @@ -62,6 +66,8 @@ private: QMutex index_access_lock_; + bool is_image_sequence_; + }; using VideoStreamPtr = std::shared_ptr; diff --git a/app/render/backend/videorenderworker.cpp b/app/render/backend/videorenderworker.cpp index 4c4221426..a2de88f3b 100644 --- a/app/render/backend/videorenderworker.cpp +++ b/app/render/backend/videorenderworker.cpp @@ -179,6 +179,8 @@ void VideoRenderWorker::HashNodeRecursively(QCryptographicHash *hash, const Node if (stream->type() == Stream::kVideo) { hash->addData(QStringLiteral("%1/%2").arg(QString::number(input_time.numerator()), QString::number(input_time.denominator())).toUtf8()); + + hash->addData(QString::number(static_cast(stream.get())->start_time()).toUtf8()); /*Decoder::RetrieveState state = decoder->GetRetrieveState(input_time); if (state == Decoder::kReady) {