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).
This commit is contained in:
Bruno Herbelin
2023-08-10 18:47:44 +02:00
parent 4efaa1f350
commit 87a51afd99
16 changed files with 146 additions and 27 deletions

View File

@@ -526,7 +526,7 @@ void DeviceSource::setActive (bool on)
void DeviceSource::accept(Visitor& v)
{
Source::accept(v);
StreamSource::accept(v);
v.visit(*this);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -341,7 +341,7 @@ std::string NetworkSource::connection() const
void NetworkSource::accept(Visitor& v)
{
Source::accept(v);
StreamSource::accept(v);
v.visit(*this);
}

View File

@@ -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);
}

View File

@@ -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");

View File

@@ -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;

View File

@@ -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");

View File

@@ -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;

View File

@@ -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<StreamSource *>(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;

View File

@@ -54,7 +54,7 @@ std::string SrtReceiverSource::uri() const
void SrtReceiverSource::accept(Visitor& v)
{
Source::accept(v);
StreamSource::accept(v);
v.visit(*this);
}

View File

@@ -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

View File

@@ -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<bool> opened_;
std::atomic<bool> failed_;
bool enabled_;
bool rewind_on_disable_;
std::string decoder_name_;
// fps counter

View File

@@ -68,7 +68,7 @@ std::list<std::string> 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);
}

View File

@@ -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;

View File

@@ -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&) {}