mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-11 18:34:58 +01:00
Integrated preliminary implementation of Device class with monitoring of
v4l2 device connected to the machine using GstDeviceMonitor.
This commit is contained in:
178
DeviceSource.cpp
178
DeviceSource.cpp
@@ -1,3 +1,4 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
@@ -12,49 +13,180 @@
|
|||||||
#include "Visitor.h"
|
#include "Visitor.h"
|
||||||
#include "Log.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 )
|
//// std::string desc = "v4l2src ! video/x-raw,width=320,height=240,framerate=30/1 ! videoconvert";
|
||||||
{
|
//// std::string desc = "v4l2src ! jpegdec ! videoconvert";
|
||||||
device_ = CLAMP(device, 0, 2);
|
// 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 ! videoscale ! 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 = "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()
|
DeviceSource::DeviceSource() : StreamSource()
|
||||||
{
|
{
|
||||||
// create stream
|
// create stream
|
||||||
stream_ = (Stream *) new Device();
|
stream_ = (Stream *) new Stream();
|
||||||
|
|
||||||
// set icons TODO
|
// set icons TODO
|
||||||
overlays_[View::MIXING]->attach( new Symbol(Symbol::EMPTY, glm::vec3(0.8f, 0.8f, 0.01f)) );
|
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)) );
|
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);
|
stream_->play(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,7 +196,9 @@ void DeviceSource::accept(Visitor& v)
|
|||||||
v.visit(*this);
|
v.visit(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Device *DeviceSource::device() const
|
bool DeviceSource::failed() const
|
||||||
{
|
{
|
||||||
return dynamic_cast<Device *>(stream_);
|
return stream_->failed() || !Device::manager().exists(device_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,62 @@
|
|||||||
#ifndef DEVICESOURCE_H
|
#ifndef DEVICESOURCE_H
|
||||||
#define DEVICESOURCE_H
|
#define DEVICESOURCE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "GstToolkit.h"
|
||||||
#include "StreamSource.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:
|
public:
|
||||||
|
|
||||||
Device();
|
static Device& manager()
|
||||||
void open( uint deviceid );
|
{
|
||||||
|
// 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:
|
private:
|
||||||
uint device_;
|
|
||||||
|
void remove(const char *device);
|
||||||
|
std::vector< std::string > names_;
|
||||||
|
std::vector< std::string > pipelines_;
|
||||||
|
GstDeviceMonitor *monitor_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class DeviceSource : public StreamSource
|
class DeviceSource : public StreamSource
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DeviceSource();
|
DeviceSource();
|
||||||
|
|
||||||
// Source interface
|
// Source interface
|
||||||
|
bool failed() const override;
|
||||||
void accept (Visitor& v) override;
|
void accept (Visitor& v) override;
|
||||||
|
|
||||||
// StreamSource interface
|
// StreamSource interface
|
||||||
Stream *stream() const override { return stream_; }
|
Stream *stream() const override { return stream_; }
|
||||||
|
|
||||||
// specific interface
|
// specific interface
|
||||||
Device *device() const;
|
void setDevice(const std::string &devicename);
|
||||||
void setDevice(int id);
|
inline std::string device() const { return device_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string device_;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -291,14 +291,14 @@ Source * Mixer::createSourcePattern(int pattern, glm::ivec2 res)
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
Source * Mixer::createSourceDevice(int id)
|
Source * Mixer::createSourceDevice(const std::string &namedevice)
|
||||||
{
|
{
|
||||||
// ready to create a source
|
// ready to create a source
|
||||||
DeviceSource *s = new DeviceSource();
|
DeviceSource *s = new DeviceSource();
|
||||||
s->setDevice(id);
|
s->setDevice(namedevice);
|
||||||
|
|
||||||
// propose a new name based on pattern name
|
// propose a new name based on pattern name
|
||||||
renameSource(s, "Device");
|
renameSource(s, namedevice);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|||||||
2
Mixer.h
2
Mixer.h
@@ -41,7 +41,7 @@ public:
|
|||||||
Source * createSourceRender ();
|
Source * createSourceRender ();
|
||||||
Source * createSourceStream (const std::string &gstreamerpipeline);
|
Source * createSourceStream (const std::string &gstreamerpipeline);
|
||||||
Source * createSourcePattern(int pattern, glm::ivec2 res);
|
Source * createSourcePattern(int pattern, glm::ivec2 res);
|
||||||
Source * createSourceDevice (int id);
|
Source * createSourceDevice (const std::string &namedevice);
|
||||||
|
|
||||||
// operations on sources
|
// operations on sources
|
||||||
void addSource (Source *s);
|
void addSource (Source *s);
|
||||||
|
|||||||
70
Stream.cpp
70
Stream.cpp
@@ -128,48 +128,44 @@ void Stream::execute_open()
|
|||||||
|
|
||||||
// setup appsink
|
// setup appsink
|
||||||
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink");
|
GstElement *sink = gst_bin_get_by_name (GST_BIN (pipeline_), "sink");
|
||||||
if (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 {
|
|
||||||
Log::Warning("Stream %d Could not configure sink", id_);
|
Log::Warning("Stream %d Could not configure sink", id_);
|
||||||
failed_ = true;
|
failed_ = true;
|
||||||
return;
|
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)
|
// set to desired state (PLAY or PAUSE)
|
||||||
GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_);
|
GstStateChangeReturn ret = gst_element_set_state (pipeline_, desired_state_);
|
||||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||||
|
|||||||
@@ -2013,12 +2013,16 @@ void Navigator::RenderNewPannel()
|
|||||||
|
|
||||||
ImGui::SetCursorPosY(2.f * width_);
|
ImGui::SetCursorPosY(2.f * width_);
|
||||||
|
|
||||||
|
|
||||||
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
|
||||||
if (ImGui::BeginCombo("##Hardware", "Select device"))
|
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();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user