From 298ae2f36c5d13137b849115826c4310cdeeb6f5 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 4 Aug 2019 14:45:17 +1000 Subject: [PATCH] mocked up a prototype of decoding data into the node graph --- app/decoder/decoder.cpp | 7 +-- app/decoder/decoder.h | 6 +-- app/decoder/ffmpeg/ffmpegdecoder.cpp | 50 ++++++++++++++++- app/decoder/frame.cpp | 3 +- app/node/input.cpp | 14 +++++ app/node/input.h | 11 ++++ app/node/input/media/media.cpp | 53 ++++++++++++++++--- app/node/input/media/media.h | 5 +- app/project/item/footage/stream.cpp | 8 +-- app/project/item/footage/stream.h | 8 +-- .../footagecombobox/footagecombobox.cpp | 22 ++++++-- app/widget/footagecombobox/footagecombobox.h | 4 ++ .../nodeparamview/nodeparamviewitem.cpp | 3 +- .../nodeparamviewwidgetbridge.cpp | 14 +++-- .../nodeparamview/nodeparamviewwidgetbridge.h | 2 + 15 files changed, 178 insertions(+), 32 deletions(-) diff --git a/app/decoder/decoder.cpp b/app/decoder/decoder.cpp index ab86c2fa6..8f8e316ef 100644 --- a/app/decoder/decoder.cpp +++ b/app/decoder/decoder.cpp @@ -21,7 +21,8 @@ #include "decoder.h" Decoder::Decoder() : - open_(false) + open_(false), + stream_(nullptr) { } @@ -35,12 +36,12 @@ Decoder::~Decoder() { } -Stream *Decoder::stream() +const Stream *Decoder::stream() { return stream_; } -void Decoder::set_stream(Stream *fs) +void Decoder::set_stream(const Stream *fs) { Close(); diff --git a/app/decoder/decoder.h b/app/decoder/decoder.h index 045280694..70378e29a 100644 --- a/app/decoder/decoder.h +++ b/app/decoder/decoder.h @@ -54,8 +54,8 @@ public: virtual ~Decoder(); - Stream* stream(); - void set_stream(Stream* fs); + const Stream* stream(); + void set_stream(const Stream *fs); /** * @brief Probe a footage file and dump metadata about it @@ -148,7 +148,7 @@ protected: bool open_; private: - Stream* stream_; + const Stream* stream_; }; using DecoderPtr = std::shared_ptr; diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 52552461f..160259361 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -122,12 +122,58 @@ bool FFmpegDecoder::Open() FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &length) { - // FIXME: Fill this out + if (!open_ && !Open()) { + return nullptr; + } + +// avcodec_flush_buffers(codec_ctx_); +// av_seek_frame(fmt_ctx_, avstream_->index, 0, AVSEEK_FLAG_BACKWARD); + + AVFrame* frame = av_frame_alloc(); + AVPacket pkt; + av_init_packet(&pkt); + + int ret = 69; + bool eof = false; + + while ((ret = avcodec_receive_frame(codec_ctx_, frame)) == AVERROR(EAGAIN) && !eof) { + ret = av_read_frame(fmt_ctx_, &pkt); + + if (ret == AVERROR_EOF) { + // Don't break so that receive gets called again, but don't try to read again + eof = true; + + // Send a null packet to signal end of + avcodec_send_packet(codec_ctx_, nullptr); + } else if (ret < 0) { + // Handle other error + break; + } else { + // Successful read, send the packet + ret = avcodec_send_packet(codec_ctx_, &pkt); + av_packet_unref(&pkt); + + if (ret < 0) { + break; + } + } + } + + if (ret < 0) { + qWarning() << tr("Failed to retrieve frame from FFmpeg decoder: %1").arg(ret); + av_frame_free(&frame); + return nullptr; + } + + FramePtr frame_container = std::make_shared(); + frame_container->SetAVFrame(frame, avstream_->time_base); Q_UNUSED(timecode) Q_UNUSED(length) - return nullptr; + // Close(); + + return frame_container; } void FFmpegDecoder::Close() diff --git a/app/decoder/frame.cpp b/app/decoder/frame.cpp index 65db2dcb3..031c287b5 100644 --- a/app/decoder/frame.cpp +++ b/app/decoder/frame.cpp @@ -20,6 +20,7 @@ #include "frame.h" +#include #include Frame::Frame() : @@ -71,7 +72,7 @@ void Frame::SetAVFrame(AVFrame *f, AVRational timebase) { FreeChild(); - f = frame_; + frame_ = f; timestamp_ = rational(timebase.num*f->pts, timebase.den); } diff --git a/app/node/input.cpp b/app/node/input.cpp index b63c04b41..65bef54ec 100644 --- a/app/node/input.cpp +++ b/app/node/input.cpp @@ -23,6 +23,7 @@ #include "output.h" NodeInput::NodeInput() : + keyframing_(false), can_accept_multiple_inputs_(false) { // Have at least one keyframe/value active at any time @@ -87,6 +88,19 @@ QVariant NodeInput::get_value(const rational &time) } } +void NodeInput::set_value(const rational &time, const QVariant &value) +{ + if (keyframing()) { + // FIXME: Keyframing code + Q_UNUSED(time) + } else { + keyframes_.first().set_value(value); + + // FIXME: Put correct values here + emit ValueChanged(0, 0); + } +} + bool NodeInput::keyframing() { return keyframing_; diff --git a/app/node/input.h b/app/node/input.h index 2c8f116c0..5951484d8 100644 --- a/app/node/input.h +++ b/app/node/input.h @@ -29,6 +29,7 @@ */ class NodeInput : public NodeParam { + Q_OBJECT public: NodeInput(); @@ -81,6 +82,13 @@ public: */ QVariant get_value(const rational &time); + /** + * @brief Set the value at a given time + * + * This function will only work if there are no outputs connected. + */ + void set_value(const rational& time, const QVariant& value); + /** * @brief Return whether keyframing is enabled on this input or not */ @@ -96,6 +104,9 @@ public: */ const QList& inputs(); +signals: + void ValueChanged(const rational& start, const rational& end); + private: /** * @brief Internal list of accepted data types diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 713a8318f..5eabc699b 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -21,11 +21,16 @@ #include "media.h" #include +#include #include "project/item/footage/footage.h" +// FIXME: Test code only +#include "decoder/ffmpeg/ffmpegdecoder.h" +// End test code + MediaInput::MediaInput() : - texture_(nullptr) + texture_(0) { footage_input_ = new NodeInput(); footage_input_->add_data_input(NodeInput::kFootage); @@ -34,6 +39,10 @@ MediaInput::MediaInput() : texture_output_ = new NodeOutput(); texture_output_->set_data_type(NodeOutput::kTexture); AddParameter(texture_output_); + + // FIXME: Test code only + decoder_ = new FFmpegDecoder(); // FIXME: Doesn't ever get free'd + // End test code } QString MediaInput::Name() @@ -81,13 +90,43 @@ void MediaInput::Process(const rational &time) // Grab frame from decoder - // FIXME: Test code - if (texture_ == nullptr) { - QImage img("/home/matt/Desktop/oof.png"); - - texture_ = new QOpenGLTexture(img); + if (decoder_->stream() == nullptr) { + decoder_->set_stream(footage->stream(0)); } - texture_output_->set_value(texture_->textureId()); + FramePtr frame = decoder_->Retrieve(time); + + if (frame == nullptr) { + texture_output_->set_value(0); + + return; + } + + // FIXME: Test code + if (texture_ == 0) { + glGenTextures(1, &texture_); + + glBindTexture(GL_TEXTURE_2D, texture_); + + // Set texture filtering to bilinear + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + // Set texture wrapping to clamp + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, + 0, + GL_RGBA8, + frame->width(), + frame->height(), + 0, + GL_RGBA, + GL_UNSIGNED_BYTE, + frame->data()[0]); + } + + texture_output_->set_value(texture_); // End test code } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index 6851c8ee8..f312bfce4 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -23,6 +23,7 @@ #include +#include "decoder/decoder.h" #include "node/node.h" /** @@ -52,7 +53,9 @@ private: NodeOutput* texture_output_; - QOpenGLTexture* texture_; + GLuint texture_; + + Decoder* decoder_; }; #endif // IMAGE_H diff --git a/app/project/item/footage/stream.cpp b/app/project/item/footage/stream.cpp index c7d445b72..866a6ea4c 100644 --- a/app/project/item/footage/stream.cpp +++ b/app/project/item/footage/stream.cpp @@ -41,7 +41,7 @@ void Stream::set_type(const Stream::Type &type) type_ = type; } -Footage *Stream::footage() +Footage *Stream::footage() const { return footage_; } @@ -51,7 +51,7 @@ void Stream::set_footage(Footage *f) footage_ = f; } -const rational &Stream::timebase() +const rational &Stream::timebase() const { return timebase_; } @@ -61,7 +61,7 @@ void Stream::set_timebase(const rational &timebase) timebase_ = timebase; } -const int &Stream::index() +const int &Stream::index() const { return index_; } @@ -71,7 +71,7 @@ void Stream::set_index(const int &index) index_ = index; } -const int64_t &Stream::duration() +const int64_t &Stream::duration() const { return duration_; } diff --git a/app/project/item/footage/stream.h b/app/project/item/footage/stream.h index 099703a45..919139317 100644 --- a/app/project/item/footage/stream.h +++ b/app/project/item/footage/stream.h @@ -59,16 +59,16 @@ public: const Type& type(); void set_type(const Type& type); - Footage* footage(); + Footage* footage() const; void set_footage(Footage* f); - const rational& timebase(); + const rational& timebase() const; void set_timebase(const rational& timebase); - const int& index(); + const int& index() const; void set_index(const int& index); - const int64_t& duration(); + const int64_t& duration() const; void set_duration(const int64_t& duration); private: diff --git a/app/widget/footagecombobox/footagecombobox.cpp b/app/widget/footagecombobox/footagecombobox.cpp index 6fd97331c..6dbd58d35 100644 --- a/app/widget/footagecombobox/footagecombobox.cpp +++ b/app/widget/footagecombobox/footagecombobox.cpp @@ -1,11 +1,13 @@ #include "footagecombobox.h" #include +#include #include FootageComboBox::FootageComboBox(QWidget *parent) : QComboBox(parent), - root_(nullptr) + root_(nullptr), + footage_(nullptr) { } @@ -25,11 +27,14 @@ void FootageComboBox::showPopup() QAction* selected = menu.exec(parentWidget()->mapToGlobal(pos())); if (selected != nullptr) { + // Use combobox functions to show the footage name clear(); addItem(selected->text()); - emit FootageChanged(reinterpret_cast(selected->data().value())); + footage_ = reinterpret_cast(selected->data().value()); + + emit FootageChanged(footage_); } } @@ -40,11 +45,22 @@ void FootageComboBox::SetRoot(const Folder *p) clear(); } +Footage *FootageComboBox::SelectedFootage() +{ + return footage_; +} + void FootageComboBox::SetFootage(Footage *f) { + // Remove existing single item used to show the footage name clear(); - addItem(f->name()); + footage_ = f; + + if (footage_ != nullptr) { + // Use combobox functions to show the footage name + addItem(footage_->name()); + } } void FootageComboBox::TraverseFolder(const Folder *f, QMenu *m) diff --git a/app/widget/footagecombobox/footagecombobox.h b/app/widget/footagecombobox/footagecombobox.h index 5f2271620..55617c4bd 100644 --- a/app/widget/footagecombobox/footagecombobox.h +++ b/app/widget/footagecombobox/footagecombobox.h @@ -17,6 +17,8 @@ public: void SetRoot(const Folder *p); + Footage* SelectedFootage(); + public slots: void SetFootage(Footage* f); @@ -27,6 +29,8 @@ private: void TraverseFolder(const Folder *f, QMenu* m); const Folder* root_; + + Footage* footage_; }; #endif // FOOTAGECOMBOBOX_H diff --git a/app/widget/nodeparamview/nodeparamviewitem.cpp b/app/widget/nodeparamview/nodeparamviewitem.cpp index 1f8ccee25..c0c619c34 100644 --- a/app/widget/nodeparamview/nodeparamviewitem.cpp +++ b/app/widget/nodeparamview/nodeparamviewitem.cpp @@ -156,6 +156,7 @@ void NodeParamViewItemTitleBar::paintEvent(QPaintEvent *event) QPainter p(this); // Draw bottom border using text color + int bottom = height() - 1; p.setPen(palette().text().color()); - p.drawLine(0, height() - 1, width(), height() - 1); + p.drawLine(0, bottom, width(), bottom); } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp index 7e710ca5f..d5bb822fa 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.cpp @@ -4,6 +4,7 @@ #include #include +#include "node/node.h" #include "widget/footagecombobox/footagecombobox.h" // FIXME: Test code only @@ -79,7 +80,8 @@ void NodeParamViewWidgetBridge::CreateWidgets() // Pretty hacky way of getting the root folder for this node's sequence ProjectPanel* pp = olive::panel_focus_manager->MostRecentlyFocused(); footage_combobox->SetRoot(pp->project()->root()); - + footage_combobox->SetFootage(Node::ValueToPtr(input_->get_value(Now()))); + connect(footage_combobox, SIGNAL(FootageChanged(Footage*)), this, SLOT(WidgetCallback())); // End test code widgets_.append(footage_combobox); @@ -89,6 +91,12 @@ void NodeParamViewWidgetBridge::CreateWidgets() } } +rational NodeParamViewWidgetBridge::Now() +{ + // FIXME: Actually implement this + return 0; +} + void NodeParamViewWidgetBridge::WidgetCallback() { switch (input_->inputs().first()) { @@ -132,8 +140,8 @@ void NodeParamViewWidgetBridge::WidgetCallback() case NodeParam::kFootage: { // Widget is a FootageComboBox - //FootageComboBox* footage_combobox = static_cast(sender()); - + FootageComboBox* footage_combobox = static_cast(sender()); + input_->set_value(Now(), Node::PtrToValue(footage_combobox->SelectedFootage())); break; } } diff --git a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h index 3d6365c41..f71b2568f 100644 --- a/app/widget/nodeparamview/nodeparamviewwidgetbridge.h +++ b/app/widget/nodeparamview/nodeparamviewwidgetbridge.h @@ -20,6 +20,8 @@ private: void CreateWidgets(); + rational Now(); + private slots: void WidgetCallback(); };