diff --git a/CMakeLists.txt b/CMakeLists.txt index bc67a43..7acb465 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,6 +238,7 @@ set(VMIX_SRCS Stream.cpp MediaPlayer.cpp MediaSource.cpp + StreamSource.cpp PatternSource.cpp DeviceSource.cpp FrameBuffer.cpp diff --git a/DeviceSource.cpp b/DeviceSource.cpp index 68c1293..683e16f 100644 --- a/DeviceSource.cpp +++ b/DeviceSource.cpp @@ -27,7 +27,6 @@ void Device::open( uint device ) { device_ = CLAMP(device, 0, 2); - single_frame_ = false; live_ = true; // std::string desc = "v4l2src ! video/x-raw,width=320,height=240,framerate=30/1 ! videoconvert"; @@ -38,133 +37,34 @@ void Device::open( uint device ) std::string desc = "ximagesrc endx=640 endy=480 ! video/x-raw,framerate=5/1 ! videoconvert ! queue"; // (private) open stream - open(desc); + Stream::open(desc); } -void Device::open(std::string gstreamer_description) -{ - // set gstreamer pipeline source - description_ = gstreamer_description; - - // close before re-openning - if (isOpen()) - close(); - - execute_open(); -} - - -DeviceSource::DeviceSource() : Source() +DeviceSource::DeviceSource() : StreamSource() { // create stream - stream_ = new Device(); + stream_ = (Stream *) new Device(); - // create surface - devicesurface_ = new Surface(renderingshader_); -} - -DeviceSource::~DeviceSource() -{ - // delete media surface & stream - delete devicesurface_; - delete stream_; -} - -bool DeviceSource::failed() const -{ - return stream_->failed(); -} - -uint DeviceSource::texture() const -{ - return stream_->texture(); -} - -void DeviceSource::replaceRenderingShader() -{ - devicesurface_->replaceShader(renderingshader_); + // icon in mixing view + overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) ); + overlays_[View::LAYER]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) ); } void DeviceSource::setDevice(int id) { Log::Notify("Openning device %d", id); - stream_->open(id); + device()->open(id); stream_->play(true); } - -void DeviceSource::init() -{ - if ( stream_->isOpen() ) { - - // update video - stream_->update(); - - // once the texture of media player is created - if (stream_->texture() != Resource::getTextureBlack()) { - - // get the texture index from media player, apply it to the media surface - devicesurface_->setTextureIndex( stream_->texture() ); - - // create Frame buffer matching size of media player - float height = float(stream_->width()) / stream_->aspectRatio(); - FrameBuffer *renderbuffer = new FrameBuffer(stream_->width(), (uint)height, true); - - // set the renderbuffer of the source and attach rendering nodes - attach(renderbuffer); - - // icon in mixing view - overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) ); - overlays_[View::LAYER]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) ); - - // done init - initialized_ = true; - Log::Info("Source Device linked to Stream %d.", stream_->description().c_str()); - - // force update of activation mode - active_ = true; - touch(); - } - } - -} - -void DeviceSource::setActive (bool on) -{ - bool was_active = active_; - - Source::setActive(on); - - // change status of media player (only if status changed) - if ( active_ != was_active ) { - stream_->enable(active_); - } -} - -void DeviceSource::update(float dt) -{ - Source::update(dt); - - // update stream - stream_->update(); -} - -void DeviceSource::render() -{ - if (!initialized_) - init(); - else { - // render the media player into frame buffer - static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f); - renderbuffer_->begin(); - devicesurface_->draw(glm::identity(), projection); - renderbuffer_->end(); - } -} - void DeviceSource::accept(Visitor& v) { Source::accept(v); v.visit(*this); } + +Device *DeviceSource::device() const +{ + return dynamic_cast(stream_); +} diff --git a/DeviceSource.h b/DeviceSource.h index 279d247..3ca2d0d 100644 --- a/DeviceSource.h +++ b/DeviceSource.h @@ -1,8 +1,7 @@ #ifndef DEVICESOURCE_H #define DEVICESOURCE_H -#include "Stream.h" -#include "Source.h" +#include "StreamSource.h" class Device : public Stream { @@ -14,35 +13,24 @@ public: glm::ivec2 resolution(); private: - void open( std::string description ) override; uint device_; }; -class DeviceSource : public Source +class DeviceSource : public StreamSource { public: DeviceSource(); - ~DeviceSource(); - // implementation of source API - void update (float dt) override; - void setActive (bool on) override; - void render() override; - bool failed() const override; - uint texture() const override; + // Source interface void accept (Visitor& v) override; - // Pattern specific interface - inline Device *device() const { return stream_; } + // StreamSource interface + Stream *stream() const override { return stream_; } + + // specific interface + Device *device() const; void setDevice(int id); -protected: - - void init() override; - void replaceRenderingShader() override; - - Surface *devicesurface_; - Device *stream_; }; #endif // DEVICESOURCE_H diff --git a/Mixer.cpp b/Mixer.cpp index 9b95176..d288a3b 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -24,6 +24,7 @@ using namespace tinyxml2; #include "MediaSource.h" #include "PatternSource.h" #include "DeviceSource.h" +#include "StreamSource.h" #include "Mixer.h" @@ -266,6 +267,18 @@ Source * Mixer::createSourceRender() return s; } +Source * Mixer::createSourceStream(const std::string &gstreamerpipeline) +{ + // ready to create a source + GenericStreamSource *s = new GenericStreamSource; + s->setDescription(gstreamerpipeline); + + // propose a new name based on pattern name + renameSource(s, gstreamerpipeline.substr(0,10)); + + return s; +} + Source * Mixer::createSourcePattern(int pattern, glm::ivec2 res) { // ready to create a source diff --git a/Mixer.h b/Mixer.h index 437b817..abab4b7 100644 --- a/Mixer.h +++ b/Mixer.h @@ -39,6 +39,7 @@ public: Source * createSourceFile (const std::string &path); Source * createSourceClone (const std::string &namesource = ""); Source * createSourceRender (); + Source * createSourceStream (const std::string &gstreamerpipeline); Source * createSourcePattern(int pattern, glm::ivec2 res); Source * createSourceDevice (int id); diff --git a/PatternSource.cpp b/PatternSource.cpp index 553950a..de255ed 100644 --- a/PatternSource.cpp +++ b/PatternSource.cpp @@ -5,7 +5,6 @@ #include "defines.h" #include "ImageShader.h" -#include "ImageProcessingShader.h" #include "Resource.h" #include "Primitives.h" #include "Stream.h" @@ -118,7 +117,6 @@ void Pattern::open( uint pattern ) std::ostringstream oss; oss << " kx2=" << (int)(aspectRatio() * 10.f) << " ky2=10 kt=4"; gstreamer_pattern += oss.str(); // Zone plate - single_frame_ = false; } break; default: @@ -129,133 +127,129 @@ void Pattern::open( uint pattern ) single_frame_ = type_ < 15; // (private) open stream - open(gstreamer_pattern); + Stream::open(gstreamer_pattern); } -void Pattern::open(std::string gstreamer_pattern) -{ - // set gstreamer pipeline source - description_ = gstreamer_pattern; - - // close before re-openning - if (isOpen()) - close(); - - execute_open(); -} - - -PatternSource::PatternSource(glm::ivec2 resolution) : Source() +PatternSource::PatternSource(glm::ivec2 resolution) : StreamSource() { // create stream - stream_ = new Pattern(resolution); + stream_ = (Stream *) new Pattern(resolution); - // create surface - patternsurface_ = new Surface(renderingshader_); -} +// // create surface +// surface_ = new Surface(renderingshader_); -PatternSource::~PatternSource() -{ - // delete media surface & stream - delete patternsurface_; - delete stream_; -} - -bool PatternSource::failed() const -{ - return stream_->failed(); -} - -uint PatternSource::texture() const -{ - return stream_->texture(); -} - -void PatternSource::replaceRenderingShader() -{ - patternsurface_->replaceShader(renderingshader_); + overlays_[View::MIXING]->attach( new Symbol(Symbol::PATTERN, glm::vec3(0.8f, 0.8f, 0.01f)) ); + overlays_[View::LAYER]->attach( new Symbol(Symbol::PATTERN, glm::vec3(0.8f, 0.8f, 0.01f)) ); } void PatternSource::setPattern(int id) { Log::Notify("Creating pattern %s", Pattern::pattern_types[id].c_str()); - stream_->open(id); + pattern()->open( (uint) id ); stream_->play(true); } - -void PatternSource::init() -{ - if ( stream_->isOpen() ) { - - // update video - stream_->update(); - - // once the texture of media player is created - if (stream_->texture() != Resource::getTextureBlack()) { - - // get the texture index from media player, apply it to the media surface - patternsurface_->setTextureIndex( stream_->texture() ); - - // create Frame buffer matching size of media player - float height = float(stream_->width()) / stream_->aspectRatio(); - FrameBuffer *renderbuffer = new FrameBuffer(stream_->width(), (uint)height, true); - - // set the renderbuffer of the source and attach rendering nodes - attach(renderbuffer); - - // icon in mixing view - overlays_[View::MIXING]->attach( new Symbol(Symbol::PATTERN, glm::vec3(0.8f, 0.8f, 0.01f)) ); - overlays_[View::LAYER]->attach( new Symbol(Symbol::PATTERN, glm::vec3(0.8f, 0.8f, 0.01f)) ); - - // done init - initialized_ = true; - Log::Info("Source Pattern linked to Stream %d.", stream_->description().c_str()); - - // force update of activation mode - active_ = true; - touch(); - } - } - -} - -void PatternSource::setActive (bool on) -{ - bool was_active = active_; - - Source::setActive(on); - - // change status of media player (only if status changed) - if ( active_ != was_active ) { - stream_->enable(active_); - } -} - -void PatternSource::update(float dt) -{ - Source::update(dt); - - // update stream - stream_->update(); -} - -void PatternSource::render() -{ - if (!initialized_) - init(); - else { - // render the media player into frame buffer - static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f); - renderbuffer_->begin(); - patternsurface_->draw(glm::identity(), projection); - renderbuffer_->end(); - } -} - void PatternSource::accept(Visitor& v) { Source::accept(v); v.visit(*this); } + +Pattern *PatternSource::pattern() const +{ + return dynamic_cast(stream_); +} + + +//PatternSource::~PatternSource() +//{ +// // delete media surface & stream +// delete patternsurface_; +// delete stream_; +//} + +//bool PatternSource::failed() const +//{ +// return stream_->failed(); +//} + +//uint PatternSource::texture() const +//{ +// return stream_->texture(); +//} + +//void PatternSource::replaceRenderingShader() +//{ +// patternsurface_->replaceShader(renderingshader_); +//} + + +//void PatternSource::init() +//{ +// if ( stream_->isOpen() ) { + +// // update video +// stream_->update(); + +// // once the texture of media player is created +// if (stream_->texture() != Resource::getTextureBlack()) { + +// // get the texture index from media player, apply it to the media surface +// patternsurface_->setTextureIndex( stream_->texture() ); + +// // create Frame buffer matching size of media player +// float height = float(stream_->width()) / stream_->aspectRatio(); +// FrameBuffer *renderbuffer = new FrameBuffer(stream_->width(), (uint)height, true); + +// // set the renderbuffer of the source and attach rendering nodes +// attach(renderbuffer); + +// // icon in mixing view +// overlays_[View::MIXING]->attach( new Symbol(Symbol::PATTERN, glm::vec3(0.8f, 0.8f, 0.01f)) ); +// overlays_[View::LAYER]->attach( new Symbol(Symbol::PATTERN, glm::vec3(0.8f, 0.8f, 0.01f)) ); + +// // done init +// initialized_ = true; +// Log::Info("Source Pattern linked to Stream %d.", stream_->description().c_str()); + +// // force update of activation mode +// active_ = true; +// touch(); +// } +// } + +//} + +//void PatternSource::setActive (bool on) +//{ +// bool was_active = active_; + +// Source::setActive(on); + +// // change status of media player (only if status changed) +// if ( active_ != was_active ) { +// stream_->enable(active_); +// } +//} + +//void PatternSource::update(float dt) +//{ +// Source::update(dt); + +// // update stream +// stream_->update(); +//} + +//void PatternSource::render() +//{ +// if (!initialized_) +// init(); +// else { +// // render the media player into frame buffer +// static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f); +// renderbuffer_->begin(); +// patternsurface_->draw(glm::identity(), projection); +// renderbuffer_->end(); +// } +//} diff --git a/PatternSource.h b/PatternSource.h index 90a935b..9fde443 100644 --- a/PatternSource.h +++ b/PatternSource.h @@ -3,8 +3,7 @@ #include -#include "Stream.h" -#include "Source.h" +#include "StreamSource.h" class Pattern : public Stream { @@ -18,36 +17,24 @@ public: inline uint type() const { return type_; } private: - void open( std::string description ) override; uint type_; }; -class PatternSource : public Source +class PatternSource : public StreamSource { public: PatternSource(glm::ivec2 resolution); - ~PatternSource(); - // implementation of source API - void update (float dt) override; - void setActive (bool on) override; - void render() override; - bool failed() const override; - uint texture() const override; + // Source interface void accept (Visitor& v) override; - // Pattern specific interface - inline Pattern *pattern() const { return stream_; } + // StreamSource interface + Stream *stream() const override { return stream_; } + + // specific interface + Pattern *pattern() const; void setPattern(int id); -protected: - - void init() override; - void replaceRenderingShader() override; - - Surface *patternsurface_; - Pattern *stream_; - }; #endif // PATTERNSOURCE_H diff --git a/SessionCreator.cpp b/SessionCreator.cpp index 5fd9d3f..0be39e1 100644 --- a/SessionCreator.cpp +++ b/SessionCreator.cpp @@ -8,7 +8,9 @@ #include "Source.h" #include "MediaSource.h" #include "SessionSource.h" +#include "StreamSource.h" #include "PatternSource.h" +#include "DeviceSource.h" #include "Session.h" #include "ImageShader.h" #include "ImageProcessingShader.h" diff --git a/Source.cpp b/Source.cpp index 1759840..e451403 100644 --- a/Source.cpp +++ b/Source.cpp @@ -179,7 +179,6 @@ void Source::accept(Visitor& v) v.visit(*this); } - Source::Mode Source::mode() const { return mode_; diff --git a/Stream.cpp b/Stream.cpp index 3084bff..68436fb 100644 --- a/Stream.cpp +++ b/Stream.cpp @@ -33,7 +33,7 @@ Stream::Stream() width_ = 800; height_ = 600; - single_frame_ = true; + single_frame_ = false; ready_ = false; failed_ = false; enabled_ = true; @@ -71,6 +71,18 @@ guint Stream::texture() const } +void Stream::open(const std::string &gstreamer_description) +{ + // set gstreamer pipeline source + description_ = gstreamer_description; + + // close before re-openning + if (isOpen()) + close(); + + execute_open(); +} + std::string Stream::description() const { @@ -541,7 +553,7 @@ double Stream::updateFrameRate() const bool Stream::fill_frame(GstBuffer *buf, FrameStatus status) { - Log::Info("Stream fill frame"); +// Log::Info("Stream fill frame"); // Do NOT overwrite an unread EOS if ( frame_[write_index_].status == EOS ) @@ -593,7 +605,9 @@ bool Stream::fill_frame(GstBuffer *buf, FrameStatus status) // else; null buffer for EOS: give a position else { frame_[write_index_].status = EOS; +#ifdef STREAM_DEBUG Log::Info("Stream EOS"); +#endif } // unlock access to frame @@ -657,7 +671,7 @@ GstFlowReturn Stream::callback_new_sample (GstAppSink *sink, gpointer p) { GstFlowReturn ret = GST_FLOW_OK; - Log::Info("callback_new_sample"); +// Log::Info("callback_new_sample"); // non-blocking read new sample GstSample *sample = gst_app_sink_pull_sample(sink); diff --git a/Stream.h b/Stream.h index 0a4f355..04dea9e 100644 --- a/Stream.h +++ b/Stream.h @@ -29,11 +29,11 @@ public: /** * Open a media using gstreamer pipeline keyword * */ - virtual void open( std::string description ) = 0; + void open(const std::string &gstreamer_description ); /** * Get description string * */ - virtual std::string description() const; + std::string description() const; /** * True if a media was oppenned * */ diff --git a/StreamSource.cpp b/StreamSource.cpp new file mode 100644 index 0000000..2e8beaa --- /dev/null +++ b/StreamSource.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include "StreamSource.h" + +#include "defines.h" +#include "ImageShader.h" +#include "Resource.h" +#include "Primitives.h" +#include "Stream.h" +#include "Visitor.h" +#include "Log.h" + + +GenericStreamSource::GenericStreamSource() : StreamSource() +{ + // create stream + stream_ = new Stream; + + // icon in mixing view + overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) ); + overlays_[View::LAYER]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) ); +} + +void GenericStreamSource::setDescription(const std::string &desc) +{ + Log::Notify("Creating Stream %s", desc); + + stream_->open(desc); + stream_->play(true); +} + +void GenericStreamSource::accept(Visitor& v) +{ + Source::accept(v); + v.visit(*this); +} + +StreamSource::StreamSource() : Source() +{ + // create surface + surface_ = new Surface(renderingshader_); +} + +StreamSource::~StreamSource() +{ + // delete media surface & stream + delete surface_; + delete stream_; +} + +bool StreamSource::failed() const +{ + return stream_->failed(); +} + +uint StreamSource::texture() const +{ + return stream_->texture(); +} + +void StreamSource::replaceRenderingShader() +{ + surface_->replaceShader(renderingshader_); +} + + +void StreamSource::init() +{ + if ( stream_->isOpen() ) { + + // update video + stream_->update(); + + // once the texture of media player is created + if (stream_->texture() != Resource::getTextureBlack()) { + + // get the texture index from media player, apply it to the media surface + surface_->setTextureIndex( stream_->texture() ); + + // create Frame buffer matching size of media player + float height = float(stream_->width()) / stream_->aspectRatio(); + FrameBuffer *renderbuffer = new FrameBuffer(stream_->width(), (uint)height, true); + + // set the renderbuffer of the source and attach rendering nodes + attach(renderbuffer); + + // done init + initialized_ = true; + Log::Info("Source Stream linked to Stream %d.", stream_->description().c_str()); + + // force update of activation mode + active_ = true; + touch(); + } + } + +} + +void StreamSource::setActive (bool on) +{ + bool was_active = active_; + + Source::setActive(on); + + // change status of media player (only if status changed) + if ( active_ != was_active ) { + stream_->enable(active_); + } +} + +void StreamSource::update(float dt) +{ + Source::update(dt); + + // update stream + stream_->update(); +} + +void StreamSource::render() +{ + if (!initialized_) + init(); + else { + // render the media player into frame buffer + static glm::mat4 projection = glm::ortho(-1.f, 1.f, 1.f, -1.f, -1.f, 1.f); + renderbuffer_->begin(); + surface_->draw(glm::identity(), projection); + renderbuffer_->end(); + } +} diff --git a/StreamSource.h b/StreamSource.h new file mode 100644 index 0000000..84d14ec --- /dev/null +++ b/StreamSource.h @@ -0,0 +1,73 @@ +#ifndef STREAMSOURCE_H +#define STREAMSOURCE_H + + +#include "Stream.h" +#include "Source.h" + +/** + * @brief The StreamSource class + * + * StreamSource is a virtual base class + * (because stream() = 0) + * based on the virtual base class Source + * that implements the update and display + * of a Stream object (gstreamer generic) + * + * StreamSource does *not* create a stream + * in its constructor to let this for the + * specific implementation of the subclass. + * Therefore it cannot be instanciated and + * it cannot give access to its stream. + * + */ +class StreamSource: public Source +{ +public: + StreamSource(); + virtual ~StreamSource(); + + // implementation of source API + void update (float dt) override; + void setActive (bool on) override; + void render() override; + bool failed() const override; + uint texture() const override; + + // pure virtual interface + virtual Stream *stream() const = 0; + +protected: + void init() override; + void replaceRenderingShader() override; + + Surface *surface_; + Stream *stream_; +}; + +/** + * @brief The GenericStreamSource class + * + * Implements the StreamSource + * with an initialization + * using a generic description + * of the gstreamer pipeline. + * + * It can be instanciated. + */ +class GenericStreamSource : public StreamSource +{ +public: + GenericStreamSource(); + + // Source interface + void accept (Visitor& v) override; + + // StreamSource interface + Stream *stream() const override { return stream_; } + + // specific interface + void setDescription(const std::string &desc); +}; + +#endif // STREAMSOURCE_H diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index d6483a7..6529b21 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -53,6 +53,8 @@ using namespace std; #include "MediaPlayer.h" #include "MediaSource.h" #include "PatternSource.h" +#include "DeviceSource.h" +#include "StreamSource.h" #include "PickingVisitor.h" #include "ImageShader.h" #include "ImageProcessingShader.h" @@ -854,7 +856,7 @@ void ToolBox::Render() { if (ImGui::BeginMenu("Render")) { - if ( ImGui::MenuItem( ICON_FA_CAMERA_RETRO " Screenshot") ) + if ( ImGui::MenuItem( ICON_FA_CAMERA_RETRO " Screenshot", "F12") ) UserInterface::manager().StartScreenshot(); ImGui::EndMenu(); @@ -871,6 +873,17 @@ void ToolBox::Render() } + static char buf1[64] = ""; + ImGui::InputText("gstreamer pipeline", buf1, 64); + if (ImGui::Button("Create Generic Stream Source") ) + { +// GenericStreamSource *s = + Mixer::manager().addSource( Mixer::manager().createSourceStream(buf1) ); + } + + // + // display histogram of update time and plot framerate + // // keep array of 120 values, i.e. approx 2 seconds of recording static float framerate_values[2][120] = {{}}; static float sum[2] = { 0.f, 0.f }; diff --git a/Visitor.h b/Visitor.h index 7631499..be5c834 100644 --- a/Visitor.h +++ b/Visitor.h @@ -28,6 +28,8 @@ class ImageProcessingShader; class Source; class MediaSource; class PatternSource; +class DeviceSource; +class GenericStreamSource; class SessionSource; class RenderSource; class CloneSource; @@ -64,6 +66,8 @@ public: // utility virtual void visit (Source&) {} virtual void visit (MediaSource&) {} + virtual void visit (GenericStreamSource&) {} + virtual void visit (DeviceSource&) {} virtual void visit (PatternSource&) {} virtual void visit (SessionSource&) {} virtual void visit (RenderSource&) {}