First draft of an automatic discoverer for gst device source caps

(framerate, resolution and image format) to enable auto creation of
DeviceSource.
This commit is contained in:
brunoherbelin
2020-09-25 00:05:35 +02:00
parent 84caf2da9a
commit 202db9eaa2
4 changed files with 193 additions and 11 deletions

View File

@@ -2,6 +2,10 @@
#include <sstream> #include <sstream>
#include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/matrix_transform.hpp>
#include <gst/pbutils/gstdiscoverer.h>
#include <gst/pbutils/pbutils.h>
#include <gst/gst.h>
#include "DeviceSource.h" #include "DeviceSource.h"
#include "defines.h" #include "defines.h"
@@ -30,7 +34,7 @@ Device::callback_device_monitor (GstBus * bus, GstMessage * message, gpointer us
std::ostringstream pipe; std::ostringstream pipe;
pipe << gst_structure_get_string(gst_device_get_properties(device), "device.api"); pipe << gst_structure_get_string(gst_device_get_properties(device), "device.api");
pipe << "src device="; pipe << "src name=devsrc device=";
pipe << gst_structure_get_string(gst_device_get_properties(device), "device.path"); pipe << gst_structure_get_string(gst_device_get_properties(device), "device.path");
manager().pipelines_.push_back(pipe.str()); manager().pipelines_.push_back(pipe.str());
@@ -108,7 +112,7 @@ Device::Device()
std::ostringstream pipe; std::ostringstream pipe;
pipe << gst_structure_get_string(gst_device_get_properties(device), "device.api"); pipe << gst_structure_get_string(gst_device_get_properties(device), "device.api");
pipe << "src device="; pipe << "src name=devsrc device=";
pipe << gst_structure_get_string(gst_device_get_properties(device), "device.path"); pipe << gst_structure_get_string(gst_device_get_properties(device), "device.path");
pipelines_.push_back(pipe.str()); pipelines_.push_back(pipe.str());
@@ -179,14 +183,31 @@ DeviceSource::DeviceSource() : StreamSource()
} }
void DeviceSource::setDevice(const std::string &devicename) void DeviceSource::setDevice(const std::string &devicename)
{ {
device_ = devicename; device_ = devicename;
Log::Notify("Creating Source with device '%s'", device_.c_str()); Log::Notify("Creating Source with device '%s'", device_.c_str());
std::string pipeline = Device::manager().pipeline(device_); std::ostringstream pipeline;
pipeline += " ! jpegdec ! videoscale ! videoconvert"; pipeline << Device::manager().pipeline(device_);
stream_->open( pipeline ); DeviceInfoSet confs = getDeviceConfigs(pipeline.str());
DeviceInfoSet::reverse_iterator best = confs.rbegin();
pipeline << " ! " << (*best).format;
pipeline << ",framerate=" << (*best).fps_numerator << "/" << (*best).fps_denominator;
pipeline << ",width=" << (*best).width;
pipeline << ",height=" << (*best).height;
if ( (*best).format.find("jpeg") != std::string::npos )
pipeline << " ! jpegdec";
// for( DeviceInfoSet::iterator it = confs.begin(); it != confs.end(); it++ ){
// Log::Info("config possible : %s %dx%d @ %d fps", (*it).format.c_str(), (*it).width, (*it).height, (*it).fps_numerator);
// }
pipeline << " ! videoconvert";
stream_->open( pipeline.str(), (*best).width, (*best).height);
stream_->play(true); stream_->play(true);
} }
@@ -202,3 +223,116 @@ bool DeviceSource::failed() const
} }
DeviceInfoSet DeviceSource::getDeviceConfigs(const std::string &pipeline)
{
DeviceInfoSet configs;
// create dummy pipeline to be tested
std::string description = pipeline;
description += " ! fakesink name=sink";
// parse pipeline descriptor
GError *error = NULL;
GstElement *pipeline_ = gst_parse_launch (description.c_str(), &error);
if (error != NULL) {
Log::Warning("DeviceSource Could not construct test pipeline %s:\n%s", description.c_str(), error->message);
g_clear_error (&error);
return configs;
}
// get the pipeline element named "devsrc" from the Device class
GstElement *elem = gst_bin_get_by_name (GST_BIN (pipeline_), "devsrc");
if (elem) {
// initialize the pipeline
GstStateChangeReturn ret = gst_element_set_state (pipeline_, GST_STATE_PAUSED);
if (ret != GST_STATE_CHANGE_FAILURE) {
// get the first pad and its content
GstIterator *iter = gst_element_iterate_src_pads(elem);
GValue vPad = G_VALUE_INIT;
GstPad* ret = NULL;
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
{
ret = GST_PAD(g_value_get_object(&vPad));
GstCaps *device_caps = gst_pad_query_caps (ret, NULL);
// loop over all caps offered by the pad
int C = gst_caps_get_size(device_caps);
for (int c = 0; c < C; ++c) {
GstStructure *decice_cap_struct = gst_caps_get_structure (device_caps, c);
DeviceInfo config;
// NAME : typically video/x-raw or image/jpeg
config.format = gst_structure_get_name (decice_cap_struct);
// FRAMERATE : can be a fraction of a list of fractions
if ( gst_structure_has_field (decice_cap_struct, "framerate")) {
// get generic value
const GValue *val = gst_structure_get_value(decice_cap_struct, "framerate");
// if its a single fraction
if ( GST_VALUE_HOLDS_FRACTION(val)) {
config.fps_numerator = gst_value_get_fraction_numerator(val);
config.fps_denominator= gst_value_get_fraction_denominator(val);
}
// deal otherwise with a list of fractions
else {
gdouble fps_max = 1.0;
// loop over all fractions
int N = gst_value_list_get_size(val);
for (int n = 0; n < N; n++ ){
const GValue *frac = gst_value_list_get_value(val, n);
// read one fraction in the list
if ( GST_VALUE_HOLDS_FRACTION(frac)) {
int n = gst_value_get_fraction_numerator(frac);
int d = gst_value_get_fraction_denominator(frac);
// keep only the higher FPS
gdouble f = 1.0;
gst_util_fraction_to_double( n, d, &f );
if ( f > fps_max ) {
config.fps_numerator = n;
config.fps_denominator = d;
fps_max = f;
}
}
}
}
}
if ( gst_structure_has_field (decice_cap_struct, "width"))
gst_structure_get_int (decice_cap_struct, "width", &config.width);
if ( gst_structure_has_field (decice_cap_struct, "height"))
gst_structure_get_int (decice_cap_struct, "height", &config.height);
// gchar *capstext = gst_structure_to_string (decice_cap_struct);
// Log::Info("DeviceSource found cap struct %s", capstext);
// g_free(capstext);
configs.insert(config);
}
}
gst_iterator_free(iter);
// terminate pipeline
gst_element_set_state (pipeline_, GST_STATE_NULL);
}
g_object_unref (elem);
}
gst_object_unref (pipeline_);
return configs;
}

View File

@@ -2,6 +2,7 @@
#define DEVICESOURCE_H #define DEVICESOURCE_H
#include <vector> #include <vector>
#include <set>
#include "GstToolkit.h" #include "GstToolkit.h"
#include "StreamSource.h" #include "StreamSource.h"
@@ -23,9 +24,9 @@ public:
int numDevices () const; int numDevices () const;
std::string name (int index) const; std::string name (int index) const;
bool exists (const std::string &device) const;
std::string pipeline (int index) const; std::string pipeline (int index) const;
bool exists (const std::string &device) const;
std::string pipeline (const std::string &device) const; std::string pipeline (const std::string &device) const;
static gboolean callback_device_monitor (GstBus *, GstMessage *, gpointer); static gboolean callback_device_monitor (GstBus *, GstMessage *, gpointer);
@@ -39,6 +40,49 @@ private:
}; };
struct DeviceInfo {
gint width;
gint height;
gint fps_numerator;
gint fps_denominator;
std::string format;
DeviceInfo() {
width = 0;
height = 0;
fps_numerator = 1;
fps_denominator = 1;
format = "";
}
inline DeviceInfo& operator = (const DeviceInfo& b)
{
if (this != &b) {
this->width = b.width;
this->height = b.height;
this->fps_numerator = b.fps_numerator;
this->fps_denominator = b.fps_denominator;
this->format = b.format;
}
return *this;
}
inline bool operator < (const DeviceInfo b) const
{
return ( this->fps_numerator * this->height < b.fps_numerator * b.height );
}
};
struct better_device_comparator
{
inline bool operator () (const DeviceInfo a, const DeviceInfo b) const
{
return (a < b);
}
};
typedef std::set<DeviceInfo, better_device_comparator> DeviceInfoSet;
class DeviceSource : public StreamSource class DeviceSource : public StreamSource
{ {
public: public:
@@ -58,6 +102,8 @@ public:
private: private:
std::string device_; std::string device_;
DeviceInfoSet getDeviceConfigs(const std::string &pipeline);
}; };
#endif // DEVICESOURCE_H #endif // DEVICESOURCE_H

View File

@@ -72,11 +72,14 @@ guint Stream::texture() const
} }
void Stream::open(const std::string &gstreamer_description) void Stream::open(const std::string &gstreamer_description, int w, int h)
{ {
// set gstreamer pipeline source // set gstreamer pipeline source
description_ = gstreamer_description; description_ = gstreamer_description;
if (w>0) width_ = w;
if (h>0) height_ = h;
// close before re-openning // close before re-openning
if (isOpen()) if (isOpen())
close(); close();
@@ -95,7 +98,6 @@ int Stream::id() const
return id_; return id_;
} }
void Stream::execute_open() void Stream::execute_open()
{ {
// reset // reset

View File

@@ -29,7 +29,7 @@ public:
/** /**
* Open a media using gstreamer pipeline keyword * Open a media using gstreamer pipeline keyword
* */ * */
void open(const std::string &gstreamer_description); void open(const std::string &gstreamer_description, int w = -1, int h = -1);
/** /**
* Get description string * Get description string
* */ * */