diff --git a/app/common/CMakeLists.txt b/app/common/CMakeLists.txt index 930eb2d53..b8627409c 100644 --- a/app/common/CMakeLists.txt +++ b/app/common/CMakeLists.txt @@ -35,5 +35,7 @@ set(OLIVE_SOURCES common/threadedobject.cpp common/timecodefunctions.h common/timecodefunctions.cpp + common/timerange.h + common/timerange.cpp PARENT_SCOPE ) diff --git a/app/common/timerange.cpp b/app/common/timerange.cpp new file mode 100644 index 000000000..e4ec160a1 --- /dev/null +++ b/app/common/timerange.cpp @@ -0,0 +1,51 @@ +#include "timerange.h" + +TimeRange::TimeRange() +{ +} + +TimeRange::TimeRange(const rational &in, const rational &out) : + in_(in), + out_(out) +{ + normalize(); +} + +const rational &TimeRange::in() const +{ + return in_; +} + +const rational &TimeRange::out() const +{ + return out_; +} + +void TimeRange::set_in(const rational &in) +{ + in_ = in; + normalize(); +} + +void TimeRange::set_out(const rational &out) +{ + out_ = out; + normalize(); +} + +void TimeRange::set_range(const rational &in, const rational &out) +{ + in_ = in; + out_ = out; + normalize(); +} + +void TimeRange::normalize() +{ + // If `out` is earlier than `in`, swap them + if (out_ < in_) { + rational temp = in_; + in_ = out_; + out_ = temp; + } +} diff --git a/app/common/timerange.h b/app/common/timerange.h new file mode 100644 index 000000000..1ff31992b --- /dev/null +++ b/app/common/timerange.h @@ -0,0 +1,25 @@ +#ifndef TIMERANGE_H +#define TIMERANGE_H + +#include "rational.h" + +class TimeRange { +public: + TimeRange(); + TimeRange(const rational& in, const rational& out); + + const rational& in() const; + const rational& out() const; + + void set_in(const rational& in); + void set_out(const rational& out); + void set_range(const rational& in, const rational& out); + +private: + void normalize(); + + rational in_; + rational out_; +}; + +#endif // TIMERANGE_H diff --git a/app/core.cpp b/app/core.cpp index 01db4d6cf..d85a470ee 100644 --- a/app/core.cpp +++ b/app/core.cpp @@ -274,7 +274,7 @@ void Core::CreateNewSequence() SequencePtr new_sequence = std::make_shared(); // Set all defaults for the sequence - new_sequence->SetDefaultParameters(); + new_sequence->set_default_parameters(); // Get default name for this sequence (in the format "Sequence N", the first that doesn't exist) int sequence_number = 1; diff --git a/app/dialog/sequence/sequence.cpp b/app/dialog/sequence/sequence.cpp index da1f9c13d..29a227203 100644 --- a/app/dialog/sequence/sequence.cpp +++ b/app/dialog/sequence/sequence.cpp @@ -115,16 +115,16 @@ SequenceDialog::SequenceDialog(Sequence* s, Type t, QWidget* parent) : AddFrameRate(rational(60, 1)); // 60 FPS // Set up available sample rates - AddSampleRate(rational(8000, 1)); // 8000 Hz - AddSampleRate(rational(11025, 1)); // 11025 Hz - AddSampleRate(rational(16000, 1)); // 16000 Hz - AddSampleRate(rational(22050, 1)); // 22050 Hz - AddSampleRate(rational(24000, 1)); // 24000 Hz - AddSampleRate(rational(32000, 1)); // 32000 Hz - AddSampleRate(rational(44100, 1)); // 44100 Hz - AddSampleRate(rational(48000, 1)); // 48000 Hz - AddSampleRate(rational(88200, 1)); // 88200 Hz - AddSampleRate(rational(96000, 1)); // 96000 Hz + AddSampleRate(8000); // 8000 Hz + AddSampleRate(11025); // 11025 Hz + AddSampleRate(16000); // 16000 Hz + AddSampleRate(22050); // 22050 Hz + AddSampleRate(24000); // 24000 Hz + AddSampleRate(32000); // 32000 Hz + AddSampleRate(44100); // 44100 Hz + AddSampleRate(48000); // 48000 Hz + AddSampleRate(88200); // 88200 Hz + AddSampleRate(96000); // 96000 Hz // Set up available channel layouts AddChannelLayout(AV_CH_LAYOUT_MONO); @@ -133,17 +133,17 @@ SequenceDialog::SequenceDialog(Sequence* s, Type t, QWidget* parent) : AddChannelLayout(AV_CH_LAYOUT_7POINT1); // Set values based on input sequence - video_width_field_->setValue(sequence_->video_width()); - video_height_field_->setValue(sequence_->video_height()); + video_width_field_->setValue(sequence_->video_params().width()); + video_height_field_->setValue(sequence_->video_params().height()); - int frame_rate_index = frame_rate_list_.indexOf(sequence_->video_time_base().flipped()); + int frame_rate_index = frame_rate_list_.indexOf(sequence_->video_params().time_base().flipped()); video_frame_rate_field_->setCurrentIndex(frame_rate_index); - int sample_rate_index = sample_rate_list_.indexOf(sequence_->audio_time_base().flipped()); + int sample_rate_index = sample_rate_list_.indexOf(sequence_->audio_params().sample_rate()); audio_sample_rate_field_->setCurrentIndex(sample_rate_index); for (int i=0;icount();i++) { - if (audio_channels_field_->itemData(i).toULongLong() == sequence_->audio_channel_layout()) { + if (audio_channels_field_->itemData(i).toULongLong() == sequence_->audio_params().channel_layout()) { audio_channels_field_->setCurrentIndex(i); break; } @@ -169,32 +169,33 @@ void SequenceDialog::accept() // Get the rational at the combobox's index (which will be correct provided AddFrameRate() was used at all time) - rational audio_time_base = sample_rate_list_.at(audio_sample_rate_field_->currentIndex()).flipped(); + int audio_sample_rate = sample_rate_list_.at(audio_sample_rate_field_->currentIndex()); // Get the audio channel layout value uint64_t channels = audio_channels_field_->currentData().toULongLong(); + // Generate video and audio parameter structs from data + VideoParams video_params = VideoParams(video_width_field_->value(), + video_height_field_->value(), + video_time_base); + + AudioParams audio_params = AudioParams(audio_sample_rate, + channels); + if (make_undoable_) { // Make undoable command to change the parameters SequenceParamCommand* param_command = new SequenceParamCommand(sequence_, - video_width_field_->value(), - video_height_field_->value(), - video_time_base, - audio_time_base, - channels); + video_params, + audio_params, + name_field_->text()); olive::undo_stack.push(param_command); } else { // Set sequence values directly with no undo command - sequence_->set_video_width(video_width_field_->value()); - sequence_->set_video_height(video_height_field_->value()); - sequence_->set_video_time_base(video_time_base); - - sequence_->set_audio_time_base(audio_time_base); - sequence_->set_audio_channel_layout(channels); - + sequence_->set_video_params(video_params); + sequence_->set_audio_params(audio_params); sequence_->set_name(name_field_->text()); } @@ -208,11 +209,11 @@ void SequenceDialog::AddFrameRate(const rational &r) video_frame_rate_field_->addItem(tr("%1 FPS").arg(r.toDouble())); } -void SequenceDialog::AddSampleRate(const rational &rate) +void SequenceDialog::AddSampleRate(const int &rate) { sample_rate_list_.append(rate); - audio_sample_rate_field_->addItem(tr("%1 Hz").arg(rate.toDouble())); + audio_sample_rate_field_->addItem(tr("%1 Hz").arg(rate)); } void SequenceDialog::AddChannelLayout(int layout) @@ -239,42 +240,32 @@ void SequenceDialog::AddChannelLayout(int layout) audio_channels_field_->addItem(layout_name, layout); } -SequenceDialog::SequenceParamCommand::SequenceParamCommand(Sequence *s, - const int &width, - const int &height, - const rational &v_timebase, - const rational &a_timebase, - const uint64_t &channels, - QUndoCommand *parent): +SequenceDialog::SequenceParamCommand::SequenceParamCommand(Sequence* s, + const VideoParams& video_params, + const AudioParams& audio_params, + const QString& name, + QUndoCommand* parent) : QUndoCommand(parent), sequence_(s), - width_(width), - height_(height), - v_timebase_(v_timebase), - a_timebase_(a_timebase), - channels_(channels), - old_width_(s->video_width()), - old_height_(s->video_height()), - old_v_timebase_(s->video_time_base()), - old_a_timebase_(s->audio_time_base()), - old_channels_(s->audio_channel_layout()) + new_video_params_(video_params), + new_audio_params_(audio_params), + new_name_(name), + old_video_params_(s->video_params()), + old_audio_params_(s->audio_params()), + old_name_(s->name()) { } void SequenceDialog::SequenceParamCommand::redo() { - sequence_->set_video_width(width_); - sequence_->set_video_height(height_); - sequence_->set_video_time_base(v_timebase_); - sequence_->set_audio_time_base(a_timebase_); - sequence_->set_audio_channel_layout(channels_); + sequence_->set_video_params(new_video_params_); + sequence_->set_audio_params(new_audio_params_); + sequence_->set_name(new_name_); } void SequenceDialog::SequenceParamCommand::undo() { - sequence_->set_video_width(old_width_); - sequence_->set_video_height(old_height_); - sequence_->set_video_time_base(old_v_timebase_); - sequence_->set_audio_time_base(old_a_timebase_); - sequence_->set_audio_channel_layout(old_channels_); + sequence_->set_video_params(old_video_params_); + sequence_->set_audio_params(old_audio_params_); + sequence_->set_name(old_name_); } diff --git a/app/dialog/sequence/sequence.h b/app/dialog/sequence/sequence.h index 6ada69f10..c4e53206f 100644 --- a/app/dialog/sequence/sequence.h +++ b/app/dialog/sequence/sequence.h @@ -90,7 +90,7 @@ private: /** * @brief Internal function for adding a selectable sample rate */ - void AddSampleRate(const rational &rate); + void AddSampleRate(const int &rate); /** * @brief Internal function for adding a selectable channel layout @@ -115,7 +115,7 @@ private: QVector frame_rate_list_; - QVector sample_rate_list_; + QVector sample_rate_list_; /** * @brief A QUndoCommand for setting the parameters on a sequence @@ -123,11 +123,9 @@ private: class SequenceParamCommand : public QUndoCommand { public: SequenceParamCommand(Sequence* s, - const int& width, - const int& height, - const rational& v_timebase, - const rational& a_timebase, - const uint64_t &channels, + const VideoParams& video_params, + const AudioParams& audio_params, + const QString& name, QUndoCommand* parent = nullptr); virtual void redo() override; @@ -135,17 +133,13 @@ private: private: Sequence* sequence_; - int width_; - int height_; - rational v_timebase_; - rational a_timebase_; - uint64_t channels_; + VideoParams new_video_params_; + AudioParams new_audio_params_; + QString new_name_; - int old_width_; - int old_height_; - rational old_v_timebase_; - rational old_a_timebase_; - uint64_t old_channels_; + VideoParams old_video_params_; + AudioParams old_audio_params_; + QString old_name_; }; }; diff --git a/app/node/block/clip/clip.cpp b/app/node/block/clip/clip.cpp index a0b7a4bc9..029ba5ff1 100644 --- a/app/node/block/clip/clip.cpp +++ b/app/node/block/clip/clip.cpp @@ -99,7 +99,7 @@ QList ClipBlock::RunDependencies(NodeOutput *output, const ratio QList deps; if (output == texture_output() && texture_input_->IsConnected()) { - deps.append(NodeDependency(texture_input_->get_connected_output(), SequenceToMediaTime(time))); + deps.append(NodeDependency(texture_input_->get_connected_output(), SequenceToMediaTime(time), SequenceToMediaTime(time))); } return deps; diff --git a/app/node/dependency.cpp b/app/node/dependency.cpp index a98117a57..c86e1e829 100644 --- a/app/node/dependency.cpp +++ b/app/node/dependency.cpp @@ -25,9 +25,15 @@ NodeDependency::NodeDependency() : { } -NodeDependency::NodeDependency(NodeOutput *node, const rational &time) : +NodeDependency::NodeDependency(NodeOutput *node, const TimeRange &range) : node_(node), - time_(time) + range_(range) +{ +} + +NodeDependency::NodeDependency(NodeOutput *node, const rational &in, const rational &out) : + node_(node), + range_(in, out) { } @@ -36,7 +42,17 @@ NodeOutput *NodeDependency::node() const return node_; } -const rational& NodeDependency::time() const +const rational& NodeDependency::in() const { - return time_; + return range_.in(); +} + +const rational &NodeDependency::out() const +{ + return range_.out(); +} + +const TimeRange &NodeDependency::range() const +{ + return range_; } diff --git a/app/node/dependency.h b/app/node/dependency.h index 7fa719173..718c5f6a2 100644 --- a/app/node/dependency.h +++ b/app/node/dependency.h @@ -23,20 +23,23 @@ #include -#include "common/rational.h" +#include "common/timerange.h" #include "node/output.h" class NodeDependency { public: NodeDependency(); - NodeDependency(NodeOutput* node, const rational& time); + NodeDependency(NodeOutput* node, const TimeRange& range); + NodeDependency(NodeOutput* node, const rational& in, const rational &out); NodeOutput* node() const; - const rational& time() const; + const rational& in() const; + const rational& out() const; + const TimeRange& range() const; private: NodeOutput* node_; - rational time_; + TimeRange range_; }; Q_DECLARE_METATYPE(NodeDependency) diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 985edb7d2..3cf4ee5f3 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -173,7 +173,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &in, const rationa // OpenColorIO v1's color transforms can be done on GPU, which improves performance but reduces accuracy. When // online, we prefer accuracy over performance so we use the CPU path instead: // NOTE: OCIO v2 boasts 1:1 results with the CPU and GPU path so this won't be necessary forever - if (renderer->mode() == olive::RenderMode::kOnline) { + if (renderer->params().mode() == olive::RenderMode::kOnline) { // Convert to 32F, which is required for OpenColorIO's color transformation frame_ = PixelService::ConvertPixelFormat(frame_, olive::PIX_FMT_RGBA32F); @@ -220,15 +220,15 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &in, const rationa RenderTexturePtr output_texture = std::make_shared(); output_texture->Create(renderer->context(), - renderer->width(), - renderer->height(), - renderer->format(), - RenderTexture::kDoubleBuffer); + renderer->params().width(), + renderer->params().height(), + renderer->params().format(), + RenderTexture::kDoubleBuffer); // Using the transformation matrix, blit our internal texture (in frame format) to our output texture (in // reference format) - if (renderer->mode() == olive::RenderMode::kOffline) { + if (renderer->params().mode() == olive::RenderMode::kOffline) { // For offline rendering, OCIO's GPU path is acceptable: // NOTE: OCIO v2 boasts 1:1 results with the CPU and GPU path so this won't be necessary forever @@ -259,8 +259,8 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &in, const rationa QMatrix4x4 transform; // Scale texture to a square for incoming matrix transformation - transform.scale(2.0f / static_cast(renderer->width() * renderer->divider()), - 2.0f / static_cast(renderer->height() * renderer->divider())); + transform.scale(2.0f / static_cast(renderer->params().width() * renderer->params().divider()), + 2.0f / static_cast(renderer->params().height() * renderer->params().divider())); // Multiply by input transformation transform *= matrix_input_->get_value(in).value(); @@ -273,7 +273,7 @@ QVariant MediaInput::Value(NodeOutput *output, const rational &in, const rationa //transform.scale(media_size, media_size); // Use pipeline to blit using transformation matrix from input - if (renderer->mode() == olive::RenderMode::kOffline) { + if (renderer->params().mode() == olive::RenderMode::kOffline) { olive::gl::OCIOBlit(pipeline_, ocio_texture_, false, transform); } else { olive::gl::Blit(pipeline_, false, transform); diff --git a/app/node/node.cpp b/app/node/node.cpp index f8374b6f6..d5f361fb5 100644 --- a/app/node/node.cpp +++ b/app/node/node.cpp @@ -293,7 +293,7 @@ QList Node::RunDependencies(NodeOutput *output, const rational & NodeOutput* potential_dep = input->get_connected_output(); if (potential_dep != nullptr) { - run_deps.append(NodeDependency(potential_dep, time)); + run_deps.append(NodeDependency(potential_dep, time, time)); } } } @@ -367,7 +367,7 @@ void Node::Hash(QCryptographicHash *hash, NodeOutput* from, const rational &time QList deps = RunDependencies(from, time); foreach (const NodeDependency& dep, deps) { // Hash the connected node - dep.node()->parent()->Hash(hash, dep.node(), dep.time()); + dep.node()->parent()->Hash(hash, dep.node(), dep.in()); } } diff --git a/app/node/output/track/track.cpp b/app/node/output/track/track.cpp index b6d997b17..2e2209cbc 100644 --- a/app/node/output/track/track.cpp +++ b/app/node/output/track/track.cpp @@ -131,7 +131,7 @@ QList TrackOutput::RunDependencies(NodeOutput* output, const rat ValidateCurrentBlock(time); if (current_block_ != this) { - deps.append(NodeDependency(current_block_->texture_output(), time)); + deps.append(NodeDependency(current_block_->texture_output(), time, time)); } } diff --git a/app/node/output/viewer/viewer.cpp b/app/node/output/viewer/viewer.cpp index a82eed662..de21fa3b2 100644 --- a/app/node/output/viewer/viewer.cpp +++ b/app/node/output/viewer/viewer.cpp @@ -20,9 +20,7 @@ #include "viewer.h" -ViewerOutput::ViewerOutput() : - viewer_width_(0), - viewer_height_(0) +ViewerOutput::ViewerOutput() { texture_input_ = new NodeInput("tex_in"); texture_input_->add_data_input(NodeInput::kTexture); @@ -57,18 +55,6 @@ QString ViewerOutput::Description() return tr("Interface between a Viewer panel and the node system."); } -const rational &ViewerOutput::Timebase() -{ - return timebase_; -} - -void ViewerOutput::SetTimebase(const rational &timebase) -{ - timebase_ = timebase; - - emit TimebaseChanged(timebase_); -} - NodeInput *ViewerOutput::texture_input() { return texture_input_; @@ -103,22 +89,27 @@ void ViewerOutput::InvalidateCache(const rational &start_range, const rational & SendInvalidateCache(start_range, end_range); } -void ViewerOutput::SetViewerSize(const int &width, const int &height) +const VideoParams &ViewerOutput::video_params() { - viewer_width_ = width; - viewer_height_ = height; - - emit SizeChanged(viewer_width_, viewer_height_); + return video_params_; } -const int &ViewerOutput::ViewerWidth() +const AudioParams &ViewerOutput::audio_params() { - return viewer_width_; + return audio_params_; } -const int &ViewerOutput::ViewerHeight() +void ViewerOutput::set_video_params(const VideoParams &video) { - return viewer_height_; + video_params_ = video; + + emit SizeChanged(video_params_.width(), video_params_.height()); + emit TimebaseChanged(video_params_.time_base()); +} + +void ViewerOutput::set_audio_params(const AudioParams &audio) +{ + audio_params_ = audio; } rational ViewerOutput::Length() diff --git a/app/node/output/viewer/viewer.h b/app/node/output/viewer/viewer.h index 44732a0e6..8b38c3f2a 100644 --- a/app/node/output/viewer/viewer.h +++ b/app/node/output/viewer/viewer.h @@ -22,6 +22,8 @@ #define VIEWER_H #include "node/node.h" +#include "render/videoparams.h" +#include "render/audio/audioparams.h" #include "render/rendertexture.h" /** @@ -40,9 +42,6 @@ public: virtual QString Category() override; virtual QString Description() override; - const rational& Timebase(); - void SetTimebase(const rational& timebase); - NodeInput* texture_input(); NodeInput* samples_input(); NodeInput* length_input(); @@ -52,10 +51,11 @@ public: virtual void InvalidateCache(const rational &start_range, const rational &end_range, NodeInput *from = nullptr) override; - void SetViewerSize(const int& width, const int& height); + const VideoParams& video_params(); + const AudioParams& audio_params(); - const int& ViewerWidth(); - const int& ViewerHeight(); + void set_video_params(const VideoParams& video); + void set_audio_params(const AudioParams& audio); rational Length(); @@ -78,9 +78,9 @@ private: rational timebase_; - int viewer_width_; + VideoParams video_params_; - int viewer_height_; + AudioParams audio_params_; }; diff --git a/app/project/item/sequence/sequence.cpp b/app/project/item/sequence/sequence.cpp index ef4ff584b..ba28f41c3 100644 --- a/app/project/item/sequence/sequence.cpp +++ b/app/project/item/sequence/sequence.cpp @@ -84,9 +84,7 @@ void Sequence::add_default_nodes() NodeParam::ConnectEdge(audio_track_output_->track_output(), timeline_output_->track_input(kTrackTypeAudio)); // Update the timebase on these nodes - //video_renderer_processor_->SetCacheName(name()); - set_video_time_base(video_time_base_); - update_video_parameters(); + set_video_params(video_params_); } Item::Type Sequence::type() const @@ -107,102 +105,48 @@ QString Sequence::duration() rational timeline_length = timeline_output_->length_output()->get_value(0, 0).value(); - int64_t timestamp = olive::time_to_timestamp(timeline_length, video_time_base_); + int64_t timestamp = olive::time_to_timestamp(timeline_length, video_params_.time_base()); - return olive::timestamp_to_timecode(timestamp, video_time_base_, olive::CurrentTimecodeDisplay()); + return olive::timestamp_to_timecode(timestamp, video_params_.time_base(), olive::CurrentTimecodeDisplay()); } QString Sequence::rate() { - return QCoreApplication::translate("Sequence", "%1 FPS").arg(video_time_base_.flipped().toDouble()); + return QCoreApplication::translate("Sequence", "%1 FPS").arg(video_params_.time_base().flipped().toDouble()); } -const int &Sequence::video_width() +const VideoParams &Sequence::video_params() { - return video_width_; + return video_params_; } -void Sequence::set_video_width(const int &width) +void Sequence::set_video_params(const VideoParams &vparam) { - video_width_ = width; - - update_video_parameters(); -} - -const int &Sequence::video_height() const -{ - return video_height_; -} - -void Sequence::set_video_height(const int &height) -{ - video_height_ = height; - - update_video_parameters(); -} - -const rational &Sequence::video_time_base() -{ - return video_time_base_; -} - -void Sequence::set_video_time_base(const rational &time_base) -{ - video_time_base_ = time_base; - - if (timeline_output_ != nullptr) - timeline_output_->SetTimebase(video_time_base_); + video_params_ = vparam; if (viewer_output_ != nullptr) - viewer_output_->SetTimebase(video_time_base_); + viewer_output_->set_video_params(video_params_); + + if (timeline_output_ != nullptr) + timeline_output_->SetTimebase(video_params_.time_base()); } -const rational &Sequence::audio_time_base() +const AudioParams &Sequence::audio_params() { - return audio_time_base_; + return audio_params_; } -void Sequence::set_audio_time_base(const rational &time_base) +void Sequence::set_audio_params(const AudioParams ¶ms) { - audio_time_base_ = time_base; + audio_params_ = params; + + if (viewer_output_ != nullptr) + viewer_output_->set_audio_params(audio_params_); } -const uint64_t &Sequence::audio_channel_layout() +void Sequence::set_default_parameters() { - return audio_channel_layout_; -} - -void Sequence::set_audio_channel_layout(const uint64_t &channel_layout) -{ - audio_channel_layout_ = channel_layout; -} - -void Sequence::SetDefaultParameters() -{ - // FIXME: Make these configurable - set_video_width(1920); - set_video_height(1080); - set_video_time_base(rational(1001, 30000)); - - set_audio_time_base(rational(1, 48000)); - set_audio_channel_layout(AV_CH_LAYOUT_STEREO); -} - -void Sequence::update_video_parameters() -{ - /*if (video_renderer_processor_ != nullptr) { - // Set renderer's parameters based on sequence's parameters - video_renderer_processor_->SetParameters(video_width_, - video_height_, - olive::PIX_FMT_RGBA16F, // FIXME: Make this configurable - olive::RenderMode::kOffline, - 2); - - // Set the "cache name" only here to aid the cache ID's uniqueness - video_renderer_processor_->SetCacheName(name()); - }*/ - - if (viewer_output_ != nullptr) { - viewer_output_->SetViewerSize(video_width_, video_height_); - } + // FIXME: Make these configurable (hardcoded) + set_video_params(VideoParams(1920, 1080, rational(1001, 30000))); + set_audio_params(AudioParams(48000, AV_CH_LAYOUT_STEREO)); } diff --git a/app/project/item/sequence/sequence.h b/app/project/item/sequence/sequence.h index 7b525ab0a..a94ebedae 100644 --- a/app/project/item/sequence/sequence.h +++ b/app/project/item/sequence/sequence.h @@ -25,6 +25,7 @@ #include "node/graph.h" #include "node/output/timeline/timeline.h" #include "node/output/viewer/viewer.h" +#include "render/videoparams.h" #include "render/video/videorenderer.h" #include "project/item/item.h" @@ -53,41 +54,23 @@ public: virtual QString duration() override; virtual QString rate() override; - /* VIDEO GETTER/SETTER FUNCTIONS */ + const VideoParams& video_params(); + void set_video_params(const VideoParams& vparam); - const int& video_width(); - void set_video_width(const int& width); + const AudioParams& audio_params(); + void set_audio_params(const AudioParams& params); - const int& video_height() const; - void set_video_height(const int& height); - - const rational& video_time_base(); - void set_video_time_base(const rational& time_base); - - /* AUDIO GETTER/SETTER FUNCTIONS */ - - const rational& audio_time_base(); - void set_audio_time_base(const rational& time_base); - - const uint64_t& audio_channel_layout(); - void set_audio_channel_layout(const uint64_t& channel_layout); - - void SetDefaultParameters(); + void set_default_parameters(); private: - void update_video_parameters(); - TimelineOutput* timeline_output_; ViewerOutput* viewer_output_; TrackOutput* video_track_output_; TrackOutput* audio_track_output_; - int video_width_; - int video_height_; - rational video_time_base_; + VideoParams video_params_; - rational audio_time_base_; - uint64_t audio_channel_layout_; + AudioParams audio_params_; }; #endif // SEQUENCE_H diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index a7234a734..f360554ff 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -35,5 +35,7 @@ set(OLIVE_SOURCES render/renderframebuffer.cpp render/rendertexture.h render/rendertexture.cpp + render/videoparams.h + render/videoparams.cpp PARENT_SCOPE ) diff --git a/app/render/audio/CMakeLists.txt b/app/render/audio/CMakeLists.txt index f29c143c9..ba57a7a4d 100644 --- a/app/render/audio/CMakeLists.txt +++ b/app/render/audio/CMakeLists.txt @@ -16,6 +16,8 @@ set(OLIVE_SOURCES ${OLIVE_SOURCES} + render/audio/audioparams.h + render/audio/audioparams.cpp render/audio/audiorenderer.h render/audio/audiorenderer.cpp render/audio/audiorendererdownloadthread.h diff --git a/app/render/audio/audioparams.cpp b/app/render/audio/audioparams.cpp new file mode 100644 index 000000000..facaaf457 --- /dev/null +++ b/app/render/audio/audioparams.cpp @@ -0,0 +1,89 @@ +#include "audioparams.h" + +extern "C" { +#include +} + +AudioParams::AudioParams() : + sample_rate_(0), + channel_layout_(0) +{ +} + +AudioParams::AudioParams(const int &sample_rate, const uint64_t &channel_layout) : + sample_rate_(sample_rate), + channel_layout_(channel_layout) +{ +} + +const int &AudioParams::sample_rate() const +{ + return sample_rate_; +} + +const uint64_t &AudioParams::channel_layout() const +{ + return channel_layout_; +} + +AudioRenderingParams::AudioRenderingParams() : + format_(olive::SAMPLE_FMT_INVALID) +{ +} + +AudioRenderingParams::AudioRenderingParams(const int &sample_rate, const uint64_t &channel_layout, const olive::SampleFormat &format) : + AudioParams(sample_rate, channel_layout), + format_(format) +{ +} + +AudioRenderingParams::AudioRenderingParams(const AudioParams ¶ms, const olive::SampleFormat &format) : + AudioParams(params), + format_(format) +{ +} + +const olive::SampleFormat &AudioRenderingParams::format() const +{ + return format_; +} + +int AudioRenderingParams::time_to_bytes(const rational &time) const +{ + Q_ASSERT(is_valid()); + + return qFloor(time.toDouble() * sample_rate()) * channel_count() * sample_size(); +} + +int AudioRenderingParams::channel_count() const +{ + return av_get_channel_layout_nb_channels(channel_layout()); +} + +int AudioRenderingParams::sample_size() const +{ + switch (format_) { + case olive::SAMPLE_FMT_U8: + return 1; + case olive::SAMPLE_FMT_S16: + return 2; + case olive::SAMPLE_FMT_S32: + case olive::SAMPLE_FMT_FLT: + return 4; + case olive::SAMPLE_FMT_DBL: + return 8; + case olive::SAMPLE_FMT_INVALID: + case olive::SAMPLE_FMT_COUNT: + break; + } + + return 0; +} + +bool AudioRenderingParams::is_valid() const +{ + return (sample_rate() > 0 + && channel_layout() > 0 + && format_ != olive::SAMPLE_FMT_INVALID + && format_ != olive::SAMPLE_FMT_COUNT); +} diff --git a/app/render/audio/audioparams.h b/app/render/audio/audioparams.h new file mode 100644 index 000000000..9f657108c --- /dev/null +++ b/app/render/audio/audioparams.h @@ -0,0 +1,42 @@ +#ifndef AUDIOPARAMS_H +#define AUDIOPARAMS_H + +#include + +#include "audio/sampleformat.h" +#include "common/rational.h" + +class AudioParams +{ +public: + AudioParams(); + AudioParams(const int& sample_rate, const uint64_t& channel_layout); + + const int& sample_rate() const; + const uint64_t& channel_layout() const; + +private: + int sample_rate_; + + uint64_t channel_layout_; + +}; + +class AudioRenderingParams : public AudioParams { +public: + AudioRenderingParams(); + AudioRenderingParams(const int& sample_rate, const uint64_t& channel_layout, const olive::SampleFormat& format); + AudioRenderingParams(const AudioParams& params, const olive::SampleFormat& format); + + int time_to_bytes(const rational& time) const; + int channel_count() const; + int sample_size() const; + bool is_valid() const; + + const olive::SampleFormat& format() const; + +private: + olive::SampleFormat format_; +}; + +#endif // AUDIOPARAMS_H diff --git a/app/render/audio/audiorenderer.cpp b/app/render/audio/audiorenderer.cpp index b92722390..8e4d8b201 100644 --- a/app/render/audio/audiorenderer.cpp +++ b/app/render/audio/audiorenderer.cpp @@ -36,11 +36,7 @@ AudioRendererProcessor::AudioRendererProcessor(QObject *parent) : QObject(parent), started_(false), - width_(0), - height_(0), - divider_(1), caching_(false), - push_time_(-1), starting_(false), viewer_node_(nullptr) { @@ -63,10 +59,6 @@ void AudioRendererProcessor::SetCacheName(const QString &s) void AudioRendererProcessor::InvalidateCache(const rational &start_range, const rational &end_range) { - if (timebase_.isNull()) { - return; - } - // Adjust range to min/max values rational start_range_adj = qMax(rational(0), start_range); rational end_range_adj = qMin(viewer_node_->Length(), end_range); @@ -76,101 +68,45 @@ void AudioRendererProcessor::InvalidateCache(const rational &start_range, const << "and" << end_range_adj.toDouble(); - // Snap start_range to timebase - double start_range_dbl = start_range_adj.toDouble(); - double start_range_numf = start_range_dbl * static_cast(timebase_.denominator()); - int64_t start_range_numround = qFloor(start_range_numf/static_cast(timebase_.numerator())) * timebase_.numerator(); - rational true_start_range(start_range_numround, timebase_.denominator()); + bool append = true; - for (rational r=true_start_range;r<=end_range_adj;r+=timebase_) { - // Try to order the queue from closest to the playhead to furthest - rational last_time = last_time_requested_; + for (int i=0;i::iterator insert_iterator; - - for (QLinkedList::iterator i = cache_queue_.begin();i != cache_queue_.end();i++) { - rational compare = *i; - - if (!added) { - rational compare_diff = compare - last_time; - - if (compare_diff > diff) { - insert_iterator = i; - added = true; - } + if (start_range_adj >= const_range.in() + && start_range_adj <= const_range.out()) { + append = false; + if (const_range.out() < end_range_adj) { + // Same in point but longer, extend + cache_queue_[i].set_out(end_range_adj); } - - if (compare == r) { - contains = true; - break; + break; + } else if (end_range_adj <= const_range.out() + && end_range_adj >= const_range.in()) { + append = false; + if (const_range.in() > start_range_adj) { + // Same out point but longer, extend + cache_queue_[i].set_in(start_range_adj); } + break; } + } - if (!contains) { - if (added) { - cache_queue_.insert(insert_iterator, r); - } else { - cache_queue_.append(r); - } - } + if (append) { + cache_queue_.append(TimeRange(start_range_adj, end_range_adj)); } CacheNext(); } -void AudioRendererProcessor::SetTimebase(const rational &timebase) -{ - timebase_ = timebase; - timebase_dbl_ = timebase_.toDouble(); -} - -void AudioRendererProcessor::SetParameters(const int &width, - const int &height, - const olive::PixelFormat &format, - const olive::RenderMode &mode, - const int& divider) +void AudioRendererProcessor::SetParameters(const AudioRenderingParams& params) { // Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again // next time this Node has to process anything. Stop(); // Set new parameters - width_ = width; - height_ = height; - format_ = format; - mode_ = mode; - - // divider's default value is 0, so we can assume if it's 0 a divider wasn't specified - if (divider > 0) { - divider_ = divider; - } - - CalculateEffectiveDimensions(); - - // Regenerate the cache ID - GenerateCacheIDInternal(); -} - -void AudioRendererProcessor::SetDivider(const int ÷r) -{ - Q_ASSERT(divider_ > 0); - - Stop(); - - divider_ = divider; - - CalculateEffectiveDimensions(); + params_ = params; // Regenerate the cache ID GenerateCacheIDInternal(); @@ -193,7 +129,7 @@ void AudioRendererProcessor::Start() threads_.resize(background_thread_count); for (int i=0;i(this, ctx, effective_width_, effective_height_, divider_, format_, mode_); + threads_[i] = std::make_shared(this, params_); threads_[i]->StartThread(QThread::LowPriority); // Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the @@ -207,46 +143,14 @@ void AudioRendererProcessor::Start() // Connect first thread (master thread) to the callback connect(threads_.first().get(), - SIGNAL(CachedFrame(RenderTexturePtr, const rational&, const QByteArray&)), + SIGNAL(CachedFrame(const QByteArray&, const rational&, const rational&)), this, - SLOT(ThreadCallback(RenderTexturePtr, const rational&, const QByteArray&)), + SLOT(ThreadCallback(const QByteArray&, const rational&, const rational&)), Qt::QueuedConnection); - connect(threads_.first().get(), - SIGNAL(FrameSkipped(const rational&, const QByteArray&)), - this, - SLOT(ThreadSkippedFrame(const rational&, const QByteArray&)), - Qt::QueuedConnection); - - download_threads_.resize(background_thread_count); - - for (int i=0;i(ctx, effective_width_, effective_height_, divider_, format_, mode_); - download_threads_[i]->StartThread(QThread::LowPriority); - - connect(download_threads_[i].get(), - SIGNAL(Downloaded(const QByteArray&)), - this, - SLOT(DownloadThreadComplete(const QByteArray&)), - Qt::QueuedConnection); - } - - last_download_thread_ = 0; // Restore context now that thread creation is complete ctx->makeCurrent(old_surface); - // Create master texture (the one sent to the viewer) - master_texture_ = std::make_shared(); - master_texture_->Create(ctx, effective_width_, effective_height_, format_); - - // Create internal FBO for copying textures - copy_buffer_.Create(ctx); - copy_buffer_.Attach(master_texture_); - copy_pipeline_ = olive::ShaderGenerator::DefaultPipeline(); - - cache_frame_load_buffer_.resize(PixelService::GetBufferSize(format_, effective_width_, effective_height_)); - started_ = true; } @@ -258,26 +162,15 @@ void AudioRendererProcessor::Stop() started_ = false; - foreach (AudioRendererDownloadThreadPtr download_thread_, download_threads_) { - download_thread_->Cancel(); - } - download_threads_.clear(); - foreach (AudioRendererProcessThreadPtr process_thread, threads_) { process_thread->Cancel(); } threads_.clear(); - - copy_buffer_.Destroy(); - master_texture_ = nullptr; - copy_pipeline_ = nullptr; - - cache_frame_load_buffer_.clear(); } void AudioRendererProcessor::GenerateCacheIDInternal() { - if (cache_name_.isEmpty() || effective_width_ == 0 || effective_height_ == 0) { + if (cache_name_.isEmpty() || !params_.is_valid()) { return; } @@ -285,10 +178,9 @@ void AudioRendererProcessor::GenerateCacheIDInternal() QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(cache_name_.toUtf8()); hash.addData(QString::number(cache_time_).toUtf8()); - hash.addData(QString::number(width_).toUtf8()); - hash.addData(QString::number(height_).toUtf8()); - hash.addData(QString::number(format_).toUtf8()); - hash.addData(QString::number(divider_).toUtf8()); + hash.addData(QString::number(params_.sample_rate()).toUtf8()); + hash.addData(QString::number(params_.channel_layout()).toUtf8()); + hash.addData(QString::number(params_.format()).toUtf8()); QByteArray bytes = hash.result(); cache_id_ = bytes.toHex(); @@ -303,9 +195,9 @@ void AudioRendererProcessor::CacheNext() // Make sure cache has started Start(); - rational cache_frame = cache_queue_.takeFirst(); + TimeRange cache_frame = cache_queue_.takeFirst(); - qDebug() << "Caching" << cache_frame.toDouble(); + qDebug() << "Caching" << cache_frame.in().toDouble() << "-" << cache_frame.out().toDouble(); threads_.first()->Queue(NodeDependency(viewer_node_->texture_input()->get_connected_output(), cache_frame), true, false); @@ -317,90 +209,25 @@ QString AudioRendererProcessor::CachePathName(const QByteArray &hash) QDir this_cache_dir = QDir(GetMediaCacheLocation()).filePath(cache_id_); this_cache_dir.mkpath("."); - QString filename = QString("%1.exr").arg(QString(hash.toHex())); + QString filename = QString("%1.pcm").arg(QString(hash.toHex())); return this_cache_dir.filePath(filename); } -void AudioRendererProcessor::DeferMap(const rational &time, const QByteArray &hash) -{ - deferred_maps_.append({time, hash}); -} - -bool AudioRendererProcessor::HasHash(const QByteArray &hash) -{ - return QFileInfo::exists(CachePathName(hash)); -} - -bool AudioRendererProcessor::IsCaching(const QByteArray &hash) -{ - cache_hash_list_mutex_.lock(); - - bool is_caching = cache_hash_list_.contains(hash); - - cache_hash_list_mutex_.unlock(); - - return is_caching; -} - -bool AudioRendererProcessor::TryCache(const QByteArray &hash) -{ - cache_hash_list_mutex_.lock(); - - bool is_caching = cache_hash_list_.contains(hash); - - if (!is_caching) { - cache_hash_list_.append(hash); - } - - cache_hash_list_mutex_.unlock(); - - return !is_caching; -} - -void AudioRendererProcessor::CalculateEffectiveDimensions() -{ - effective_width_ = width_ / divider_; - effective_height_ = height_ / divider_; -} - -void AudioRendererProcessor::ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash) +void AudioRendererProcessor::ThreadCallback(const QByteArray& samples, const rational& in, const rational& out) { // Threads are all done now, time to proceed caching_ = false; - DeferMap(time, hash); + int start_offset = params_.time_to_bytes(in); + int end_offset = params_.time_to_bytes(out); - if (texture != nullptr) { - // We received a texture, time to start downloading it - QString fn = CachePathName(hash); - - download_threads_[last_download_thread_%download_threads_.size()]->Queue(texture, - fn, - hash); - - last_download_thread_++; - } else { - // There was no texture here, we must update the viewer - DownloadThreadComplete(hash); + // Ensure sample cache is at least large enough for this + if (sample_cache_.size() < end_offset) { + sample_cache_.resize(end_offset); } - // If the connected output is using this time, signal it to update - if (last_time_requested_ == time) { - copy_buffer_.Bind(); - texture->Bind(); - - QOpenGLContext::currentContext()->functions()->glViewport(0, 0, master_texture_->width(), master_texture_->height()); - - olive::gl::Blit(copy_pipeline_); - - texture->Release(); - copy_buffer_.Release(); - - push_time_ = time; - - emit CachedFrameReady(time); - } + sample_cache_.replace(start_offset, samples.size(), samples); CacheNext(); } @@ -415,107 +242,53 @@ void AudioRendererProcessor::ThreadRequestSibling(NodeDependency dep) } } -void AudioRendererProcessor::ThreadSkippedFrame(const rational& time, const QByteArray& hash) -{ - caching_ = false; - - DeferMap(time, hash); - - if (!IsCaching(hash)) { - DownloadThreadComplete(hash); - - // Signal output to update value - emit CachedFrameReady(time); - } - - CacheNext(); -} - -void AudioRendererProcessor::DownloadThreadComplete(const QByteArray &hash) -{ - cache_hash_list_mutex_.lock(); - cache_hash_list_.removeAll(hash); - cache_hash_list_mutex_.unlock(); - - for (int i=0;i(QThread::currentThread()); } -RenderInstance *AudioRendererProcessor::CurrentInstance() +AudioParams *AudioRendererProcessor::CurrentInstance() { AudioRendererThreadBase* thread = CurrentThread(); if (thread != nullptr) { - return thread->render_instance(); + return thread->params(); } return nullptr; } -RenderTexturePtr AudioRendererProcessor::GetCachedFrame(const rational &time) +QByteArray AudioRendererProcessor::GetCachedSamples(const rational &in, const rational &out) { - last_time_requested_ = time; - - if (push_time_ >= 0) { - rational temp_push_time = push_time_; - push_time_ = -1; - - if (time == temp_push_time) { - return master_texture_; - } - } - - if (viewer_node_ == nullptr) { + if (viewer_node_ == nullptr || in == out) { // Nothing is connected - nothing to show or render return nullptr; } + if (!params_.is_valid()) { + qWarning() << "Invalid parameters"; + return nullptr; + } + if (cache_id_.isEmpty()) { - qWarning() << "RendererProcessor has no cache ID"; + qWarning() << "No cache ID"; return nullptr; } - if (timebase_.isNull()) { - qWarning() << "RendererProcessor has no timebase"; + if (out < in || in < 0 || out < 0) { + qWarning() << "Invalid time requested"; return nullptr; } - // Find frame in map - if (time_hash_map_.contains(time)) { - QString fn = CachePathName(time_hash_map_[time]); + int start_offset = qMin(params_.time_to_bytes(in), sample_cache_.size()); + int end_offset = qMin(params_.time_to_bytes(out), sample_cache_.size()); + int length = end_offset - start_offset; - if (QFileInfo::exists(fn)) { - auto in = OIIO::ImageInput::open(fn.toStdString()); - - if (in) { - in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data()); - - in->close(); - - master_texture_->Upload(cache_frame_load_buffer_.data()); - - return master_texture_; - } else { - qWarning() << "OIIO Error:" << OIIO::geterror().c_str(); - } - } + if (length == 0) { + return nullptr; } - return nullptr; + return sample_cache_.mid(start_offset, length); } void AudioRendererProcessor::SetViewerNode(ViewerOutput *viewer) @@ -530,9 +303,6 @@ void AudioRendererProcessor::SetViewerNode(ViewerOutput *viewer) connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); // FIXME: Hardcoded format and mode - SetParameters(viewer_node_->ViewerWidth(), - viewer_node_->ViewerHeight(), - olive::PIX_FMT_RGBA16F, - olive::kOffline); + AudioRenderingParams(viewer_node_->audio_params(), olive::SAMPLE_FMT_FLT); } } diff --git a/app/render/audio/audiorenderer.h b/app/render/audio/audiorenderer.h index b3f047d46..e19ca4c49 100644 --- a/app/render/audio/audiorenderer.h +++ b/app/render/audio/audiorenderer.h @@ -24,6 +24,7 @@ #include #include +#include "common/timerange.h" #include "node/output/viewer/viewer.h" #include "render/pixelformat.h" #include "render/rendermodes.h" @@ -49,8 +50,6 @@ public: void SetCacheName(const QString& s); - void SetTimebase(const rational& timebase); - /** * @brief Set parameters of the Renderer * @@ -69,28 +68,7 @@ public: * * Buffer pixel format */ - void SetParameters(const int& width, - const int& height, - const olive::PixelFormat& format, - const olive::RenderMode& mode, - const int ÷r = 0); - - void SetDivider(const int& divider); - - /** - * @brief Return whether a frame with this hash already exists - */ - bool HasHash(const QByteArray& hash); - - /** - * @brief Return whether a frame is currently being cached - */ - bool IsCaching(const QByteArray& hash); - - /** - * @brief Check if a frame is currently being cached, and if not reserve it - */ - bool TryCache(const QByteArray& hash); + void SetParameters(const AudioRenderingParams ¶ms); /** * @brief Return current instance of a RenderThread (or nullptr if there is none) @@ -100,21 +78,13 @@ public: */ static AudioRendererThreadBase* CurrentThread(); - static RenderInstance* CurrentInstance(); + static AudioParams* CurrentInstance(); - RenderTexturePtr GetCachedFrame(const rational& time); + QByteArray GetCachedSamples(const rational& in, const rational& out); void SetViewerNode(ViewerOutput* viewer); -signals: - void CachedFrameReady(const rational& time); - private: - struct HashTimeMapping { - rational time; - QByteArray hash; - }; - /** * @brief Allocate and start the multithreaded backend */ @@ -137,15 +107,11 @@ private: */ void CacheNext(); - bool ShouldPushTexture(const rational &time); - /** * @brief Return the path of the cached image at this time */ QString CachePathName(const QByteArray &hash); - void DeferMap(const rational &time, const QByteArray &hash); - /** * @brief Internal list of RenderProcessThreads */ @@ -156,63 +122,28 @@ private: */ bool started_; - int width_; - int height_; + AudioRenderingParams params_; - void CalculateEffectiveDimensions(); - - int divider_; - int effective_width_; - int effective_height_; - - olive::PixelFormat format_; - - olive::RenderMode mode_; - - rational last_time_requested_; - - rational timebase_; - double timebase_dbl_; - - QLinkedList cache_queue_; + QList cache_queue_; QString cache_name_; qint64 cache_time_; QString cache_id_; bool caching_; - QVector cache_frame_load_buffer_; - - QVector download_threads_; - int last_download_thread_; - - RenderTexturePtr master_texture_; - rational push_time_; - - RenderFramebuffer copy_buffer_; - ShaderPtr copy_pipeline_; - - QMap time_hash_map_; - - QMutex cache_hash_list_mutex_; - QVector cache_hash_list_; - - QList deferred_maps_; bool starting_; ViewerOutput* viewer_node_; + QByteArray sample_cache_; + private slots: void InvalidateCache(const rational &start_range, const rational &end_range); - void ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash); + void ThreadCallback(const QByteArray& samples, const rational& in, const rational &out); void ThreadRequestSibling(NodeDependency dep); - void ThreadSkippedFrame(const rational &time, const QByteArray &hash); - - void DownloadThreadComplete(const QByteArray &hash); - }; #endif // AUDIORENDERER_H diff --git a/app/render/audio/audiorendererdownloadthread.cpp b/app/render/audio/audiorendererdownloadthread.cpp index fc69ebeec..d6bf0538f 100644 --- a/app/render/audio/audiorendererdownloadthread.cpp +++ b/app/render/audio/audiorendererdownloadthread.cpp @@ -7,7 +7,7 @@ #include "common/define.h" #include "render/pixelservice.h" -AudioRendererDownloadThread::AudioRendererDownloadThread(QOpenGLContext *share_ctx, +/*AudioRendererDownloadThread::AudioRendererDownloadThread(QOpenGLContext *share_ctx, const int &width, const int &height, const int ÷r, @@ -125,4 +125,4 @@ void AudioRendererDownloadThread::ProcessLoop() } f->glDeleteFramebuffers(1, &read_buffer_); -} +}*/ diff --git a/app/render/audio/audiorendererdownloadthread.h b/app/render/audio/audiorendererdownloadthread.h index 7cb2222c9..f77a3eb99 100644 --- a/app/render/audio/audiorendererdownloadthread.h +++ b/app/render/audio/audiorendererdownloadthread.h @@ -3,7 +3,7 @@ #include "audiorendererthreadbase.h" -class AudioRendererDownloadThread : public AudioRendererThreadBase +/*class AudioRendererDownloadThread : public AudioRendererThreadBase { Q_OBJECT public: @@ -44,6 +44,6 @@ private: }; -using AudioRendererDownloadThreadPtr = std::shared_ptr; +using AudioRendererDownloadThreadPtr = std::shared_ptr;*/ #endif // AUDIORENDERERDOWNLOADTHREAD_H diff --git a/app/render/audio/audiorendererprocessthread.cpp b/app/render/audio/audiorendererprocessthread.cpp index 6e8970086..53ace48af 100644 --- a/app/render/audio/audiorendererprocessthread.cpp +++ b/app/render/audio/audiorendererprocessthread.cpp @@ -23,13 +23,8 @@ #include "audiorenderer.h" AudioRendererProcessThread::AudioRendererProcessThread(AudioRendererProcessor* parent, - QOpenGLContext *share_ctx, - const int &width, - const int &height, - const int ÷r, - const olive::PixelFormat &format, - const olive::RenderMode &mode) : - AudioRendererThreadBase(share_ctx, width, height, divider, format, mode), + const AudioRenderingParams ¶ms) : + AudioRendererThreadBase(params), parent_(parent), cancelled_(false) { @@ -93,49 +88,20 @@ void AudioRendererProcessThread::ProcessLoop() NodeOutput* output_to_process = path_.node(); Node* node_to_process = output_to_process->parent(); - texture_ = nullptr; - QList all_deps; - bool has_hash = false; - bool can_cache = true; - if (!sibling_) { - node_to_process->Lock(); + QList deps = node_to_process->RunDependencies(output_to_process, path_.in()); - all_deps = node_to_process->GetDependencies(); - foreach (Node* dep, all_deps) { - dep->Lock(); - } - - // Check hash - QCryptographicHash hasher(QCryptographicHash::Sha1); - node_to_process->Hash(&hasher, output_to_process, path_.time()); - hash_ = hasher.result(); - - has_hash = parent_->HasHash(hash_); - can_cache = false; - } - - if (!has_hash){ - - if ((can_cache = parent_->TryCache(hash_))) { - - QList deps = node_to_process->RunDependencies(output_to_process, path_.time()); - - // Ask for other threads to run these deps while we're here - if (!deps.isEmpty()) { - for (int i=1;iget_value(path_.time(), path_.time()).value(); - - render_instance()->context()->functions()->glFinish(); + // Ask for other threads to run these deps while we're here + if (!deps.isEmpty()) { + for (int i=1;iget_value(path_.in(), path_.out()).toByteArray(); + if (!sibling_) { foreach (Node* dep, all_deps) { dep->Unlock(); @@ -144,12 +110,7 @@ void AudioRendererProcessThread::ProcessLoop() node_to_process->Unlock(); } - if (can_cache) { - // We cached this frame, signal that it will need to be downloaded to disk - emit CachedFrame(texture_, path_.time(), hash_); - } else { - // This hash already exists, no need to cache, just map it - emit FrameSkipped(path_.time(), hash_); - } + // Signal that we cached some samples + emit CachedSamples(samples, path_.in(), path_.out()); } } diff --git a/app/render/audio/audiorendererprocessthread.h b/app/render/audio/audiorendererprocessthread.h index a02ab0643..91b728c4f 100644 --- a/app/render/audio/audiorendererprocessthread.h +++ b/app/render/audio/audiorendererprocessthread.h @@ -30,11 +30,7 @@ class AudioRendererProcessThread : public AudioRendererThreadBase Q_OBJECT public: AudioRendererProcessThread(AudioRendererProcessor* parent, - QOpenGLContext* share_ctx, - const int& width, - const int& height, const int ÷r, - const olive::PixelFormat& format, - const olive::RenderMode& mode); + const AudioRenderingParams ¶ms); bool Queue(const NodeDependency &dep, bool wait, bool sibling); @@ -47,19 +43,13 @@ protected: signals: void RequestSibling(NodeDependency dep); - void CachedFrame(RenderTexturePtr texture, const rational& time, const QByteArray& hash); - - void FrameSkipped(const rational& time, const QByteArray& hash); + void CachedSamples(const QByteArray& samples, const rational& in, const rational& out); private: AudioRendererProcessor* parent_; NodeDependency path_; - QByteArray hash_; - - RenderTexturePtr texture_; - QAtomicInt cancelled_; bool sibling_; diff --git a/app/render/audio/audiorendererthreadbase.cpp b/app/render/audio/audiorendererthreadbase.cpp index e1544c424..7045de398 100644 --- a/app/render/audio/audiorendererthreadbase.cpp +++ b/app/render/audio/audiorendererthreadbase.cpp @@ -22,16 +22,14 @@ #include -AudioRendererThreadBase::AudioRendererThreadBase(QOpenGLContext *share_ctx, const int &width, const int &height, const int ÷r, const olive::PixelFormat &format, const olive::RenderMode &mode) : - share_ctx_(share_ctx), - render_instance_(width, height, divider, format, mode) +AudioRendererThreadBase::AudioRendererThreadBase(const AudioRenderingParams ¶ms) : + params_(params) { - connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); } -RenderInstance *AudioRendererThreadBase::render_instance() +AudioParams *AudioRendererThreadBase::params() { - return &render_instance_; + return ¶ms_; } void AudioRendererThreadBase::run() @@ -39,23 +37,11 @@ void AudioRendererThreadBase::run() // Lock mutex for main loop mutex_.lock(); - render_instance_.SetShareContext(share_ctx_); - - // Allocate and create resources - bool started = render_instance_.Start(); - // Signal that main thread can continue now WakeCaller(); - if (started) { - - // Main loop (use Cancel() to exit it) - ProcessLoop(); - - } - - // Free all resources - render_instance_.Stop(); + // Main loop (use Cancel() to exit it) + ProcessLoop(); // Unlock mutex before exiting mutex_.unlock(); diff --git a/app/render/audio/audiorendererthreadbase.h b/app/render/audio/audiorendererthreadbase.h index 5e6e72872..b5ddfb8d3 100644 --- a/app/render/audio/audiorendererthreadbase.h +++ b/app/render/audio/audiorendererthreadbase.h @@ -26,21 +26,16 @@ #include #include +#include "audioparams.h" #include "node/node.h" -#include "render/renderinstance.h" class AudioRendererThreadBase : public QThread { Q_OBJECT public: - AudioRendererThreadBase(QOpenGLContext* share_ctx, - const int& width, - const int& height, - const int& divider, - const olive::PixelFormat& format, - const olive::RenderMode& mode); + AudioRendererThreadBase(const AudioRenderingParams ¶ms); - RenderInstance* render_instance(); + AudioParams* params(); void StartThread(Priority priority = InheritPriority); @@ -61,9 +56,7 @@ protected: private: void WakeCaller(); - QOpenGLContext* share_ctx_; - - RenderInstance render_instance_; + AudioRenderingParams params_; }; diff --git a/app/render/renderinstance.cpp b/app/render/renderinstance.cpp index c22f80765..e575e8d17 100644 --- a/app/render/renderinstance.cpp +++ b/app/render/renderinstance.cpp @@ -24,17 +24,9 @@ #include "render/gl/shadergenerators.h" -RenderInstance::RenderInstance(const int& width, - const int& height, - const int& divider, - const olive::PixelFormat& format, - const olive::RenderMode& mode) : +RenderInstance::RenderInstance(const VideoRenderingParams& params) : share_ctx_(nullptr), - width_(width), - height_(height), - format_(format), - mode_(mode), - divider_(divider) + params_(params) { // Create offscreen surface surface_.create(); @@ -82,7 +74,7 @@ bool RenderInstance::Start() buffer_.Create(ctx_); // Set viewport to the compositing dimensions - ctx_->functions()->glViewport(0, 0, width_, height_); + ctx_->functions()->glViewport(0, 0, params_.width(), params_.height()); ctx_->functions()->glEnable(GL_BLEND); // Set up default pipeline @@ -122,29 +114,9 @@ QOpenGLContext *RenderInstance::context() return ctx_; } -const int &RenderInstance::width() const +const VideoRenderingParams &RenderInstance::params() const { - return width_; -} - -const int &RenderInstance::height() const -{ - return height_; -} - -const int &RenderInstance::divider() const -{ - return divider_; -} - -const olive::PixelFormat &RenderInstance::format() const -{ - return format_; -} - -const olive::RenderMode &RenderInstance::mode() const -{ - return mode_; + return params_; } ShaderPtr RenderInstance::default_pipeline() const diff --git a/app/render/renderinstance.h b/app/render/renderinstance.h index 12e488cf0..4229b9f83 100644 --- a/app/render/renderinstance.h +++ b/app/render/renderinstance.h @@ -28,6 +28,7 @@ #include "render/gl/shaderptr.h" #include "render/renderframebuffer.h" #include "render/rendermodes.h" +#include "render/videoparams.h" /** * @brief An object containing all resources necessary for each thread to support hardware accelerated rendering @@ -40,11 +41,7 @@ class RenderInstance : public QObject { public: - RenderInstance(const int& width, - const int& height, - const int& divider, - const olive::PixelFormat& format, - const olive::RenderMode& mode); + RenderInstance(const VideoRenderingParams ¶ms); virtual ~RenderInstance() override; @@ -80,15 +77,7 @@ public: QOpenGLContext* context(); - const int& width() const; - - const int& height() const; - - const int& divider() const; - - const olive::PixelFormat& format() const; - - const olive::RenderMode& mode() const; + const VideoRenderingParams& params() const; ShaderPtr default_pipeline() const; @@ -101,15 +90,7 @@ private: RenderFramebuffer buffer_; - int width_; - - int height_; - - olive::PixelFormat format_; - - olive::RenderMode mode_; - - int divider_; + VideoRenderingParams params_; ShaderPtr default_pipeline_; }; diff --git a/app/render/video/videorenderer.cpp b/app/render/video/videorenderer.cpp index fb694892c..3bda31620 100644 --- a/app/render/video/videorenderer.cpp +++ b/app/render/video/videorenderer.cpp @@ -36,9 +36,6 @@ VideoRendererProcessor::VideoRendererProcessor(QObject *parent) : QObject(parent), started_(false), - width_(0), - height_(0), - divider_(1), caching_(false), push_time_(-1), starting_(false), @@ -63,7 +60,7 @@ void VideoRendererProcessor::SetCacheName(const QString &s) void VideoRendererProcessor::InvalidateCache(const rational &start_range, const rational &end_range) { - if (timebase_.isNull()) { + if (!params_.is_valid()) { return; } @@ -78,11 +75,11 @@ void VideoRendererProcessor::InvalidateCache(const rational &start_range, const // Snap start_range to timebase double start_range_dbl = start_range_adj.toDouble(); - double start_range_numf = start_range_dbl * static_cast(timebase_.denominator()); - int64_t start_range_numround = qFloor(start_range_numf/static_cast(timebase_.numerator())) * timebase_.numerator(); - rational true_start_range(start_range_numround, timebase_.denominator()); + double start_range_numf = start_range_dbl * static_cast(params_.time_base().denominator()); + int64_t start_range_numround = qFloor(start_range_numf/static_cast(params_.time_base().numerator())) * params_.time_base().numerator(); + rational true_start_range(start_range_numround, params_.time_base().denominator()); - for (rational r=true_start_range;r<=end_range_adj;r+=timebase_) { + for (rational r=true_start_range;r<=end_range_adj;r+=params_.time_base()) { // Try to order the queue from closest to the playhead to furthest rational last_time = last_time_requested_; @@ -129,48 +126,14 @@ void VideoRendererProcessor::InvalidateCache(const rational &start_range, const CacheNext(); } -void VideoRendererProcessor::SetTimebase(const rational &timebase) -{ - timebase_ = timebase; - timebase_dbl_ = timebase_.toDouble(); -} - -void VideoRendererProcessor::SetParameters(const int &width, - const int &height, - const olive::PixelFormat &format, - const olive::RenderMode &mode, - const int& divider) +void VideoRendererProcessor::SetParameters(const VideoRenderingParams& params) { // Since we're changing parameters, all the existing threads are invalid and must be removed. They will start again // next time this Node has to process anything. Stop(); // Set new parameters - width_ = width; - height_ = height; - format_ = format; - mode_ = mode; - - // divider's default value is 0, so we can assume if it's 0 a divider wasn't specified - if (divider > 0) { - divider_ = divider; - } - - CalculateEffectiveDimensions(); - - // Regenerate the cache ID - GenerateCacheIDInternal(); -} - -void VideoRendererProcessor::SetDivider(const int ÷r) -{ - Q_ASSERT(divider_ > 0); - - Stop(); - - divider_ = divider; - - CalculateEffectiveDimensions(); + params_ = params; // Regenerate the cache ID GenerateCacheIDInternal(); @@ -193,7 +156,7 @@ void VideoRendererProcessor::Start() threads_.resize(background_thread_count); for (int i=0;i(this, ctx, effective_width_, effective_height_, divider_, format_, mode_); + threads_[i] = std::make_shared(this, ctx, params_); threads_[i]->StartThread(QThread::LowPriority); // Ensure this connection is "Queued" so that it always runs in this object's threaded rather than any of the @@ -221,7 +184,7 @@ void VideoRendererProcessor::Start() for (int i=0;i(ctx, effective_width_, effective_height_, divider_, format_, mode_); + download_threads_[i] = std::make_shared(ctx, params_); download_threads_[i]->StartThread(QThread::LowPriority); connect(download_threads_[i].get(), @@ -238,14 +201,14 @@ void VideoRendererProcessor::Start() // Create master texture (the one sent to the viewer) master_texture_ = std::make_shared(); - master_texture_->Create(ctx, effective_width_, effective_height_, format_); + master_texture_->Create(ctx, params_.effective_width(), params_.effective_height(), params_.format()); // Create internal FBO for copying textures copy_buffer_.Create(ctx); copy_buffer_.Attach(master_texture_); copy_pipeline_ = olive::ShaderGenerator::DefaultPipeline(); - cache_frame_load_buffer_.resize(PixelService::GetBufferSize(format_, effective_width_, effective_height_)); + cache_frame_load_buffer_.resize(PixelService::GetBufferSize(params_.format(), params_.effective_width(), params_.effective_height())); started_ = true; } @@ -277,7 +240,7 @@ void VideoRendererProcessor::Stop() void VideoRendererProcessor::GenerateCacheIDInternal() { - if (cache_name_.isEmpty() || effective_width_ == 0 || effective_height_ == 0) { + if (cache_name_.isEmpty() || !params_.is_valid()) { return; } @@ -285,10 +248,10 @@ void VideoRendererProcessor::GenerateCacheIDInternal() QCryptographicHash hash(QCryptographicHash::Sha1); hash.addData(cache_name_.toUtf8()); hash.addData(QString::number(cache_time_).toUtf8()); - hash.addData(QString::number(width_).toUtf8()); - hash.addData(QString::number(height_).toUtf8()); - hash.addData(QString::number(format_).toUtf8()); - hash.addData(QString::number(divider_).toUtf8()); + hash.addData(QString::number(params_.width()).toUtf8()); + hash.addData(QString::number(params_.height()).toUtf8()); + hash.addData(QString::number(params_.format()).toUtf8()); + hash.addData(QString::number(params_.divider()).toUtf8()); QByteArray bytes = hash.result(); cache_id_ = bytes.toHex(); @@ -307,7 +270,7 @@ void VideoRendererProcessor::CacheNext() qDebug() << "Caching" << cache_frame.toDouble(); - threads_.first()->Queue(NodeDependency(viewer_node_->texture_input()->get_connected_output(), cache_frame), true, false); + threads_.first()->Queue(NodeDependency(viewer_node_->texture_input()->get_connected_output(), cache_frame, cache_frame), true, false); caching_ = true; } @@ -358,12 +321,6 @@ bool VideoRendererProcessor::TryCache(const QByteArray &hash) return !is_caching; } -void VideoRendererProcessor::CalculateEffectiveDimensions() -{ - effective_width_ = width_ / divider_; - effective_height_ = height_ / divider_; -} - void VideoRendererProcessor::ThreadCallback(RenderTexturePtr texture, const rational& time, const QByteArray& hash) { // Threads are all done now, time to proceed @@ -485,12 +442,12 @@ RenderTexturePtr VideoRendererProcessor::GetCachedFrame(const rational &time) } if (cache_id_.isEmpty()) { - qWarning() << "RendererProcessor has no cache ID"; + qWarning() << "No cache ID"; return nullptr; } - if (timebase_.isNull()) { - qWarning() << "RendererProcessor has no timebase"; + if (!params_.is_valid()) { + qWarning() << "Invalid parameters"; return nullptr; } @@ -502,7 +459,7 @@ RenderTexturePtr VideoRendererProcessor::GetCachedFrame(const rational &time) auto in = OIIO::ImageInput::open(fn.toStdString()); if (in) { - in->read_image(PixelService::GetPixelFormatInfo(format_).oiio_desc, cache_frame_load_buffer_.data()); + in->read_image(PixelService::GetPixelFormatInfo(params_.format()).oiio_desc, cache_frame_load_buffer_.data()); in->close(); @@ -529,10 +486,7 @@ void VideoRendererProcessor::SetViewerNode(ViewerOutput *viewer) if (viewer_node_ != nullptr) { connect(viewer_node_, SIGNAL(TextureChangedBetween(const rational&, const rational&)), this, SLOT(InvalidateCache(const rational&, const rational&))); - // FIXME: Hardcoded format and mode - SetParameters(viewer_node_->ViewerWidth(), - viewer_node_->ViewerHeight(), - olive::PIX_FMT_RGBA16F, - olive::kOffline); + // FIXME: Hardcoded format, mode, and divider + SetParameters(VideoRenderingParams(viewer_node_->video_params(), olive::PIX_FMT_RGBA16F, olive::kOffline, 2)); } } diff --git a/app/render/video/videorenderer.h b/app/render/video/videorenderer.h index d12f3f125..0eae2479f 100644 --- a/app/render/video/videorenderer.h +++ b/app/render/video/videorenderer.h @@ -49,8 +49,6 @@ public: void SetCacheName(const QString& s); - void SetTimebase(const rational& timebase); - /** * @brief Set parameters of the Renderer * @@ -69,13 +67,7 @@ public: * * Buffer pixel format */ - void SetParameters(const int& width, - const int& height, - const olive::PixelFormat& format, - const olive::RenderMode& mode, - const int ÷r = 0); - - void SetDivider(const int& divider); + void SetParameters(const VideoRenderingParams ¶ms); /** * @brief Return whether a frame with this hash already exists @@ -156,24 +148,10 @@ private: */ bool started_; - int width_; - int height_; - - void CalculateEffectiveDimensions(); - - int divider_; - int effective_width_; - int effective_height_; - - olive::PixelFormat format_; - - olive::RenderMode mode_; + VideoRenderingParams params_; rational last_time_requested_; - rational timebase_; - double timebase_dbl_; - QLinkedList cache_queue_; QString cache_name_; qint64 cache_time_; diff --git a/app/render/video/videorendererdownloadthread.cpp b/app/render/video/videorendererdownloadthread.cpp index 0d9966cdb..f936495b9 100644 --- a/app/render/video/videorendererdownloadthread.cpp +++ b/app/render/video/videorendererdownloadthread.cpp @@ -8,12 +8,8 @@ #include "render/pixelservice.h" VideoRendererDownloadThread::VideoRendererDownloadThread(QOpenGLContext *share_ctx, - const int &width, - const int &height, - const int ÷r, - const olive::PixelFormat &format, - const olive::RenderMode &mode) : - VideoRendererThreadBase(share_ctx, width, height, divider, format, mode), + const VideoRenderingParams& params) : + VideoRendererThreadBase(share_ctx, params), cancelled_(false) { } @@ -49,17 +45,17 @@ void VideoRendererDownloadThread::ProcessLoop() DownloadQueueEntry entry; - int buffer_size = PixelService::GetBufferSize(render_instance()->format(), - render_instance()->width(), - render_instance()->height()); + int buffer_size = PixelService::GetBufferSize(render_instance()->params().format(), + render_instance()->params().width(), + render_instance()->params().height()); QVector data_buffer; data_buffer.resize(buffer_size); - PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(render_instance()->format()); + PixelFormatInfo format_info = PixelService::GetPixelFormatInfo(render_instance()->params().format()); // Set up OIIO::ImageSpec for compressing cached images on disk - OIIO::ImageSpec spec(render_instance()->width(), render_instance()->height(), kRGBAChannels, format_info.oiio_desc); + OIIO::ImageSpec spec(render_instance()->params().width(), render_instance()->params().height(), kRGBAChannels, format_info.oiio_desc); spec.attribute("compression", "dwaa:200"); while (!cancelled_) { diff --git a/app/render/video/videorendererdownloadthread.h b/app/render/video/videorendererdownloadthread.h index bc6f291c2..556d4d6be 100644 --- a/app/render/video/videorendererdownloadthread.h +++ b/app/render/video/videorendererdownloadthread.h @@ -8,11 +8,7 @@ class VideoRendererDownloadThread : public VideoRendererThreadBase Q_OBJECT public: VideoRendererDownloadThread(QOpenGLContext* share_ctx, - const int& width, - const int& height, - const int ÷r, - const olive::PixelFormat& format, - const olive::RenderMode& mode); + const VideoRenderingParams ¶ms); void Queue(RenderTexturePtr texture, const QString &fn, const QByteArray &hash); diff --git a/app/render/video/videorendererprocessthread.cpp b/app/render/video/videorendererprocessthread.cpp index 67f7f79d1..f28906cdc 100644 --- a/app/render/video/videorendererprocessthread.cpp +++ b/app/render/video/videorendererprocessthread.cpp @@ -24,12 +24,8 @@ RendererProcessThread::RendererProcessThread(VideoRendererProcessor* parent, QOpenGLContext *share_ctx, - const int &width, - const int &height, - const int ÷r, - const olive::PixelFormat &format, - const olive::RenderMode &mode) : - VideoRendererThreadBase(share_ctx, width, height, divider, format, mode), + const VideoRenderingParams ¶ms) : + VideoRendererThreadBase(share_ctx, params), parent_(parent), cancelled_(false) { @@ -109,7 +105,7 @@ void RendererProcessThread::ProcessLoop() // Check hash QCryptographicHash hasher(QCryptographicHash::Sha1); - node_to_process->Hash(&hasher, output_to_process, path_.time()); + node_to_process->Hash(&hasher, output_to_process, path_.in()); hash_ = hasher.result(); has_hash = parent_->HasHash(hash_); @@ -120,7 +116,7 @@ void RendererProcessThread::ProcessLoop() if ((can_cache = parent_->TryCache(hash_))) { - QList deps = node_to_process->RunDependencies(output_to_process, path_.time()); + QList deps = node_to_process->RunDependencies(output_to_process, path_.in()); // Ask for other threads to run these deps while we're here if (!deps.isEmpty()) { @@ -130,7 +126,7 @@ void RendererProcessThread::ProcessLoop() } // Get the requested value - texture_ = output_to_process->get_value(path_.time(), path_.time()).value(); + texture_ = output_to_process->get_value(path_.in(), path_.in()).value(); render_instance()->context()->functions()->glFinish(); } @@ -146,10 +142,10 @@ void RendererProcessThread::ProcessLoop() if (can_cache) { // We cached this frame, signal that it will need to be downloaded to disk - emit CachedFrame(texture_, path_.time(), hash_); + emit CachedFrame(texture_, path_.in(), hash_); } else { // This hash already exists, no need to cache, just map it - emit FrameSkipped(path_.time(), hash_); + emit FrameSkipped(path_.in(), hash_); } } } diff --git a/app/render/video/videorendererprocessthread.h b/app/render/video/videorendererprocessthread.h index ef1ed9fc8..bf5b51275 100644 --- a/app/render/video/videorendererprocessthread.h +++ b/app/render/video/videorendererprocessthread.h @@ -31,10 +31,7 @@ class RendererProcessThread : public VideoRendererThreadBase public: RendererProcessThread(VideoRendererProcessor* parent, QOpenGLContext* share_ctx, - const int& width, - const int& height, const int ÷r, - const olive::PixelFormat& format, - const olive::RenderMode& mode); + const VideoRenderingParams ¶ms); bool Queue(const NodeDependency &dep, bool wait, bool sibling); diff --git a/app/render/video/videorendererthreadbase.cpp b/app/render/video/videorendererthreadbase.cpp index 6c53c11c7..602d7a229 100644 --- a/app/render/video/videorendererthreadbase.cpp +++ b/app/render/video/videorendererthreadbase.cpp @@ -22,9 +22,9 @@ #include -VideoRendererThreadBase::VideoRendererThreadBase(QOpenGLContext *share_ctx, const int &width, const int &height, const int ÷r, const olive::PixelFormat &format, const olive::RenderMode &mode) : +VideoRendererThreadBase::VideoRendererThreadBase(QOpenGLContext *share_ctx, const VideoRenderingParams ¶ms) : share_ctx_(share_ctx), - render_instance_(width, height, divider, format, mode) + render_instance_(params) { connect(share_ctx_, SIGNAL(aboutToBeDestroyed()), this, SLOT(Cancel())); } diff --git a/app/render/video/videorendererthreadbase.h b/app/render/video/videorendererthreadbase.h index 879db21ce..592017d3d 100644 --- a/app/render/video/videorendererthreadbase.h +++ b/app/render/video/videorendererthreadbase.h @@ -33,12 +33,7 @@ class VideoRendererThreadBase : public QThread { Q_OBJECT public: - VideoRendererThreadBase(QOpenGLContext* share_ctx, - const int& width, - const int& height, - const int& divider, - const olive::PixelFormat& format, - const olive::RenderMode& mode); + VideoRendererThreadBase(QOpenGLContext* share_ctx, const VideoRenderingParams& params); RenderInstance* render_instance(); diff --git a/app/render/videoparams.cpp b/app/render/videoparams.cpp new file mode 100644 index 000000000..84210f6ed --- /dev/null +++ b/app/render/videoparams.cpp @@ -0,0 +1,93 @@ +#include "videoparams.h" + +VideoParams::VideoParams() : + width_(0), + height_(0) +{ + +} + +VideoParams::VideoParams(const int &width, const int &height, const rational &time_base) : + width_(width), + height_(height), + time_base_(time_base) +{ +} + +const int &VideoParams::width() const +{ + return width_; +} + +const int &VideoParams::height() const +{ + return height_; +} + +const rational &VideoParams::time_base() const +{ + return time_base_; +} + +VideoRenderingParams::VideoRenderingParams() : + format_(olive::PIX_FMT_INVALID) +{ +} + +VideoRenderingParams::VideoRenderingParams(const int &width, const int &height, const rational &time_base, const olive::PixelFormat &format, const olive::RenderMode& mode, const int ÷r) : + VideoParams(width, height, time_base), + format_(format), + mode_(mode), + divider_(divider) +{ + calculate_effective_size(); +} + +VideoRenderingParams::VideoRenderingParams(const VideoParams ¶ms, const olive::PixelFormat &format, const olive::RenderMode& mode, const int& divider) : + VideoParams(params), + format_(format), + mode_(mode), + divider_(divider) +{ + calculate_effective_size(); +} + +const int &VideoRenderingParams::divider() const +{ + return divider_; +} + +const int& VideoRenderingParams::effective_width() const +{ + return effective_width_; +} + +const int& VideoRenderingParams::effective_height() const +{ + return effective_height_; +} + +const olive::PixelFormat &VideoRenderingParams::format() const +{ + return format_; +} + +const olive::RenderMode &VideoRenderingParams::mode() const +{ + return mode_; +} + +void VideoRenderingParams::calculate_effective_size() +{ + effective_width_ = width() / divider_; + effective_height_ = height() / divider_; +} + +bool VideoRenderingParams::is_valid() const +{ + return (width() > 0 + && height() > 0 + && !time_base().isNull() + && format_ != olive::PIX_FMT_INVALID + && format_ != olive::PIX_FMT_COUNT); +} diff --git a/app/render/videoparams.h b/app/render/videoparams.h new file mode 100644 index 000000000..f2ada83cf --- /dev/null +++ b/app/render/videoparams.h @@ -0,0 +1,50 @@ +#ifndef VIDEOPARAMS_H +#define VIDEOPARAMS_H + +#include "common/rational.h" +#include "pixelformat.h" +#include "rendermodes.h" + +class VideoParams +{ +public: + VideoParams(); + VideoParams(const int& width, const int& height, const rational& time_base); + + const int& width() const; + const int& height() const; + const rational& time_base() const; + +private: + int width_; + int height_; + rational time_base_; + +}; + +class VideoRenderingParams : public VideoParams { +public: + VideoRenderingParams(); + VideoRenderingParams(const int& width, const int& height, const rational& time_base, const olive::PixelFormat& format, const olive::RenderMode& mode, const int& divider = 1); + VideoRenderingParams(const VideoParams& params, const olive::PixelFormat& format, const olive::RenderMode& mode, const int& divider = 1); + + const int& divider() const; + const int& effective_width() const; + const int& effective_height() const; + + bool is_valid() const; + const olive::PixelFormat& format() const; + const olive::RenderMode& mode() const; + +private: + void calculate_effective_size(); + + olive::PixelFormat format_; + olive::RenderMode mode_; + + int divider_; + int effective_width_; + int effective_height_; +}; + +#endif // VIDEOPARAMS_H diff --git a/app/widget/viewer/viewer.cpp b/app/widget/viewer/viewer.cpp index 7aa98d040..51faab723 100644 --- a/app/widget/viewer/viewer.cpp +++ b/app/widget/viewer/viewer.cpp @@ -97,8 +97,6 @@ void ViewerWidget::SetTimebase(const rational &r) controls_->SetTimebase(r); playback_timer_.setInterval(qFloor(r.toDouble())); - - video_renderer_->SetTimebase(r); } const double &ViewerWidget::scale() @@ -154,12 +152,12 @@ void ViewerWidget::ConnectViewerNode(ViewerOutput *node) UpdateTextureFromNode(GetTime()); if (viewer_node_ != nullptr) { - SetTimebase(viewer_node_->Timebase()); + SetTimebase(viewer_node_->video_params().time_base()); connect(viewer_node_, SIGNAL(TimebaseChanged(const rational&)), this, SLOT(SetTimebase(const rational&))); connect(viewer_node_, SIGNAL(SizeChanged(int, int)), this, SLOT(SizeChangedSlot(int, int))); - SizeChangedSlot(viewer_node_->ViewerWidth(), viewer_node_->ViewerHeight()); + SizeChangedSlot(viewer_node_->video_params().width(), viewer_node_->video_params().height()); } video_renderer_->SetViewerNode(viewer_node_); diff --git a/app/widget/viewer/viewer.h b/app/widget/viewer/viewer.h index 378e58768..182fb7969 100644 --- a/app/widget/viewer/viewer.h +++ b/app/widget/viewer/viewer.h @@ -30,6 +30,7 @@ #include "common/rational.h" #include "node/output/viewer/viewer.h" +#include "render/audio/audiorenderer.h" #include "render/video/videorenderer.h" #include "viewerglwidget.h" #include "viewersizer.h" @@ -112,6 +113,8 @@ private: VideoRendererProcessor* video_renderer_; + AudioRendererProcessor* audio_renderer_; + ViewerSizer* sizer_; ViewerGLWidget* gl_widget_;