diff --git a/DeviceSource.cpp b/DeviceSource.cpp index b56861f..05ac701 100644 --- a/DeviceSource.cpp +++ b/DeviceSource.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -12,49 +13,180 @@ #include "Visitor.h" #include "Log.h" -Device::Device() : Stream() + +gboolean +Device::callback_device_monitor (GstBus * bus, GstMessage * message, gpointer user_data) { + GstDevice *device; + gchar *name; + gchar *stru; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_DEVICE_ADDED: { + gst_message_parse_device_added (message, &device); + name = gst_device_get_display_name (device); + manager().names_.push_back(name); + g_free (name); + + std::ostringstream pipe; + pipe << gst_structure_get_string(gst_device_get_properties(device), "device.api"); + pipe << "src device="; + pipe << gst_structure_get_string(gst_device_get_properties(device), "device.path"); + manager().pipelines_.push_back(pipe.str()); + + stru = gst_structure_to_string( gst_device_get_properties(device) ); + g_print("New device %s \n", stru); + + gst_object_unref (device); + } + break; + case GST_MESSAGE_DEVICE_REMOVED: { + gst_message_parse_device_removed (message, &device); + name = gst_device_get_display_name (device); + manager().remove(name); + g_print("Device removed: %s\n", name); + g_free (name); + + gst_object_unref (device); + } + break; + default: + break; + } + + return G_SOURCE_CONTINUE; +} + +void Device::remove(const char *device) +{ + std::vector< std::string >::iterator nameit = names_.begin(); + std::vector< std::string >::iterator pipeit = pipelines_.begin(); + while (nameit != names_.end()){ + + if ( (*nameit).compare(device) == 0 ) + { + names_.erase(nameit); + pipelines_.erase(pipeit); + break; + } + + nameit++; + pipeit++; + } +} + +Device::Device() +{ + GstBus *bus; + GstCaps *caps; + + // create GStreamer device monitor to capture + // when a device is plugged in or out + monitor_ = gst_device_monitor_new (); + + bus = gst_device_monitor_get_bus (monitor_); + gst_bus_add_watch (bus, callback_device_monitor, NULL); + gst_object_unref (bus); + + caps = gst_caps_new_empty_simple ("video/x-raw"); + gst_device_monitor_add_filter (monitor_, "Video/Source", caps); + gst_caps_unref (caps); + + gst_device_monitor_set_show_all_devices(monitor_, true); + gst_device_monitor_start (monitor_); + + // initial fill of the list + GList *devices = gst_device_monitor_get_devices(monitor_); + GList *tmp; + for (tmp = devices; tmp ; tmp = tmp->next ) { + + GstDevice *device = (GstDevice *) tmp->data; + + gchar *name = gst_device_get_display_name (device); + names_.push_back(name); + g_free (name); + + std::ostringstream pipe; + pipe << gst_structure_get_string(gst_device_get_properties(device), "device.api"); + pipe << "src device="; + pipe << gst_structure_get_string(gst_device_get_properties(device), "device.path"); + pipelines_.push_back(pipe.str()); + + } + g_list_free(devices); } -glm::ivec2 Device::resolution() + +int Device::numDevices() const { - return glm::ivec2( width_, height_); + return names_.size(); +} + +bool Device::exists(const std::string &device) const +{ + std::vector< std::string >::const_iterator d = std::find(names_.begin(), names_.end(), device); + return d != names_.end(); +} + +std::string Device::name(int index) const +{ + if (index > -1 && index < names_.size()) + return names_[index]; + else + return ""; +} + +std::string Device::pipeline(int index) const +{ + if (index > -1 && index < pipelines_.size()) + return pipelines_[index]; + else + return ""; +} + +std::string Device::pipeline(const std::string &device) const +{ + std::string pip = ""; + std::vector< std::string >::const_iterator p = std::find(names_.begin(), names_.end(), device); + if (p != names_.end()) + { + int index = std::distance(names_.begin(), p); + pip = pipelines_[index]; + } + return pip; } -void Device::open( uint device ) -{ - device_ = CLAMP(device, 0, 2); +//// std::string desc = "v4l2src ! video/x-raw,width=320,height=240,framerate=30/1 ! videoconvert"; +//// std::string desc = "v4l2src ! jpegdec ! videoconvert"; +// std::string desc = "v4l2src ! image/jpeg,width=640,height=480,framerate=30/1 ! jpegdec ! videoscale ! videoconvert"; -// std::string desc = "v4l2src ! video/x-raw,width=320,height=240,framerate=30/1 ! videoconvert"; -// std::string desc = "v4l2src ! jpegdec ! videoconvert"; - std::string desc = "v4l2src ! image/jpeg,width=640,height=480,framerate=30/1 ! jpegdec ! videoscale ! videoconvert"; +//// std::string desc = "v4l2src ! jpegdec ! videoscale ! videoconvert"; -// std::string desc = "v4l2src ! jpegdec ! videoscale ! videoconvert"; +//// std::string desc = "videotestsrc pattern=snow is-live=true "; +//// std::string desc = "ximagesrc endx=800 endy=600 ! video/x-raw,framerate=15/1 ! videoscale ! videoconvert"; -// std::string desc = "videotestsrc pattern=snow is-live=true "; -// std::string desc = "ximagesrc endx=800 endy=600 ! video/x-raw,framerate=15/1 ! videoscale ! videoconvert"; - - // (private) open stream - Stream::open(desc); -} DeviceSource::DeviceSource() : StreamSource() { // create stream - stream_ = (Stream *) new Device(); + stream_ = (Stream *) new Stream(); // set icons TODO 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) +void DeviceSource::setDevice(const std::string &devicename) { - Log::Notify("Creating Source with device '%d'", id); + device_ = devicename; + Log::Notify("Creating Source with device '%s'", device_.c_str()); - device()->open(id); + std::string pipeline = Device::manager().pipeline(device_); + pipeline += " ! jpegdec ! videoscale ! videoconvert"; + + stream_->open( pipeline ); stream_->play(true); } @@ -64,7 +196,9 @@ void DeviceSource::accept(Visitor& v) v.visit(*this); } -Device *DeviceSource::device() const +bool DeviceSource::failed() const { - return dynamic_cast(stream_); + return stream_->failed() || !Device::manager().exists(device_); } + + diff --git a/DeviceSource.h b/DeviceSource.h index 3ca2d0d..13f12b1 100644 --- a/DeviceSource.h +++ b/DeviceSource.h @@ -1,35 +1,62 @@ #ifndef DEVICESOURCE_H #define DEVICESOURCE_H +#include + +#include "GstToolkit.h" #include "StreamSource.h" -class Device : public Stream +class Device { + Device(); + Device(Rendering const& copy); // Not Implemented + Device& operator=(Rendering const& copy); // Not Implemented + public: - Device(); - void open( uint deviceid ); + static Device& manager() + { + // The only instance + static Device _instance; + return _instance; + } - glm::ivec2 resolution(); + int numDevices () const; + std::string name (int index) const; + bool exists (const std::string &device) const; + + std::string pipeline (int index) const; + std::string pipeline (const std::string &device) const; + + static gboolean callback_device_monitor (GstBus *, GstMessage *, gpointer); private: - uint device_; + + void remove(const char *device); + std::vector< std::string > names_; + std::vector< std::string > pipelines_; + GstDeviceMonitor *monitor_; }; + class DeviceSource : public StreamSource { public: DeviceSource(); // Source interface + bool failed() const override; void accept (Visitor& v) override; // StreamSource interface Stream *stream() const override { return stream_; } // specific interface - Device *device() const; - void setDevice(int id); + void setDevice(const std::string &devicename); + inline std::string device() const { return device_; } + +private: + std::string device_; }; diff --git a/Mixer.cpp b/Mixer.cpp index d288a3b..ff45929 100644 --- a/Mixer.cpp +++ b/Mixer.cpp @@ -291,14 +291,14 @@ Source * Mixer::createSourcePattern(int pattern, glm::ivec2 res) return s; } -Source * Mixer::createSourceDevice(int id) +Source * Mixer::createSourceDevice(const std::string &namedevice) { // ready to create a source DeviceSource *s = new DeviceSource(); - s->setDevice(id); + s->setDevice(namedevice); // propose a new name based on pattern name - renameSource(s, "Device"); + renameSource(s, namedevice); return s; } diff --git a/Mixer.h b/Mixer.h index abab4b7..973657e 100644 --- a/Mixer.h +++ b/Mixer.h @@ -41,7 +41,7 @@ public: Source * createSourceRender (); Source * createSourceStream (const std::string &gstreamerpipeline); Source * createSourcePattern(int pattern, glm::ivec2 res); - Source * createSourceDevice (int id); + Source * createSourceDevice (const std::string &namedevice); // operations on sources void addSource (Source *s); diff --git a/Stream.cpp b/Stream.cpp index a1d3a47..0e1f994 100644 --- a/Stream.cpp +++ b/Stream.cpp @@ -128,48 +128,44 @@ void Stream::execute_open() // setup appsink GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink"); - if (sink) { -// // instruct the sink to send samples synched in time -// gst_base_sink_set_sync (GST_BASE_SINK(sink), false); - - // instruct sink to use the required caps - gst_app_sink_set_caps (GST_APP_SINK(sink), caps); - - // Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached. - gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 50); - gst_app_sink_set_drop (GST_APP_SINK(sink), true); - -#ifdef USE_GST_APPSINK_CALLBACKS_ - // set the callbacks - GstAppSinkCallbacks callbacks; - if (single_frame_) { - callbacks.new_preroll = callback_new_preroll; - callbacks.eos = NULL; - callbacks.new_sample = NULL; - } - else { - callbacks.new_preroll = callback_new_preroll; - callbacks.eos = callback_end_of_stream; - callbacks.new_sample = callback_new_sample; - } - gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); - gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); -#else - // connect signals callbacks - g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this); - if (!single_frame_) { - g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this); - g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this); - } - gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true); -#endif - } - else { + if (!sink) { Log::Warning("Stream %d Could not configure sink", id_); failed_ = true; return; } + // instruct sink to use the required caps + gst_app_sink_set_caps (GST_APP_SINK(sink), caps); + + // Instruct appsink to drop old buffers when the maximum amount of queued buffers is reached. + gst_app_sink_set_max_buffers( GST_APP_SINK(sink), 50); + gst_app_sink_set_drop (GST_APP_SINK(sink), true); + +#ifdef USE_GST_APPSINK_CALLBACKS_ + // set the callbacks + GstAppSinkCallbacks callbacks; + if (single_frame_) { + callbacks.new_preroll = callback_new_preroll; + callbacks.eos = NULL; + callbacks.new_sample = NULL; + } + else { + callbacks.new_preroll = callback_new_preroll; + callbacks.eos = callback_end_of_stream; + callbacks.new_sample = callback_new_sample; + } + gst_app_sink_set_callbacks (GST_APP_SINK(sink), &callbacks, this, NULL); + gst_app_sink_set_emit_signals (GST_APP_SINK(sink), false); +#else + // connect signals callbacks + g_signal_connect(G_OBJECT(sink), "new-preroll", G_CALLBACK (callback_new_preroll), this); + if (!single_frame_) { + g_signal_connect(G_OBJECT(sink), "new-sample", G_CALLBACK (callback_new_sample), this); + g_signal_connect(G_OBJECT(sink), "eos", G_CALLBACK (callback_end_of_stream), this); + } + gst_app_sink_set_emit_signals (GST_APP_SINK(sink), true); +#endif + // set to desired state (PLAY or PAUSE) GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_); if (ret == GST_STATE_CHANGE_FAILURE) { diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 8f826e4..72af976 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -2013,12 +2013,16 @@ void Navigator::RenderNewPannel() ImGui::SetCursorPosY(2.f * width_); + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); if (ImGui::BeginCombo("##Hardware", "Select device")) { - if (ImGui::Selectable( "device 0" )) { + for (int d = 0; d < Device::manager().numDevices(); ++d){ + std::string namedev = Device::manager().name(d); + if (ImGui::Selectable( namedev.c_str() )) { - new_source_preview_.setSource( Mixer::manager().createSourceDevice(0), "device"); + new_source_preview_.setSource( Mixer::manager().createSourceDevice(namedev), namedev); + } } ImGui::EndCombo(); }