mirror of
https://github.com/brunoherbelin/vimix.git
synced 2025-12-14 03:39:57 +01:00
Cleanup source tree
Move all C++ source files in the src subfolder. Adapted the cmake process accordingly and cleanup.
This commit is contained in:
712
src/DeviceSource.cpp
Normal file
712
src/DeviceSource.cpp
Normal file
@@ -0,0 +1,712 @@
|
||||
/*
|
||||
* This file is part of vimix - video live mixer
|
||||
*
|
||||
* **Copyright** (C) 2019-2022 Bruno Herbelin <bruno.herbelin@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
**/
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include <gst/pbutils/gstdiscoverer.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
#include <gst/gst.h>
|
||||
|
||||
|
||||
#include "defines.h"
|
||||
#include "Log.h"
|
||||
#include "ImageShader.h"
|
||||
#include "ImageProcessingShader.h"
|
||||
#include "Resource.h"
|
||||
#include "Decorations.h"
|
||||
#include "Stream.h"
|
||||
#include "Visitor.h"
|
||||
#include "CloneSource.h"
|
||||
|
||||
#include "DeviceSource.h"
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define DEVICE_DEBUG
|
||||
#define GST_DEVICE_DEBUG
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#if defined(APPLE)
|
||||
std::string gst_plugin_device = "avfvideosrc";
|
||||
std::string gst_plugin_vidcap = "avfvideosrc capture-screen=true";
|
||||
#else
|
||||
std::string gst_plugin_device = "v4l2src";
|
||||
std::string gst_plugin_vidcap = "ximagesrc";
|
||||
#endif
|
||||
|
||||
std::string pipelineForDevice(GstDevice *device, uint index)
|
||||
{
|
||||
std::ostringstream pipe;
|
||||
GstStructure *stru = gst_device_get_properties(device);
|
||||
|
||||
if (stru) {
|
||||
const gchar *api = gst_structure_get_string(stru, "device.api");
|
||||
|
||||
// check that the api is supported by vimix
|
||||
if (api && gst_plugin_device.find(api) != std::string::npos)
|
||||
{
|
||||
pipe << gst_plugin_device;
|
||||
|
||||
#if defined(APPLE)
|
||||
pipe << " device-index=" << index;
|
||||
#else
|
||||
(void) index; // prevent compilatin warning unused argument
|
||||
|
||||
// get the path: either "device.path" or "api.v4l2.path"
|
||||
// (GStreamer has different ways depending on version)
|
||||
const gchar *path;
|
||||
if ( gst_structure_has_field (stru, "device.path"))
|
||||
path = gst_structure_get_string(stru, "device.path");
|
||||
else
|
||||
path = gst_structure_get_string(stru, "api.v4l2.path");
|
||||
pipe << " device=" << path;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return pipe.str();
|
||||
}
|
||||
|
||||
gboolean
|
||||
Device::callback_device_monitor (GstBus *, GstMessage * message, gpointer )
|
||||
{
|
||||
GstDevice *device;
|
||||
|
||||
switch (GST_MESSAGE_TYPE (message)) {
|
||||
case GST_MESSAGE_DEVICE_ADDED:
|
||||
{
|
||||
gst_message_parse_device_added (message, &device);
|
||||
manager().add(device);
|
||||
gst_object_unref (device);
|
||||
|
||||
}
|
||||
break;
|
||||
case GST_MESSAGE_DEVICE_REMOVED:
|
||||
{
|
||||
gst_message_parse_device_removed (message, &device);
|
||||
manager().remove(device);
|
||||
gst_object_unref (device);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
struct hasDeviceName: public std::unary_function<DeviceHandle, bool>
|
||||
{
|
||||
inline bool operator()(const DeviceHandle &elem) const {
|
||||
return (elem.name.compare(_name) == 0);
|
||||
}
|
||||
explicit hasDeviceName(const std::string &name) : _name(name) { }
|
||||
private:
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
|
||||
struct hasConnectedSource: public std::unary_function<DeviceHandle, bool>
|
||||
{
|
||||
inline bool operator()(const DeviceHandle &elem) const {
|
||||
auto sit = std::find(elem.connected_sources.begin(), elem.connected_sources.end(), s_);
|
||||
return sit != elem.connected_sources.end();
|
||||
}
|
||||
explicit hasConnectedSource(Source *s) : s_(s) { }
|
||||
private:
|
||||
Source *s_;
|
||||
};
|
||||
|
||||
void Device::add(GstDevice *device)
|
||||
{
|
||||
if (device==nullptr)
|
||||
return;
|
||||
|
||||
gchar *device_name = gst_device_get_display_name (device);
|
||||
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
// if device with this name is not already listed
|
||||
auto handle = std::find_if(handles_.cbegin(), handles_.cend(), hasDeviceName(device_name) );
|
||||
if ( handle == handles_.cend() ) {
|
||||
|
||||
std::string p = pipelineForDevice(device, handles_.size());
|
||||
DeviceConfigSet confs = getDeviceConfigs(p);
|
||||
|
||||
// add if not in the list and valid
|
||||
if (!p.empty() && !confs.empty()) {
|
||||
DeviceHandle dev;
|
||||
dev.name = device_name;
|
||||
dev.pipeline = p;
|
||||
dev.configs = confs;
|
||||
handles_.push_back(dev);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *stru = gst_structure_to_string( gst_device_get_properties(device) );
|
||||
g_print("\nDevice %s plugged : %s\n", device_name, stru);
|
||||
g_free (stru);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
|
||||
g_free (device_name);
|
||||
}
|
||||
|
||||
void Device::remove(GstDevice *device)
|
||||
{
|
||||
if (device==nullptr)
|
||||
return;
|
||||
|
||||
gchar *device_name = gst_device_get_display_name (device);
|
||||
|
||||
// lock before change
|
||||
access_.lock();
|
||||
|
||||
// if a device with this name is listed
|
||||
auto handle = std::find_if(handles_.cbegin(), handles_.cend(), hasDeviceName(device_name) );
|
||||
if ( handle != handles_.cend() ) {
|
||||
|
||||
// remove the handle if there is no source connected
|
||||
if (handle->connected_sources.empty())
|
||||
handles_.erase(handle);
|
||||
else {
|
||||
// otherwise unplug all sources
|
||||
for (auto sit = handle->connected_sources.begin(); sit != handle->connected_sources.end(); ++sit)
|
||||
(*sit)->unplug();
|
||||
// and inform user
|
||||
Log::Warning("Device %s unplugged.", device_name);
|
||||
// NB; the handle will be removed at the destruction of the last DeviceSource
|
||||
}
|
||||
}
|
||||
|
||||
// unlock access
|
||||
access_.unlock();
|
||||
|
||||
|
||||
g_free (device_name);
|
||||
}
|
||||
|
||||
Device::Device(): monitor_initialized_(false), monitor_unplug_event_(false)
|
||||
{
|
||||
std::thread(launchMonitoring, this).detach();
|
||||
}
|
||||
|
||||
void Device::launchMonitoring(Device *d)
|
||||
{
|
||||
// gstreamer monitoring of devices
|
||||
d->monitor_ = gst_device_monitor_new ();
|
||||
|
||||
// watching all video stream sources
|
||||
GstCaps *caps = gst_caps_new_empty_simple ("video/x-raw");
|
||||
gst_device_monitor_add_filter (d->monitor_, "Video/Source", caps);
|
||||
gst_caps_unref (caps);
|
||||
gst_device_monitor_set_show_all_devices(d->monitor_, true);
|
||||
gst_device_monitor_start (d->monitor_);
|
||||
|
||||
// Add configs for already plugged devices
|
||||
GList *devices = gst_device_monitor_get_devices(d->monitor_);
|
||||
GList *tmp;
|
||||
for (tmp = devices; tmp ; tmp = tmp->next ) {
|
||||
GstDevice *device = (GstDevice *) tmp->data;
|
||||
d->add(device);
|
||||
gst_object_unref (device);
|
||||
}
|
||||
g_list_free(devices);
|
||||
|
||||
// Try to get captrure screen
|
||||
DeviceConfigSet confs = getDeviceConfigs(gst_plugin_vidcap);
|
||||
if (!confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
DeviceConfigSet confscreen;
|
||||
// limit framerate to 30fps
|
||||
best.fps_numerator = MIN( best.fps_numerator, 30);
|
||||
best.fps_denominator = 1;
|
||||
confscreen.insert(best);
|
||||
// add to config list
|
||||
d->access_.lock();
|
||||
|
||||
DeviceHandle dev;
|
||||
dev.name = "Screen capture";
|
||||
dev.pipeline = gst_plugin_vidcap;
|
||||
dev.configs = confscreen;
|
||||
d->handles_.push_back(dev);
|
||||
d->access_.unlock();
|
||||
}
|
||||
|
||||
// monitor is initialized
|
||||
d->monitor_initialized_ = true;
|
||||
d->monitor_initialization_.notify_all();
|
||||
|
||||
// create a local g_main_context to launch monitoring in this thread
|
||||
GMainContext *_gcontext_device = g_main_context_new();
|
||||
g_main_context_acquire(_gcontext_device);
|
||||
|
||||
// temporarily push as default the default g_main_context for add_watch
|
||||
g_main_context_push_thread_default(_gcontext_device);
|
||||
|
||||
// add a bus watch on the device monitoring using the main loop we created
|
||||
GstBus *bus = gst_device_monitor_get_bus (d->monitor_);
|
||||
gst_bus_add_watch (bus, callback_device_monitor, NULL);
|
||||
gst_object_unref (bus);
|
||||
|
||||
// restore g_main_context
|
||||
g_main_context_pop_thread_default(_gcontext_device);
|
||||
|
||||
// start main loop for this context (blocking infinitely)
|
||||
g_main_loop_run( g_main_loop_new (_gcontext_device, true) );
|
||||
}
|
||||
|
||||
bool Device::initialized()
|
||||
{
|
||||
return Device::manager().monitor_initialized_;
|
||||
}
|
||||
|
||||
int Device::numDevices()
|
||||
{
|
||||
access_.lock();
|
||||
int ret = handles_.size();
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Device::exists(const std::string &device)
|
||||
{
|
||||
access_.lock();
|
||||
auto h = std::find_if(handles_.cbegin(), handles_.cend(), hasDeviceName(device));
|
||||
bool ret = (h != handles_.cend());
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct hasDevice: public std::unary_function<DeviceSource*, bool>
|
||||
{
|
||||
inline bool operator()(const DeviceSource* elem) const {
|
||||
return (elem && elem->device() == _d);
|
||||
}
|
||||
explicit hasDevice(const std::string &d) : _d(d) { }
|
||||
private:
|
||||
std::string _d;
|
||||
};
|
||||
|
||||
std::string Device::name(int index)
|
||||
{
|
||||
std::string ret = "";
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].name;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string Device::description(int index)
|
||||
{
|
||||
std::string ret = "";
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].pipeline;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DeviceConfigSet Device::config(int index)
|
||||
{
|
||||
DeviceConfigSet ret;
|
||||
|
||||
access_.lock();
|
||||
if (index > -1 && index < (int) handles_.size())
|
||||
ret = handles_[index].configs;
|
||||
access_.unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int Device::index(const std::string &device)
|
||||
{
|
||||
int i = -1;
|
||||
|
||||
access_.lock();
|
||||
auto h = std::find_if(handles_.cbegin(), handles_.cend(), hasDeviceName(device));
|
||||
if (h != handles_.cend())
|
||||
i = std::distance(handles_.cbegin(), h);
|
||||
access_.unlock();
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
DeviceSource::DeviceSource(uint64_t id) : StreamSource(id), unplugged_(false)
|
||||
{
|
||||
// set symbol
|
||||
symbol_ = new Symbol(Symbol::CAMERA, glm::vec3(0.75f, 0.75f, 0.01f));
|
||||
symbol_->scale_.y = 1.5f;
|
||||
}
|
||||
|
||||
DeviceSource::~DeviceSource()
|
||||
{
|
||||
unsetDevice();
|
||||
}
|
||||
|
||||
void DeviceSource::unsetDevice()
|
||||
{
|
||||
// unregister this device source from a Device handler
|
||||
auto h = std::find_if(Device::manager().handles_.begin(), Device::manager().handles_.end(), hasConnectedSource(this));
|
||||
if (h != Device::manager().handles_.end())
|
||||
{
|
||||
// remove this pointer to the list of connected sources
|
||||
h->connected_sources.remove(this);
|
||||
// if this is the last source connected to the device handler
|
||||
// the stream will be removed by the ~StreamSource destructor
|
||||
// and the device handler should not keep reference to it
|
||||
if (h->connected_sources.empty()) {
|
||||
// if the cause of deletion is the unplugging of the device
|
||||
if (unplugged_)
|
||||
// remove the handle entirely
|
||||
Device::manager().handles_.erase(h);
|
||||
else
|
||||
// otherwise just cancel the reference to the stream
|
||||
h->stream = nullptr;
|
||||
}
|
||||
// else this means another DeviceSource is using this stream
|
||||
// and we should avoid to delete the stream in the ~StreamSource destructor
|
||||
else
|
||||
stream_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DeviceSource::setDevice(const std::string &devicename)
|
||||
{
|
||||
if (device_.compare(devicename) == 0)
|
||||
return;
|
||||
|
||||
// instanciate and wait for monitor initialization if not already initialized
|
||||
std::mutex mtx;
|
||||
std::unique_lock<std::mutex> lck(mtx);
|
||||
Device::manager().monitor_initialization_.wait(lck, Device::initialized);
|
||||
|
||||
// if changing device
|
||||
if (!device_.empty())
|
||||
unsetDevice();
|
||||
|
||||
// remember device name
|
||||
device_ = devicename;
|
||||
|
||||
// check existence of a device handle with that name
|
||||
auto h = std::find_if(Device::manager().handles_.begin(), Device::manager().handles_.end(), hasDeviceName(device_));
|
||||
if ( h != Device::manager().handles_.end()) {
|
||||
|
||||
// find if a DeviceHandle with this device name already has a stream
|
||||
if ( h->stream != nullptr) {
|
||||
// just use it !
|
||||
stream_ = h->stream;
|
||||
}
|
||||
else {
|
||||
// start filling in the gstreamer pipeline
|
||||
std::ostringstream pipeline;
|
||||
pipeline << h->pipeline;
|
||||
|
||||
// test the device and get config
|
||||
DeviceConfigSet confs = h->configs;
|
||||
#ifdef DEVICE_DEBUG
|
||||
Log::Info("Device %s supported configs:", devicename.c_str());
|
||||
for( DeviceConfigSet::iterator it = confs.begin(); it != confs.end(); ++it ){
|
||||
float fps = static_cast<float>((*it).fps_numerator) / static_cast<float>((*it).fps_denominator);
|
||||
Log::Info(" - %s %s %d x %d %.1f fps", (*it).stream.c_str(), (*it).format.c_str(), (*it).width, (*it).height, fps);
|
||||
}
|
||||
#endif
|
||||
if (!confs.empty()) {
|
||||
DeviceConfig best = *confs.rbegin();
|
||||
float fps = static_cast<float>(best.fps_numerator) / static_cast<float>(best.fps_denominator);
|
||||
Log::Info("Device %s selected its optimal config: %s %s %dx%d@%.1ffps", device_.c_str(), best.stream.c_str(), best.format.c_str(), best.width, best.height, fps);
|
||||
|
||||
pipeline << " ! " << best.stream;
|
||||
if (!best.format.empty())
|
||||
pipeline << ",format=" << best.format;
|
||||
pipeline << ",framerate=" << best.fps_numerator << "/" << best.fps_denominator;
|
||||
pipeline << ",width=" << best.width;
|
||||
pipeline << ",height=" << best.height;
|
||||
|
||||
if ( best.stream.find("jpeg") != std::string::npos )
|
||||
pipeline << " ! jpegdec";
|
||||
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
pipeline << " ! videoconvert ! video/x-raw,format=RGB ! queue max-size-buffers=3";
|
||||
|
||||
pipeline << " ! videoconvert";
|
||||
|
||||
// delete and reset render buffer to enforce re-init of StreamSource
|
||||
if (renderbuffer_)
|
||||
delete renderbuffer_;
|
||||
renderbuffer_ = nullptr;
|
||||
|
||||
// new stream
|
||||
if (stream_)
|
||||
delete stream_;
|
||||
stream_ = h->stream = new Stream;
|
||||
|
||||
// open gstreamer
|
||||
h->stream->open( pipeline.str(), best.width, best.height);
|
||||
h->stream->play(true);
|
||||
}
|
||||
}
|
||||
|
||||
// reference this source in the handle
|
||||
h->connected_sources.push_back(this);
|
||||
|
||||
// will be ready after init and one frame rendered
|
||||
ready_ = false;
|
||||
}
|
||||
else {
|
||||
unplugged_ = true;
|
||||
Log::Warning("No such device '%s'", device_.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceSource::setActive (bool on)
|
||||
{
|
||||
bool was_active = active_;
|
||||
|
||||
// try to activate (may fail if source is cloned)
|
||||
Source::setActive(on);
|
||||
|
||||
if (stream_) {
|
||||
// change status of stream (only if status changed)
|
||||
if (active_ != was_active) {
|
||||
|
||||
// activate a source if any of the handled device source is active
|
||||
auto h = std::find_if(Device::manager().handles_.begin(), Device::manager().handles_.end(), hasConnectedSource(this));
|
||||
if (h != Device::manager().handles_.end()) {
|
||||
bool streamactive = false;
|
||||
for (auto sit = h->connected_sources.begin(); sit != h->connected_sources.end(); ++sit) {
|
||||
if ( (*sit)->active_)
|
||||
streamactive = true;
|
||||
}
|
||||
stream_->enable(streamactive);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DeviceSource::accept(Visitor& v)
|
||||
{
|
||||
Source::accept(v);
|
||||
if (!failed())
|
||||
v.visit(*this);
|
||||
}
|
||||
|
||||
bool DeviceSource::failed() const
|
||||
{
|
||||
return unplugged_ || StreamSource::failed();
|
||||
}
|
||||
|
||||
DeviceConfigSet Device::getDeviceConfigs(const std::string &src_description)
|
||||
{
|
||||
DeviceConfigSet configs;
|
||||
|
||||
// create dummy pipeline to be tested
|
||||
std::string description = src_description;
|
||||
description += " name=devsrc ! 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* pad_ret = NULL;
|
||||
if (gst_iterator_next(iter, &vPad) == GST_ITERATOR_OK)
|
||||
{
|
||||
pad_ret = GST_PAD(g_value_get_object(&vPad));
|
||||
GstCaps *device_caps = gst_pad_query_caps (pad_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) {
|
||||
// get GST cap
|
||||
GstStructure *decice_cap_struct = gst_caps_get_structure (device_caps, c);
|
||||
#ifdef GST_DEVICE_DEBUG
|
||||
gchar *capstext = gst_structure_to_string (decice_cap_struct);
|
||||
g_print("\nDevice caps: %s", capstext);
|
||||
g_free(capstext);
|
||||
#endif
|
||||
|
||||
// fill our config
|
||||
DeviceConfig config;
|
||||
|
||||
// not managing opengl texture-target types
|
||||
// TODO: support input devices texture-target video/x-raw(memory:GLMemory) for improved pipeline
|
||||
if ( gst_structure_has_field (decice_cap_struct, "texture-target"))
|
||||
continue;
|
||||
|
||||
// NAME : typically video/x-raw or image/jpeg
|
||||
config.stream = gst_structure_get_name (decice_cap_struct);
|
||||
|
||||
// FORMAT : typically BGRA or YUVY
|
||||
if ( gst_structure_has_field (decice_cap_struct, "format")) {
|
||||
// get generic value
|
||||
const GValue *val = gst_structure_get_value(decice_cap_struct, "format");
|
||||
|
||||
// if its a list of format string
|
||||
if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int n = 0; n < N; n++ ){
|
||||
std::string f = gst_value_serialize( gst_value_list_get_value(val, n) );
|
||||
|
||||
// preference order : 1) RGBx, 2) JPEG, 3) ALL OTHER
|
||||
// select f if it contains R (e.g. for RGBx) and not already RGB in config
|
||||
if ( (f.find("R") != std::string::npos) && (config.format.find("R") == std::string::npos ) ) {
|
||||
config.format = f;
|
||||
break;
|
||||
}
|
||||
// default, take at least one if nothing yet in config
|
||||
else if ( config.format.empty() )
|
||||
config.format = f;
|
||||
}
|
||||
|
||||
}
|
||||
// single format
|
||||
else {
|
||||
config.format = gst_value_serialize(val);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
// if its a range of fraction; take the max
|
||||
else if ( GST_VALUE_HOLDS_FRACTION_RANGE(val)) {
|
||||
config.fps_numerator = gst_value_get_fraction_numerator(gst_value_get_fraction_range_max(val));
|
||||
config.fps_denominator= gst_value_get_fraction_denominator(gst_value_get_fraction_range_max(val));
|
||||
}
|
||||
// deal otherwise with a list of fractions; find the max
|
||||
else if ( GST_VALUE_HOLDS_LIST(val)) {
|
||||
gdouble fps_max = 1.0;
|
||||
// loop over all fractions
|
||||
int N = gst_value_list_get_size(val);
|
||||
for (int i = 0; i < N; ++i ){
|
||||
const GValue *frac = gst_value_list_get_value(val, i);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// default
|
||||
config.fps_numerator = 30;
|
||||
config.fps_denominator = 1;
|
||||
}
|
||||
|
||||
// WIDTH and HEIGHT
|
||||
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);
|
||||
|
||||
|
||||
// add this config
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
glm::ivec2 DeviceSource::icon() const
|
||||
{
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
return glm::ivec2(ICON_SOURCE_DEVICE_SCREEN);
|
||||
else
|
||||
return glm::ivec2(ICON_SOURCE_DEVICE);
|
||||
}
|
||||
|
||||
std::string DeviceSource::info() const
|
||||
{
|
||||
if ( device_.find("Screen") != std::string::npos )
|
||||
return "Screen capture";
|
||||
else
|
||||
return "Device";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user