Introducing multiple levels of Source Failure

This allows Mixer manager to deal with failed sources with the appropriate action: try to repair, leave for user to recreate, or delete.
This commit is contained in:
Bruno Herbelin
2023-03-22 22:50:08 +01:00
parent 9b4ef00278
commit f91522fc14
18 changed files with 159 additions and 127 deletions

View File

@@ -61,6 +61,12 @@ CloneSource::~CloneSource()
delete filter_; delete filter_;
} }
void CloneSource::detach()
{
Log::Info("Source '%s' detached from '%s'.", name().c_str(), origin_->name().c_str() );
origin_ = nullptr;
}
void CloneSource::init() void CloneSource::init()
{ {
if (origin_ && origin_->ready_ && origin_->mode_ > Source::UNINITIALIZED && origin_->renderbuffer_) { if (origin_ && origin_->ready_ && origin_->mode_ > Source::UNINITIALIZED && origin_->renderbuffer_) {
@@ -225,6 +231,11 @@ uint CloneSource::texture() const
return Resource::getTextureBlack(); return Resource::getTextureBlack();
} }
Source::Failure CloneSource::failed() const
{
return (origin_ == nullptr || origin_->failed()) ? FAIL_FATAL : FAIL_NONE;
}
void CloneSource::accept(Visitor& v) void CloneSource::accept(Visitor& v)
{ {
Source::accept(v); Source::accept(v);

View File

@@ -22,14 +22,14 @@ public:
void replay () override; void replay () override;
guint64 playtime () const override; guint64 playtime () const override;
uint texture() const override; uint texture() const override;
bool failed() const override { return origin_ == nullptr || origin_->failed(); } Failure failed() const override;
void accept (Visitor& v) override; void accept (Visitor& v) override;
void render() override; void render() override;
glm::ivec2 icon() const override; glm::ivec2 icon() const override;
std::string info() const override; std::string info() const override;
// implementation of cloning mechanism // implementation of cloning mechanism
inline void detach() { origin_ = nullptr; } void detach();
inline Source *origin() const { return origin_; } inline Source *origin() const { return origin_; }
// Filtering // Filtering

View File

@@ -26,15 +26,10 @@
#include <gst/gst.h> #include <gst/gst.h>
#include "defines.h"
#include "Log.h" #include "Log.h"
#include "ImageShader.h"
#include "ImageProcessingShader.h"
#include "Resource.h"
#include "Decorations.h" #include "Decorations.h"
#include "Stream.h" #include "Stream.h"
#include "Visitor.h" #include "Visitor.h"
#include "CloneSource.h"
#include "DeviceSource.h" #include "DeviceSource.h"
@@ -525,9 +520,9 @@ void DeviceSource::accept(Visitor& v)
v.visit(*this); v.visit(*this);
} }
bool DeviceSource::failed() const Source::Failure DeviceSource::failed() const
{ {
return unplugged_ || StreamSource::failed(); return (unplugged_ || StreamSource::failed()) ? FAIL_CRITICAL : FAIL_NONE;
} }
DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description) DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description)

View File

@@ -15,7 +15,7 @@ public:
~DeviceSource(); ~DeviceSource();
// Source interface // Source interface
bool failed() const override; Failure failed() const override;
void accept (Visitor& v) override; void accept (Visitor& v) override;
void setActive (bool on) override; void setActive (bool on) override;

View File

@@ -443,9 +443,9 @@ void ImGuiVisitor::visit (Source& s)
ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y ) ); ImGui::SetCursorPos( ImVec2(preview_width + 20, pos.y ) );
if (s.active()) { if (s.active()) {
if (s.blendingShader()->color.a > 0.f) if (s.blendingShader()->color.a > 0.f)
ImGuiToolkit::Indication("Visible", ICON_FA_EYE); ImGuiToolkit::Indication("Visible", ICON_FA_SUN);
else else
ImGuiToolkit::Indication("Not visible", ICON_FA_EYE_SLASH); ImGuiToolkit::Indication("Not visible", ICON_FA_CLOUD_SUN);
} }
else else
ImGuiToolkit::Indication("Inactive", ICON_FA_SNOWFLAKE); ImGuiToolkit::Indication("Inactive", ICON_FA_SNOWFLAKE);
@@ -1142,8 +1142,9 @@ void ImGuiVisitor::visit (CloneSource& s)
ImGui::Text("%s", info.str().c_str()); ImGui::Text("%s", info.str().c_str());
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
// link to origin source
if ( !s.failed() ) { if ( !s.failed() ) {
// link to origin source
std::string label = std::string(s.origin()->initials()) + " - " + s.origin()->name(); std::string label = std::string(s.origin()->initials()) + " - " + s.origin()->name();
if (ImGui::Button(label.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) )) { if (ImGui::Button(label.c_str(), ImVec2(IMGUI_RIGHT_ALIGN, 0) )) {
Mixer::manager().setCurrentSource(s.origin()); Mixer::manager().setCurrentSource(s.origin());
@@ -1153,49 +1154,46 @@ void ImGuiVisitor::visit (CloneSource& s)
} }
ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Origin"); ImGui::Text("Origin");
}
else { // filter selection
ImGuiToolkit::ButtonDisabled("No source", ImVec2(IMGUI_RIGHT_ALIGN, 0) ); std::ostringstream oss;
oss << s.name();
int type = (int) s.filter()->type();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGuiToolkit::ComboIcon("##SelectFilter", &type, FrameBufferFilter::Types)) {
s.setFilter( FrameBufferFilter::Type(type) );
oss << ": Filter " << std::get<2>(FrameBufferFilter::Types[type]);
Action::manager().store(oss.str());
info.reset();
}
ImGui::SameLine(0, IMGUI_SAME_LINE); ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Origin"); if (ImGuiToolkit::TextButton("Filter")) {
} s.setFilter( FrameBufferFilter::FILTER_PASSTHROUGH );
oss << ": Filter None";
Action::manager().store(oss.str());
info.reset();
}
// filter selection // filter options
std::ostringstream oss; s.filter()->accept(*this);
oss << s.name();
int type = (int) s.filter()->type();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGuiToolkit::ComboIcon("##SelectFilter", &type, FrameBufferFilter::Types)) {
s.setFilter( FrameBufferFilter::Type(type) );
oss << ": Filter " << std::get<2>(FrameBufferFilter::Types[type]);
Action::manager().store(oss.str());
info.reset();
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
if (ImGuiToolkit::TextButton("Filter")) {
s.setFilter( FrameBufferFilter::FILTER_PASSTHROUGH );
oss << ": Filter None";
Action::manager().store(oss.str());
info.reset();
}
// filter options ImVec2 botom = ImGui::GetCursorPos();
s.filter()->accept(*this);
ImVec2 botom = ImGui::GetCursorPos();
if ( !s.failed() ) {
// icon (>) to open player // icon (>) to open player
if ( s.playable() ) { if ( s.playable() ) {
ImGui::SetCursorPos(top); ImGui::SetCursorPos(top);
if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player")) if (ImGuiToolkit::IconButton(ICON_FA_PLAY_CIRCLE, "Open in Player"))
UserInterface::manager().showSourceEditor(&s); UserInterface::manager().showSourceEditor(&s);
} }
}
else
info.reset();
ImGui::SetCursorPos(botom); ImGui::SetCursorPos(botom);
}
else {
ImGuiToolkit::ButtonDisabled("No source", ImVec2(IMGUI_RIGHT_ALIGN, 0) );
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::Text("Origin");
info.reset();
}
} }
void ImGuiVisitor::visit (PatternSource& s) void ImGuiVisitor::visit (PatternSource& s)
@@ -1254,25 +1252,26 @@ void ImGuiVisitor::visit (DeviceSource& s)
ImGui::Text("%s", info.str().c_str()); ImGui::Text("%s", info.str().c_str());
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("Device", s.device().c_str()))
{
for (int d = 0; d < Device::manager().numDevices(); ++d){
std::string namedev = Device::manager().name(d);
if (ImGui::Selectable( namedev.c_str() )) {
s.setDevice(namedev);
info.reset();
std::ostringstream oss;
oss << s.name() << " Device " << namedev;
Action::manager().store(oss.str());
}
}
ImGui::EndCombo();
}
ImVec2 botom = ImGui::GetCursorPos(); ImVec2 botom = ImGui::GetCursorPos();
if ( !s.failed() ) { if ( !s.failed() ) {
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::BeginCombo("Device", s.device().c_str()))
{
for (int d = 0; d < Device::manager().numDevices(); ++d){
std::string namedev = Device::manager().name(d);
if (ImGui::Selectable( namedev.c_str() )) {
s.setDevice(namedev);
info.reset();
std::ostringstream oss;
oss << s.name() << " Device " << namedev;
Action::manager().store(oss.str());
}
}
ImGui::EndCombo();
}
// icon (>) to open player // icon (>) to open player
if ( s.playable() ) { if ( s.playable() ) {
ImGui::SetCursorPos(top); ImGui::SetCursorPos(top);
@@ -1427,21 +1426,22 @@ void ImGuiVisitor::visit (GenericStreamSource& s)
ImGui::Text("%s", info.str().c_str()); ImGui::Text("%s", info.str().c_str());
ImGui::PopTextWrapPos(); ImGui::PopTextWrapPos();
// Prepare display pipeline text
static int numlines = 0;
const ImGuiContext& g = *GImGui;
ImVec2 fieldsize(w, MAX(3, numlines) * g.FontSize + g.Style.ItemSpacing.y + g.Style.FramePadding.y);
// Editor
std::string _description = s.description();
if ( ImGuiToolkit::InputCodeMultiline("Pipeline", &_description, fieldsize, &numlines) ) {
s.setDescription(_description);
Action::manager().store( s.name() + ": Change pipeline");
}
ImVec2 botom = ImGui::GetCursorPos(); ImVec2 botom = ImGui::GetCursorPos();
if ( !s.failed() ) { if ( !s.failed() ) {
// Prepare display pipeline text
static int numlines = 0;
const ImGuiContext& g = *GImGui;
ImVec2 fieldsize(w, MAX(3, numlines) * g.FontSize + g.Style.ItemSpacing.y + g.Style.FramePadding.y);
// Editor
std::string _description = s.description();
if ( ImGuiToolkit::InputCodeMultiline("Pipeline", &_description, fieldsize, &numlines) ) {
s.setDescription(_description);
Action::manager().store( s.name() + ": Change pipeline");
}
// icon (>) to open player // icon (>) to open player
if ( s.playable() ) { if ( s.playable() ) {
ImGui::SetCursorPos(top); ImGui::SetCursorPos(top);

View File

@@ -78,9 +78,9 @@ std::string MediaSource::info() const
return "Video File"; return "Video File";
} }
bool MediaSource::failed() const Source::Failure MediaSource::failed() const
{ {
return mediaplayer_->failed(); return mediaplayer_->failed() ? FAIL_CRITICAL : FAIL_NONE;
} }
uint MediaSource::texture() const uint MediaSource::texture() const

View File

@@ -20,7 +20,7 @@ public:
void replay () override; void replay () override;
guint64 playtime () const override; guint64 playtime () const override;
void render() override; void render() override;
bool failed() const override; Failure failed() const override;
uint texture() const override; uint texture() const override;
void accept (Visitor& v) override; void accept (Visitor& v) override;

View File

@@ -205,25 +205,37 @@ void Mixer::update()
FrameGrabbing::manager().grabFrame(session_->frame()); FrameGrabbing::manager().grabFrame(session_->frame());
// manage sources which failed update // manage sources which failed update
SourceListUnique failures = session()->failedSources(); if (session_->ready()) {
for(auto it = failures.begin(); it != failures.end(); ++it) { // go through all failed sources
// if the failed source is still attached to the mixer SourceListUnique _failedsources = session_->failedSources();
if ( attached( *it ) ) { for(auto it = _failedsources.begin(); it != _failedsources.end(); ++it) {
// special case of failed Render loopback : // only deal with sources that are still attached to mixer
// it fails when resolution change, and we can fix it by if ( attached( *it ) ) {
// recreating it in the current session // intervention depends on the severity of the failure
RenderSource *failedRender = dynamic_cast<RenderSource *>(*it); Source::Failure fail = (*it)->failed();
if (failedRender != nullptr) { // Attempt to repair BAD failed sources
// try to recreate the failed render source // (can be automatically repaired without user intervention)
if ( !recreateSource(failedRender) ) if (fail == Source::FAIL_BAD) {
// delete the source if could not if ( !recreateSource( *it ) ) {
deleteSource(failedRender); Log::Warning("Source '%s' failed and was deleted.", (*it)->name().c_str());
// delete failed source if could not recreate it
deleteSource( *it );
}
}
// Detatch CRITICAL failed sources from the mixer
// (not deleted in the session; user can replace it)
else if (fail == Source::FAIL_CRITICAL) {
detach( *it );
}
// Delete FATAL failed sources from the mixer
// (nothing can be done by the user)
else {
Log::Warning("Source '%s' failed and was deleted.", (*it)->name().c_str());
deleteSource( *it );
}
// needs refresh after intervention
++View::need_deep_update_;
} }
// general case:
// detatch failed sources from the mixer
// (not deleted in the session; user can replace it)
else
detach( *it );
} }
} }

View File

@@ -47,12 +47,12 @@ RenderSource::~RenderSource()
delete rendered_output_; delete rendered_output_;
} }
bool RenderSource::failed() const Source::Failure RenderSource::failed() const
{ {
if ( rendered_output_ != nullptr && session_ != nullptr ) if ( rendered_output_ != nullptr && session_ != nullptr )
return rendered_output_->resolution() != session_->frame()->resolution(); return rendered_output_->resolution() != session_->frame()->resolution() ? FAIL_BAD : FAIL_NONE;
return false; return FAIL_NONE;
} }
uint RenderSource::texture() const uint RenderSource::texture() const

View File

@@ -18,7 +18,7 @@ public:
void replay () override {} void replay () override {}
bool playable () const override { return true; } bool playable () const override { return true; }
guint64 playtime () const override { return runtime_; } guint64 playtime () const override { return runtime_; }
bool failed () const override; Failure failed () const override;
uint texture() const override; uint texture() const override;
void accept (Visitor& v) override; void accept (Visitor& v) override;

View File

@@ -44,7 +44,7 @@ SessionNote::SessionNote(const std::string &t, bool l, int s): label(std::to_str
} }
Session::Session(uint64_t id) : id_(id), active_(true), activation_threshold_(MIXING_MIN_THRESHOLD), Session::Session(uint64_t id) : id_(id), active_(true), activation_threshold_(MIXING_MIN_THRESHOLD),
filename_(""), thumbnail_(nullptr) filename_(""), thumbnail_(nullptr), ready_(false)
{ {
// create unique id // create unique id
if (id_ == 0) if (id_ == 0)
@@ -138,6 +138,14 @@ void Session::setActive (bool on)
} }
} }
void Session::deleteFailedSources ()
{
while (!failed_.empty()) {
deleteSource( *(failed_.begin()) );
}
}
// update all sources // update all sources
void Session::update(float dt) void Session::update(float dt)
{ {
@@ -234,7 +242,7 @@ void Session::update(float dt)
} }
// pre-render all sources // pre-render all sources
bool ready = true; ready_ = true;
for( SourceList::iterator it = sources_.begin(); it != sources_.end(); ++it){ for( SourceList::iterator it = sources_.begin(); it != sources_.end(); ++it){
// ensure the RenderSource is rendering *this* session // ensure the RenderSource is rendering *this* session
@@ -244,17 +252,20 @@ void Session::update(float dt)
// discard failed source // discard failed source
if ( (*it)->failed() ) { if ( (*it)->failed() ) {
// insert source in list of failed // if source is already attached
// (NB: insert in set fails if source is already listed) if ((*it)->group(View::RENDERING)->refcount_ > 0) {
failed_.insert( *it ); // insert source in list of failed
// do not render // (NB: insert in set fails if source is already listed)
render_.scene.ws()->detach( (*it)->group(View::RENDERING) ); failed_.insert( *it );
// detatch from rendering (do not render)
render_.scene.ws()->detach( (*it)->group(View::RENDERING) );
}
} }
// render normally // render normally
else { else {
// session is not ready if one source is not ready // session is not ready if one source is not ready
if ( !(*it)->ready() ) if ( !(*it)->ready() )
ready = false; ready_ = false;
// update the source // update the source
(*it)->setActive(activation_threshold_); (*it)->setActive(activation_threshold_);
(*it)->update(dt); (*it)->update(dt);
@@ -307,7 +318,7 @@ void Session::update(float dt)
render_.draw(); render_.draw();
// draw the thumbnail only after all sources are ready // draw the thumbnail only after all sources are ready
if (ready) if (ready_)
render_.drawThumbnail(); render_.drawThumbnail();
} }
@@ -589,9 +600,9 @@ bool Session::canlink (SourceList sources)
validate(sources); validate(sources);
for (auto it = sources.begin(); it != sources.end(); ++it) { for (auto it = sources.begin(); it != sources.end(); ++it) {
// this source is linked // if this source is linked
if ( (*it)->mixingGroup() != nullptr ) { if ( (*it)->mixingGroup() != nullptr ) {
// askt its group to detach it // ask its group to detach it
canlink = false; canlink = false;
} }
} }

View File

@@ -88,6 +88,7 @@ public:
uint numSources() const; uint numSources() const;
// update all sources and mark sources which failed // update all sources and mark sources which failed
inline bool ready () const { return ready_; }
void update (float dt); void update (float dt);
uint64_t runtime() const; uint64_t runtime() const;
@@ -97,6 +98,7 @@ public:
// return the list of sources which failed // return the list of sources which failed
SourceListUnique failedSources () const { return failed_; } SourceListUnique failedSources () const { return failed_; }
void deleteFailedSources ();
// get frame result of render // get frame result of render
inline FrameBuffer *frame () const { return render_.frame(); } inline FrameBuffer *frame () const { return render_.frame(); }
@@ -195,6 +197,7 @@ protected:
std::mutex access_; std::mutex access_;
FrameBufferImage *thumbnail_; FrameBufferImage *thumbnail_;
uint64_t start_time_; uint64_t start_time_;
bool ready_;
struct Fading struct Fading
{ {

View File

@@ -113,9 +113,9 @@ Session *SessionSource::detach()
return giveaway; return giveaway;
} }
bool SessionSource::failed() const Source::Failure SessionSource::failed() const
{ {
return failed_; return failed_ ? FAIL_CRITICAL : FAIL_NONE;
} }
uint SessionSource::texture() const uint SessionSource::texture() const
@@ -149,15 +149,9 @@ void SessionSource::update(float dt)
timer_ += guint64(dt * 1000.f) * GST_USECOND; timer_ += guint64(dt * 1000.f) * GST_USECOND;
} }
// delete source which failed // fail session if all its sources failed
if ( !session_->failedSources().empty() ) { if ( session_->failedSources().size() == session_->size() )
Source *failure = *(session_->failedSources().cbegin()); failed_ = true;
Log::Info("Source '%s' deleted from Child Session %s.", failure->name().c_str(), std::to_string(session_->id()).c_str());
session_->deleteSource( failure );
// fail session if all sources failed
if ( session_->size() < 1)
failed_ = true;
}
} }

View File

@@ -19,7 +19,7 @@ public:
bool playable () const override; bool playable () const override;
guint64 playtime () const override { return timer_; } guint64 playtime () const override { return timer_; }
void replay () override; void replay () override;
bool failed () const override; Failure failed () const override;
uint texture () const override; uint texture () const override;
Session *detach(); Session *detach();

View File

@@ -177,7 +177,13 @@ public:
virtual guint64 playtime () const { return 0; } virtual guint64 playtime () const { return 0; }
// a Source shall informs if the source failed (i.e. shall be deleted) // a Source shall informs if the source failed (i.e. shall be deleted)
virtual bool failed () const = 0; typedef enum {
FAIL_NONE = 0,
FAIL_BAD= 1,
FAIL_CRITICAL = 2,
FAIL_FATAL = 3
} Failure;
virtual Failure failed () const = 0;
// a Source shall define a way to get a texture // a Source shall define a way to get a texture
virtual uint texture () const = 0; virtual uint texture () const = 0;
@@ -251,7 +257,7 @@ public:
} }
static bool isInitialized (const Source* elem) { static bool isInitialized (const Source* elem) {
return (elem && elem->mode_ > Source::UNINITIALIZED); return (elem && ( elem->mode_ > Source::UNINITIALIZED || elem->failed() ) );
} }
// class-dependent icon // class-dependent icon

View File

@@ -46,7 +46,7 @@ SourceList playable_only (const SourceList &list)
} }
bool isfailed (const Source *s) { return s->failed(); } bool isfailed (const Source *s) { return s->failed() != Source::FAIL_NONE; }
SourceList valid_only (const SourceList &list) SourceList valid_only (const SourceList &list)
{ {

View File

@@ -93,9 +93,9 @@ StreamSource::~StreamSource()
delete stream_; delete stream_;
} }
bool StreamSource::failed() const Source::Failure StreamSource::failed() const
{ {
return (stream_ != nullptr && stream_->failed() ); return (stream_ != nullptr && stream_->failed()) ? FAIL_CRITICAL : FAIL_NONE;
} }
uint StreamSource::texture() const uint StreamSource::texture() const

View File

@@ -35,7 +35,7 @@ public:
bool playable () const override; bool playable () const override;
void replay () override; void replay () override;
guint64 playtime () const override; guint64 playtime () const override;
bool failed() const override; Failure failed() const override;
uint texture() const override; uint texture() const override;
// pure virtual interface // pure virtual interface