From 87a51afd994de0300330d463167e59f4340a80cb Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Thu, 10 Aug 2023 18:47:44 +0200 Subject: [PATCH] New Generalize option to restart on deactivation to any StreamSource StreamSource now have the option 'restart on deactivation' like MediaPlayer. This option is saved in XML (added Visitors for Stream and StreamSource). The GUI is added as sub-menu in play bar (like for MediaPlayer). Some StreamSource subclasses needed to be fixed to allow this feature (e.g. MultiFileSource). --- src/DeviceSource.cpp | 2 +- src/MultiFileSource.cpp | 18 +++++++--- src/MultiFileSource.h | 1 + src/NetworkSource.cpp | 2 +- src/PatternSource.cpp | 2 +- src/SessionCreator.cpp | 19 +++++++++- src/SessionCreator.h | 2 ++ src/SessionVisitor.cpp | 18 ++++++++++ src/SessionVisitor.h | 2 ++ src/SourceControlWindow.cpp | 70 ++++++++++++++++++++++++++++--------- src/SrtReceiverSource.cpp | 2 +- src/Stream.cpp | 14 ++++++++ src/Stream.h | 8 +++++ src/StreamSource.cpp | 8 ++++- src/StreamSource.h | 3 ++ src/Visitor.h | 2 ++ 16 files changed, 146 insertions(+), 27 deletions(-) diff --git a/src/DeviceSource.cpp b/src/DeviceSource.cpp index f2a1715..2c3a033 100644 --- a/src/DeviceSource.cpp +++ b/src/DeviceSource.cpp @@ -526,7 +526,7 @@ void DeviceSource::setActive (bool on) void DeviceSource::accept(Visitor& v) { - Source::accept(v); + StreamSource::accept(v); v.visit(*this); } diff --git a/src/MultiFileSource.cpp b/src/MultiFileSource.cpp index 1f2b8f3..eb5e96a 100644 --- a/src/MultiFileSource.cpp +++ b/src/MultiFileSource.cpp @@ -129,6 +129,16 @@ void MultiFile::close () Stream::close(); } +void MultiFile::rewind () +{ + if (src_) { + int begin = 0; + g_object_get (src_, "start-index", &begin, NULL); + setIndex (begin); + } + Stream::rewind(); +} + void MultiFile::setIndex(int val) { if (src_) { @@ -219,10 +229,8 @@ void MultiFileSource::setRange (int begin, int end) void MultiFileSource::replay () { - if (multifile()) { - multifile()->setIndex (begin_); - stream_->rewind(); - } + if (multifile()) + multifile()->rewind(); } guint64 MultiFileSource::playtime () const @@ -240,7 +248,7 @@ guint64 MultiFileSource::playtime () const void MultiFileSource::accept (Visitor& v) { - Source::accept(v); + StreamSource::accept(v); v.visit(*this); } diff --git a/src/MultiFileSource.h b/src/MultiFileSource.h index 777d735..ad72366 100644 --- a/src/MultiFileSource.h +++ b/src/MultiFileSource.h @@ -26,6 +26,7 @@ public: MultiFile (); void open (const MultiFileSequence &sequence, uint framerate = 30); void close () override; + void rewind () override; // dynamic change of gstreamer multifile source properties void setProperties(int begin, int end, int loop); diff --git a/src/NetworkSource.cpp b/src/NetworkSource.cpp index da3de08..5a09bda 100644 --- a/src/NetworkSource.cpp +++ b/src/NetworkSource.cpp @@ -341,7 +341,7 @@ std::string NetworkSource::connection() const void NetworkSource::accept(Visitor& v) { - Source::accept(v); + StreamSource::accept(v); v.visit(*this); } diff --git a/src/PatternSource.cpp b/src/PatternSource.cpp index b31bd14..dc8a996 100644 --- a/src/PatternSource.cpp +++ b/src/PatternSource.cpp @@ -163,7 +163,7 @@ void PatternSource::setPattern(uint type, glm::ivec2 resolution) void PatternSource::accept(Visitor& v) { - Source::accept(v); + StreamSource::accept(v); v.visit(*this); } diff --git a/src/SessionCreator.cpp b/src/SessionCreator.cpp index 81b74f0..95a0f90 100644 --- a/src/SessionCreator.cpp +++ b/src/SessionCreator.cpp @@ -36,7 +36,6 @@ #include "NetworkSource.h" #include "SrtReceiverSource.h" #include "MultiFileSource.h" -#include "StreamSource.h" #include "RenderSource.h" #include "Session.h" #include "ImageShader.h" @@ -1169,6 +1168,24 @@ void SessionLoader::visit (RenderSource& s) s.setSession( session_ ); } +void SessionLoader::visit(Stream &n) +{ + XMLElement* streamNode = xmlCurrent_->FirstChildElement("Stream"); + + if (streamNode) { + bool rewind_on_disabled = false; + streamNode->QueryBoolAttribute("rewind_on_disabled", &rewind_on_disabled); + n.setRewindOnDisabled(rewind_on_disabled); + } +} + +void SessionLoader::visit (StreamSource& s) +{ + // set config stream + if (s.stream() != nullptr) + s.stream()->accept(*this); +} + void SessionLoader::visit (PatternSource& s) { uint t = xmlCurrent_->UnsignedAttribute("pattern"); diff --git a/src/SessionCreator.h b/src/SessionCreator.h index f126fcb..942d780 100644 --- a/src/SessionCreator.h +++ b/src/SessionCreator.h @@ -45,6 +45,7 @@ public: // Elements with attributes void visit (MediaPlayer& n) override; + void visit (Stream& n) override; void visit (Shader& n) override; void visit (ImageShader& n) override; void visit (MaskShader& n) override; @@ -53,6 +54,7 @@ public: // Sources void visit (Source& s) override; void visit (MediaSource& s) override; + void visit (StreamSource& s) override; void visit (SessionFileSource& s) override; void visit (SessionGroupSource& s) override; void visit (RenderSource& s) override; diff --git a/src/SessionVisitor.cpp b/src/SessionVisitor.cpp index 4766b0b..01cfbfd 100644 --- a/src/SessionVisitor.cpp +++ b/src/SessionVisitor.cpp @@ -405,6 +405,18 @@ void SessionVisitor::visit(FrameBufferSurface &) xmlCurrent_->SetAttribute("type", "FrameBufferSurface"); } +void SessionVisitor::visit(Stream &n) +{ + XMLElement *newelement = xmlDoc_->NewElement("Stream"); + newelement->SetAttribute("id", n.id()); + + if (!n.singleFrame()) { + newelement->SetAttribute("rewind_on_disabled", n.rewindOnDisabled()); + } + + xmlCurrent_->InsertEndChild(newelement); +} + void SessionVisitor::visit(MediaPlayer &n) { XMLElement *newelement = xmlDoc_->NewElement("MediaPlayer"); @@ -808,6 +820,12 @@ void SessionVisitor::visit (CloneSource& s) xmlCurrent_ = cloneNode; // parent for next visits (other subtypes of Source) } +void SessionVisitor::visit (StreamSource& s) +{ + if (s.stream() != nullptr) + s.stream()->accept(*this); +} + void SessionVisitor::visit (PatternSource& s) { xmlCurrent_->SetAttribute("type", "PatternSource"); diff --git a/src/SessionVisitor.h b/src/SessionVisitor.h index 910cbff..a23e7e3 100644 --- a/src/SessionVisitor.h +++ b/src/SessionVisitor.h @@ -50,6 +50,7 @@ public: // Elements with attributes void visit (MediaPlayer& n) override; + void visit (Stream& n) override; void visit (Shader& n) override; void visit (ImageShader& n) override; void visit (MaskShader& n) override; @@ -58,6 +59,7 @@ public: // Sources void visit (Source& s) override; void visit (MediaSource& s) override; + void visit (StreamSource& s) override; void visit (SessionFileSource& s) override; void visit (SessionGroupSource& s) override; void visit (RenderSource&) override; diff --git a/src/SourceControlWindow.cpp b/src/SourceControlWindow.cpp index 481bc09..da19d99 100644 --- a/src/SourceControlWindow.cpp +++ b/src/SourceControlWindow.cpp @@ -375,8 +375,8 @@ void SourceControlWindow::Render() if (ImGui::BeginMenu(ICON_FA_PHOTO_VIDEO " Media", mediaplayer_active_) ) { if ( !mediaplayer_active_->singleFrame() ) { - if (ImGui::MenuItem( ICON_FA_REDO_ALT " Reload" )) - mediaplayer_active_->reopen(); +// if (ImGui::MenuItem( ICON_FA_REDO_ALT " Reload" )) +// mediaplayer_active_->reopen(); if (ImGuiToolkit::MenuItemIcon(16, 16, "Gstreamer effect", nullptr, false, mediaplayer_active_->videoEffectAvailable()) ) mediaplayer_edit_pipeline_ = true; @@ -390,16 +390,6 @@ void SourceControlWindow::Render() mediaplayer_active_->setSoftwareDecodingForced(true); ImGui::EndMenu(); } - if (ImGui::BeginMenu(ICON_FA_SNOWFLAKE " On deactivation")) - { - bool option = !mediaplayer_active_->rewindOnDisabled(); - if (ImGui::MenuItem(ICON_FA_STOP " Stop", NULL, &option )) - mediaplayer_active_->setRewindOnDisabled(false); - option = mediaplayer_active_->rewindOnDisabled(); - if (ImGui::MenuItem(ICON_FA_FAST_BACKWARD " Rewind & Stop", NULL, &option )) - mediaplayer_active_->setRewindOnDisabled(true); - ImGui::EndMenu(); - } ImGui::Separator(); ImGui::TextDisabled("Timeline"); @@ -1410,8 +1400,10 @@ void SourceControlWindow::RenderSingleSource(Source *s) /// DrawButtonBar(bottom, rendersize.x); - // If possibly a media source, but is not playable - // then offer to make it playable by adding a timeline + /// + /// Special possibly : selected a media source that is not playable + /// then offer to make it playable by adding a timeline + /// if ( ms != nullptr ) { if (ms->mediaplayer()->isImage()) { @@ -1450,6 +1442,43 @@ void SourceControlWindow::RenderSingleSource(Source *s) ImGui::PopStyleColor(2); } } + /// + /// Not a media source, but playable source + /// Offer context menu if it is a Stream source + /// + else if ( s->active() && s->playable() ) { + StreamSource *ss = dynamic_cast(s); + if ( ss != nullptr ) { + + static uint counter_menu_timeout = 0; + + ImGui::SameLine(); + ImGui::SetCursorPosX(rendersize.x - buttons_height_ / 1.4f); + if (ImGuiToolkit::IconButton(5, 8) || ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { + counter_menu_timeout=0; + ImGui::OpenPopup( "MenuStreamOptions" ); + } + + if (ImGui::BeginPopup( "MenuStreamOptions" )) + { + // NB: ss is playable (tested above), and thus ss->stream() is not null + if (ImGui::MenuItem( ICON_FA_REDO_ALT " Reload" )) { + ss->stream()->reopen(); + } + bool option = ss->stream()->rewindOnDisabled(); + if (ImGui::MenuItem(ICON_FA_SNOWFLAKE " Restart on deactivation", NULL, &option )) { + ss->stream()->setRewindOnDisabled(option); + } + + if (ImGui::IsWindowHovered()) + counter_menu_timeout=0; + else if (++counter_menu_timeout > 10) + ImGui::CloseCurrentPopup(); + + ImGui::EndPopup(); + } + } + } } } @@ -1740,7 +1769,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) ImGui::SetCursorPosX(rendersize.x - buttons_height_ / 1.4f); if (ImGuiToolkit::IconButton(5, 8) || ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) { counter_menu_timeout=0; - ImGui::OpenPopup( "MenuPlaySpeed" ); + ImGui::OpenPopup( "MenuMediaPlayerOptions" ); } // restore buttons style @@ -1784,7 +1813,7 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) DrawButtonBar(bottom, rendersize.x); } - if (ImGui::BeginPopup( "MenuPlaySpeed" )) + if (ImGui::BeginPopup( "MenuMediaPlayerOptions" )) { if (ImGuiToolkit::MenuItemIcon(8,0, "Play forward", nullptr, current_play_speed>0)) { mediaplayer_active_->setPlaySpeed( ABS(mediaplayer_active_->playSpeed()) ); @@ -1801,6 +1830,15 @@ void SourceControlWindow::RenderMediaPlayer(MediaSource *ms) oss << ": Speed x 1.0"; Action::manager().store(oss.str()); } + ImGui::Separator(); + + if (ImGui::MenuItem( ICON_FA_REDO_ALT " Reload" )) + mediaplayer_active_->reopen(); + + bool option = mediaplayer_active_->rewindOnDisabled(); + if (ImGui::MenuItem(ICON_FA_SNOWFLAKE " Restart on deactivation", NULL, &option )) { + mediaplayer_active_->setRewindOnDisabled(option); + } if (ImGui::IsWindowHovered()) counter_menu_timeout=0; diff --git a/src/SrtReceiverSource.cpp b/src/SrtReceiverSource.cpp index f256a4a..6f64080 100644 --- a/src/SrtReceiverSource.cpp +++ b/src/SrtReceiverSource.cpp @@ -54,7 +54,7 @@ std::string SrtReceiverSource::uri() const void SrtReceiverSource::accept(Visitor& v) { - Source::accept(v); + StreamSource::accept(v); v.visit(*this); } diff --git a/src/Stream.cpp b/src/Stream.cpp index d122e08..53eb829 100644 --- a/src/Stream.cpp +++ b/src/Stream.cpp @@ -53,6 +53,7 @@ Stream::Stream() single_frame_ = false; live_ = false; failed_ = false; + rewind_on_disable_ = false; decoder_name_ = ""; // start index in frame_ stack @@ -216,6 +217,15 @@ void Stream::open(const std::string &gstreamer_description, guint w, guint h) } +void Stream::reopen() +{ + // re-openning is meaningfull only if it was already open + if (pipeline_ != nullptr) { + // reload : terminate pipeline and re-create it + close(); + execute_open(); + } +} std::string Stream::description() const { @@ -407,6 +417,10 @@ void Stream::enable(bool on) if ( enabled_ != on ) { + // option to automatically rewind each time the player is disabled + if (!on && rewind_on_disable_ && desired_state_ == GST_STATE_PLAYING) + rewind(); + enabled_ = on; // default to pause diff --git a/src/Stream.h b/src/Stream.h index 4035586..b0b95cc 100644 --- a/src/Stream.h +++ b/src/Stream.h @@ -57,6 +57,7 @@ public: * Open a media using gstreamer pipeline keyword * */ void open(const std::string &gstreamer_description, guint w = 0, guint h = 0); + void reopen (); /** * Get description string * */ @@ -143,6 +144,12 @@ public: * NB: perform request on pipeline on first call * */ std::string decoderName(); + /** + * Option to automatically rewind each time the player is disabled + * (i.e. when enable(false) is called ) + * */ + inline void setRewindOnDisabled(bool on) { rewind_on_disable_ = on; } + inline bool rewindOnDisabled() const { return rewind_on_disable_; } /** * Get logs * */ @@ -176,6 +183,7 @@ protected: std::atomic opened_; std::atomic failed_; bool enabled_; + bool rewind_on_disable_; std::string decoder_name_; // fps counter diff --git a/src/StreamSource.cpp b/src/StreamSource.cpp index b559d7f..d16f707 100644 --- a/src/StreamSource.cpp +++ b/src/StreamSource.cpp @@ -68,7 +68,7 @@ std::list GenericStreamSource::gstElements() const void GenericStreamSource::accept(Visitor& v) { - Source::accept(v); + StreamSource::accept(v); v.visit(*this); } @@ -197,3 +197,9 @@ void StreamSource::update(float dt) if ( stream_ ) stream_->update(); } + +void StreamSource::accept(Visitor& v) +{ + Source::accept(v); + v.visit(*this); +} diff --git a/src/StreamSource.h b/src/StreamSource.h index 18ed873..1601888 100644 --- a/src/StreamSource.h +++ b/src/StreamSource.h @@ -41,6 +41,9 @@ public: // pure virtual interface virtual Stream *stream() const = 0; + // Source interface + virtual void accept (Visitor& v) override; + protected: void init() override; diff --git a/src/Visitor.h b/src/Visitor.h index 33ff8d3..853cb30 100644 --- a/src/Visitor.h +++ b/src/Visitor.h @@ -30,6 +30,7 @@ class ImageProcessingShader; class Source; class MediaSource; +class StreamSource; class PatternSource; class DeviceSource; class GenericStreamSource; @@ -101,6 +102,7 @@ public: virtual void visit (MixingGroup&) {} virtual void visit (Source&) {} virtual void visit (MediaSource&) {} + virtual void visit (StreamSource&) {} virtual void visit (NetworkSource&) {} virtual void visit (SrtReceiverSource&) {} virtual void visit (GenericStreamSource&) {}