From 0f0f9d6fc0d9f3b43315b863e9f887e7cc5feb47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20Cerveau?= Date: Tue, 18 Jan 2022 16:03:34 +0100 Subject: [PATCH] app: port support update Connect the port to the element pad capabilities. Can now create a port only if the element supports the request pad Can only delete a port which is a "sometimes" port/pad. Introduce GPS module. --- Cargo.lock | 28 ++--- TODO.md | 7 +- src/app.rs | 107 +++++++++++------- src/gps.ui | 4 +- src/gps/element.rs | 222 ++++++++++++++++++++++++++++++++++++++ src/gps/mod.rs | 7 ++ src/gps/pad.rs | 97 +++++++++++++++++ src/{ => gps}/pipeline.rs | 199 ---------------------------------- src/main.rs | 2 +- src/meson.build | 5 +- src/plugindialogs.rs | 7 +- 11 files changed, 422 insertions(+), 263 deletions(-) create mode 100644 src/gps/element.rs create mode 100644 src/gps/mod.rs create mode 100644 src/gps/pad.rs rename src/{ => gps}/pipeline.rs (50%) diff --git a/Cargo.lock b/Cargo.lock index d241d50..8fa178c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,9 +287,9 @@ dependencies = [ [[package]] name = "gdk4" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1023950739cd692fbf559507cad031bf2b0369d68466121f8a1316bc5ae29f0" +checksum = "228384db1fd6539f51152c97e59fbf29b6293988ee89bc915ebff229eb5cda58" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -303,9 +303,9 @@ dependencies = [ [[package]] name = "gdk4-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401b6be19db1e0ad2978fde7b46989b82f7595affa09cb56fe6f1302bc4f19e3" +checksum = "48a39e34abe35ee2cf54a1e29dd983accecd113ad30bdead5050418fa92f2a1b" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", @@ -510,9 +510,9 @@ dependencies = [ [[package]] name = "gsk4" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd7c79a864b8c4606d39fad3fd872688ea20ba8e833a9f38e905a458a29a8c" +checksum = "cc7de6ca62027aea1d3ab5d4a6bb2db24a296f56473d8a9a4d7ee33042745ff3" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -526,9 +526,9 @@ dependencies = [ [[package]] name = "gsk4-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d25ee96f7d1bdb6fa9945425aea95ce60efedf60c994eceb671fd93ad11d541" +checksum = "e31d21d7ce02ba261bb24c50c4ab238a10b41a2c97c32afffae29471b7cca69b" dependencies = [ "cairo-sys-rs", "gdk4-sys", @@ -594,9 +594,9 @@ dependencies = [ [[package]] name = "gtk4" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6fda2b49a25eefc1116f15d06585d333c21fc3ce499ab0a86641fac2fbf40ac" +checksum = "8622c50f55cbe83e78335cf95850ccefa99e8eac3335ef7d17473312402d7e89" dependencies = [ "bitflags 1.3.2", "cairo-rs", @@ -617,9 +617,9 @@ dependencies = [ [[package]] name = "gtk4-macros" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b17860e493ada8a40577f0e92bce8249a97c831e39d7811ef21de6281d67f86" +checksum = "c666e9d310431da43cb9d24995b247e5e24d11972d19bcd8b40e1663d25d0f51" dependencies = [ "anyhow", "proc-macro-crate", @@ -631,9 +631,9 @@ dependencies = [ [[package]] name = "gtk4-sys" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc1047ea7a0ff2947fbfb703eecfcca6b58912171037a4c55b1577015f079d5" +checksum = "1544614a8d44f38719a2fdfd608d644d5f385beab33e5852688f48a983a2b583" dependencies = [ "cairo-sys-rs", "gdk-pixbuf-sys", diff --git a/TODO.md b/TODO.md index aa9e070..98a75d0 100644 --- a/TODO.md +++ b/TODO.md @@ -27,12 +27,13 @@ ### Graphview - [ ] create a crate for graphview/node/port -- [ ] Remove a port from a node +- [x] Remove a port from a node if possible - [ ] Implement graphview unit test +- [x] add a css class for pad (presence always or sometimes) ### app -- [ ] check that a node accept to create a port on request (input/output) +- [x] check that a node accept to create a port on request (input/output) - [ ] Control the connection between element - [x] unable to connect in and in out and out - [ ] unable to connect element with incompatible caps. @@ -45,7 +46,7 @@ - [ ] Implement a command line parser to graph - [ ] Render the parse launch line in a message box - [ ] Prevent to create a pad in an element without the template -- [ ] Check the pîpeline live +- [ ] Check the pipeline validity - [ ] Implement pipeline unit test - [x] Save node position in XML - [x] Autosave the graph diff --git a/src/app.rs b/src/app.rs index e25f8a5..e727dc0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -35,13 +35,13 @@ use std::ops; use std::rc::{Rc, Weak}; use crate::about; +use crate::gps::{ElementInfo, PadInfo, Pipeline, PipelineState}; use crate::logger; -use crate::pipeline::{Pipeline, PipelineState}; use crate::plugindialogs; use crate::settings::Settings; use crate::{GPS_DEBUG, GPS_ERROR, GPS_TRACE, GPS_WARN}; -use crate::graphmanager::{GraphView, Node, PortDirection}; +use crate::graphmanager::{GraphView, Node, PortDirection, PortPresence}; #[derive(Debug)] pub struct GPSAppInner { @@ -192,7 +192,7 @@ impl GPSApp { application.add_action(&gio::SimpleAction::new("graph.add-plugin", None)); - application.add_action(&gio::SimpleAction::new("port.delete-link", None)); + application.add_action(&gio::SimpleAction::new("port.delete", None)); application.add_action(&gio::SimpleAction::new("node.add-to-favorite", None)); application.add_action(&gio::SimpleAction::new("node.delete", None)); @@ -224,6 +224,28 @@ impl GPSApp { pop_menu } + fn app_menu_action(&self, action_name: &str) -> SimpleAction { + let application = gio::Application::default() + .expect("No default application") + .downcast::() + .expect("Unable to downcast default application"); + + application + .lookup_action(action_name) + .unwrap_or_else(|| panic!("Unable to find action {}", action_name)) + .dynamic_cast::() + .expect("Unable to dynamic cast to SimpleAction") + } + + fn disconnect_app_menu_action(&self, action_name: &str) { + let action = self.app_menu_action(action_name); + + if let Some(signal_handler_id) = self.menu_signal_handlers.borrow_mut().remove(action_name) + { + action.disconnect(signal_handler_id); + } + } + fn connect_app_menu_action< F: Fn(&SimpleAction, std::option::Option<&glib::Variant>) + 'static, >( @@ -231,20 +253,8 @@ impl GPSApp { action_name: &str, f: F, ) { - let application = gio::Application::default() - .expect("No default application") - .downcast::() - .expect("Unable to downcast default application"); - let action = application - .lookup_action(action_name) - .unwrap_or_else(|| panic!("Unable to find action {}", action_name)) - .dynamic_cast::() - .expect("Unable to dynamic cast to SimpleAction"); - - if let Some(signal_handler_id) = self.menu_signal_handlers.borrow_mut().remove(action_name) - { - action.disconnect(signal_handler_id); - } + let action = self.app_menu_action(action_name); + self.disconnect_app_menu_action(action_name); let signal_handler_id = action.connect_activate(f); self.menu_signal_handlers .borrow_mut() @@ -459,7 +469,7 @@ impl GPSApp { } pub fn display_plugin_list(app: &GPSApp) { - let elements = Pipeline::elements_list().expect("Unable to obtain element's list"); + let elements = ElementInfo::elements_list().expect("Unable to obtain element's list"); plugindialogs::display_plugin_list(app, &elements); } @@ -660,9 +670,18 @@ impl GPSApp { .object("port_menu") .expect("Couldn't get menu model for port"); pop_menu.set_menu_model(Some(&menu)); - app.connect_app_menu_action("port.delete-link", move |_, _| { - GPS_DEBUG!("port.delete-link port {} node {}", port_id, node_id); - }); + + if app.graphview.borrow().can_remove_port(node_id, port_id) { + let app_weak = app.downgrade(); + app.connect_app_menu_action("port.delete", move |_, _| { + let app = upgrade_weak!(app_weak); + GPS_DEBUG!("port.delete-link port {} node {}", port_id, node_id); + app.graphview.borrow().remove_port(node_id, port_id); + }); + } else { + app.disconnect_app_menu_action("port.delete"); + } + pop_menu.show(); None }, @@ -679,6 +698,7 @@ impl GPSApp { let app = upgrade_weak!(app_weak, None); let node_id = values[1].get::().expect("node id args[1]"); + let node = app.graphview.borrow().node(node_id).expect("Unable to find node with this ID"); let point = values[2].get::().expect("point in args[2]"); let pop_menu = app.app_pop_menu_at_position(&*app.graphview.borrow(), point.to_vec2().x() as f64, point.to_vec2().y() as f64); @@ -708,28 +728,33 @@ impl GPSApp { } ); - let app_weak = app.downgrade(); - app.connect_app_menu_action("node.request-pad-input", - move |_,_| { - let app = upgrade_weak!(app_weak); - GPS_DEBUG!("node.request-pad-input {}", node_id); - let mut node = app.graphview.borrow().node(node_id).unwrap(); - let port_id = app.graphview.borrow().next_port_id(); - node.add_port(port_id, "in", PortDirection::Input); - } - ); - + if ElementInfo::element_supports_new_pad_request(&node.name(), PortDirection::Input) { + let app_weak = app.downgrade(); + app.connect_app_menu_action("node.request-pad-input", + move |_,_| { + let app = upgrade_weak!(app_weak); + GPS_DEBUG!("node.request-pad-input {}", node_id); + let port_id = app.graphview.borrow().next_port_id(); + app.graphview.borrow().add_port(node_id, port_id, "in", PortDirection::Input, PortPresence::Sometimes); + } + ); + } else { + app.disconnect_app_menu_action("node.request-pad-input"); + } + if ElementInfo::element_supports_new_pad_request(&node.name(), PortDirection::Output) { let app_weak = app.downgrade(); app.connect_app_menu_action("node.request-pad-output", move |_,_| { let app = upgrade_weak!(app_weak); GPS_DEBUG!("node.request-pad-output {}", node_id); - let mut node = app.graphview.borrow_mut().node(node_id).unwrap(); let port_id = app.graphview.borrow_mut().next_port_id(); - node.add_port(port_id, "out", PortDirection::Output); + app.graphview.borrow().add_port(node_id, port_id, "out", PortDirection::Output, PortPresence::Sometimes); } ); + } else { + app.disconnect_app_menu_action("node.request-pad-output"); + } let app_weak = app.downgrade(); app.connect_app_menu_action("node.properties", @@ -771,8 +796,8 @@ impl GPSApp { pub fn add_new_element(&self, element_name: &str) { let graph_view = self.graphview.borrow(); let node_id = graph_view.next_node_id(); - let pads = Pipeline::pads(element_name, false); - if Pipeline::element_is_uri_src_handler(element_name) { + let (inputs, outputs) = PadInfo::pads(element_name, false); + if ElementInfo::element_is_uri_src_handler(element_name) { GPSApp::get_file_from_dialog(self, false, move |app, filename| { GPS_DEBUG!("Open file {}", filename); let node = app.graphview.borrow().node(node_id).unwrap(); @@ -783,9 +808,13 @@ impl GPSApp { } graph_view.add_node_with_port( node_id, - Node::new(node_id, element_name, Pipeline::element_type(element_name)), - pads.0, - pads.1, + Node::new( + node_id, + element_name, + ElementInfo::element_type(element_name), + ), + inputs.len() as u32, + outputs.len() as u32, ); } diff --git a/src/gps.ui b/src/gps.ui index 2498107..6f3cbb4 100644 --- a/src/gps.ui +++ b/src/gps.ui @@ -31,8 +31,8 @@
- _Delete link - app.port.delete-link + _Delete + app.port.delete
diff --git a/src/gps/element.rs b/src/gps/element.rs new file mode 100644 index 0000000..90f13a9 --- /dev/null +++ b/src/gps/element.rs @@ -0,0 +1,222 @@ +// element.rs +// +// Copyright 2022 Stéphane Cerveau +// +// 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 . +// +// SPDX-License-Identifier: GPL-3.0-only + +use crate::gps::PadInfo; +use crate::graphmanager::{NodeType, PortDirection, PortPresence}; +use crate::logger; +use crate::GPS_INFO; + +use gst::glib; +use gst::prelude::*; +use gstreamer as gst; +use std::collections::HashMap; + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ElementInfo { + pub name: Option, + plugin_name: Option, + rank: i32, +} + +impl Default for ElementInfo { + fn default() -> ElementInfo { + ElementInfo { + name: None, + plugin_name: None, + rank: -1, + } + } +} +impl ElementInfo { + pub fn elements_list() -> anyhow::Result> { + let registry = gst::Registry::get(); + let mut elements: Vec = Vec::new(); + let plugins = gst::Registry::plugin_list(®istry); + for plugin in plugins { + let plugin_name = gst::Plugin::plugin_name(&plugin); + let features = gst::Registry::feature_list_by_plugin(®istry, &plugin_name); + for feature in features { + let mut element = ElementInfo::default(); + if let Ok(factory) = feature.downcast::() { + let feature = factory.upcast::(); + + element.name = Some(gst::PluginFeature::name(&feature).as_str().to_owned()); + element.plugin_name = + Some(gst::Plugin::plugin_name(&plugin).as_str().to_owned()); + elements.push(element); + } + } + } + elements.sort(); + Ok(elements) + } + + pub fn element_feature(element_name: &str) -> Option { + let registry = gst::Registry::get(); + gst::Registry::find_feature(®istry, element_name, gst::ElementFactory::static_type()) + } + + pub fn element_description(element_name: &str) -> anyhow::Result { + let mut desc = String::from(""); + let feature = ElementInfo::element_feature(element_name) + .ok_or_else(|| glib::bool_error!("Failed get element feature"))?; + + if let Ok(factory) = feature.downcast::() { + desc.push_str("Factory details:\n"); + desc.push_str("Name:"); + desc.push_str(&factory.name()); + desc.push('\n'); + + let element_keys = factory.metadata_keys(); + for key in element_keys { + let val = factory.metadata(&key); + if let Some(val) = val { + desc.push_str(""); + desc.push_str(&key); + desc.push_str(":"); + desc.push_str(>k::glib::markup_escape_text(&val).to_string()); + desc.push('\n'); + } + } + let feature = factory.upcast::(); + let plugin = gst::PluginFeature::plugin(&feature); + if let Some(plugin) = plugin { + desc.push('\n'); + desc.push_str("Plugin details:"); + desc.push('\n'); + desc.push_str("Name:"); + desc.push_str(""); + desc.push_str(gst::Plugin::plugin_name(&plugin).as_str()); + desc.push('\n'); + desc.push_str("Description:"); + desc.push_str(""); + desc.push_str(>k::glib::markup_escape_text(&plugin.description()).to_string()); + desc.push('\n'); + desc.push_str("Filename:"); + desc.push_str(""); + desc.push_str( + >k::glib::markup_escape_text( + &plugin.filename().unwrap().as_path().display().to_string(), + ) + .to_string(), + ); + desc.push('\n'); + desc.push_str("Version:"); + desc.push_str(""); + desc.push_str(>k::glib::markup_escape_text(&plugin.version()).to_string()); + desc.push('\n'); + } + } + Ok(desc) + } + + pub fn element_type(element_name: &str) -> NodeType { + let (inputs, outputs) = PadInfo::pads(element_name, true); + let mut element_type = NodeType::Source; + if !inputs.is_empty() { + if !outputs.is_empty() { + element_type = NodeType::Transform; + } else { + element_type = NodeType::Sink; + } + } else if !outputs.is_empty() { + element_type = NodeType::Source; + } + + element_type + } + + fn value_as_str(v: &glib::Value) -> Option { + match v.type_() { + glib::Type::I8 => Some(str_some_value!(v, i8).to_string()), + glib::Type::U8 => Some(str_some_value!(v, u8).to_string()), + glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()), + glib::Type::I32 => Some(str_some_value!(v, i32).to_string()), + glib::Type::U32 => Some(str_some_value!(v, u32).to_string()), + glib::Type::I64 => Some(str_some_value!(v, i64).to_string()), + glib::Type::U64 => Some(str_some_value!(v, u64).to_string()), + glib::Type::F32 => Some(str_some_value!(v, f32).to_string()), + glib::Type::F64 => Some(str_some_value!(v, f64).to_string()), + glib::Type::STRING => str_opt_value!(v, String), + _ => None, + } + } + + pub fn element_properties(element_name: &str) -> anyhow::Result> { + let mut properties_list = HashMap::new(); + let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature"); + + let factory = feature + .downcast::() + .expect("Unable to get the factory from the feature"); + let element = factory.create(None)?; + let params = element.class().list_properties(); + + for param in params { + GPS_INFO!("Property_name {}", param.name()); + if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE + || (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE + { + let value = ElementInfo::value_as_str(&element.property(param.name()).unwrap()) + .unwrap_or_else(|| String::from("")); + properties_list.insert(String::from(param.name()), value); + } else if let Some(value) = ElementInfo::value_as_str(param.default_value()) { + properties_list.insert(String::from(param.name()), value); + } else { + GPS_INFO!("Unable to add property_name {}", param.name()); + } + } + Ok(properties_list) + } + + pub fn element_is_uri_src_handler(element_name: &str) -> bool { + let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature"); + + let factory = feature + .downcast::() + .expect("Unable to get the factory from the feature"); + let element = factory + .create(None) + .expect("Unable to create an element from the feature"); + match element.dynamic_cast::() { + Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src, + Err(_e) => false, + } + } + + pub fn element_supports_new_pad_request(element_name: &str, direction: PortDirection) -> bool { + let (inputs, outputs) = PadInfo::pads(element_name, true); + if direction == PortDirection::Input { + for input in inputs { + if input.presence() == PortPresence::Sometimes { + return true; + } + } + } else if direction == PortDirection::Output { + for output in outputs { + if output.presence() == PortPresence::Sometimes { + return true; + } + } + } else { + GPS_ERROR!("Port direction unknown"); + } + false + } +} diff --git a/src/gps/mod.rs b/src/gps/mod.rs new file mode 100644 index 0000000..8fc5585 --- /dev/null +++ b/src/gps/mod.rs @@ -0,0 +1,7 @@ +mod element; +mod pad; +mod pipeline; + +pub use element::ElementInfo; +pub use pad::PadInfo; +pub use pipeline::{Pipeline, PipelineState}; diff --git a/src/gps/pad.rs b/src/gps/pad.rs new file mode 100644 index 0000000..72f0461 --- /dev/null +++ b/src/gps/pad.rs @@ -0,0 +1,97 @@ +// pad.rs +// +// Copyright 2022 Stéphane Cerveau +// +// 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 . +// +// SPDX-License-Identifier: GPL-3.0-only + +use crate::logger; +use crate::GPS_INFO; + +use crate::gps::ElementInfo; +use crate::graphmanager::{PortDirection, PortPresence}; + +use gst::prelude::*; +use gstreamer as gst; + +#[derive(Debug, PartialOrd, PartialEq)] +pub struct PadInfo { + name: Option, + element_name: Option, + direction: PortDirection, + presence: PortPresence, +} + +impl Default for PadInfo { + fn default() -> PadInfo { + PadInfo { + name: None, + element_name: None, + direction: PortDirection::Unknown, + presence: PortPresence::Unknown, + } + } +} +impl PadInfo { + pub fn presence(&self) -> PortPresence { + self.presence + } + + fn pad_to_port_presence(presence: gst::PadPresence) -> PortPresence { + match presence { + gst::PadPresence::Always => PortPresence::Always, + gst::PadPresence::Sometimes => PortPresence::Sometimes, + gst::PadPresence::Request => PortPresence::Sometimes, + _ => PortPresence::Unknown, + } + } + + pub fn pads(element_name: &str, include_on_request: bool) -> (Vec, Vec) { + let feature = ElementInfo::element_feature(element_name).expect("Unable to get feature"); + let mut input = vec![]; + let mut output = vec![]; + + if let Ok(factory) = feature.downcast::() { + if factory.num_pad_templates() > 0 { + let pads = factory.static_pad_templates(); + for pad in pads { + GPS_INFO!("Found a pad name {}", pad.name_template()); + if pad.presence() == gst::PadPresence::Always + || (include_on_request + && (pad.presence() == gst::PadPresence::Request + || pad.presence() == gst::PadPresence::Sometimes)) + { + if pad.direction() == gst::PadDirection::Src { + output.push(PadInfo { + name: Some(pad.name_template().to_string()), + element_name: Some(element_name.to_string()), + direction: PortDirection::Output, + presence: PadInfo::pad_to_port_presence(pad.presence()), + }); + } else if pad.direction() == gst::PadDirection::Sink { + input.push(PadInfo { + name: Some(pad.name_template().to_string()), + element_name: Some(element_name.to_string()), + direction: PortDirection::Input, + presence: PadInfo::pad_to_port_presence(pad.presence()), + }); + } + } + } + } + } + (input, output) + } +} diff --git a/src/pipeline.rs b/src/gps/pipeline.rs similarity index 50% rename from src/pipeline.rs rename to src/gps/pipeline.rs index c7daa01..9834a70 100644 --- a/src/pipeline.rs +++ b/src/gps/pipeline.rs @@ -30,23 +30,6 @@ use std::fmt; use std::ops; use std::rc::{Rc, Weak}; -#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] -pub struct ElementInfo { - pub name: Option, - plugin_name: Option, - rank: i32, -} - -impl Default for ElementInfo { - fn default() -> ElementInfo { - ElementInfo { - name: None, - plugin_name: None, - rank: -1, - } - } -} - #[derive(Debug, Clone, Copy, PartialEq)] pub enum PipelineState { Playing, @@ -197,188 +180,6 @@ impl Pipeline { }; } - pub fn elements_list() -> anyhow::Result> { - let registry = gst::Registry::get(); - let mut elements: Vec = Vec::new(); - let plugins = gst::Registry::plugin_list(®istry); - for plugin in plugins { - let plugin_name = gst::Plugin::plugin_name(&plugin); - let features = gst::Registry::feature_list_by_plugin(®istry, &plugin_name); - for feature in features { - let mut element = ElementInfo::default(); - if let Ok(factory) = feature.downcast::() { - let feature = factory.upcast::(); - - element.name = Some(gst::PluginFeature::name(&feature).as_str().to_owned()); - element.plugin_name = - Some(gst::Plugin::plugin_name(&plugin).as_str().to_owned()); - elements.push(element); - } - } - } - elements.sort(); - Ok(elements) - } - - fn element_feature(element_name: &str) -> Option { - let registry = gst::Registry::get(); - gst::Registry::find_feature(®istry, element_name, gst::ElementFactory::static_type()) - } - - pub fn element_description(element_name: &str) -> anyhow::Result { - let mut desc = String::from(""); - let feature = Pipeline::element_feature(element_name) - .ok_or_else(|| glib::bool_error!("Failed get element feature"))?; - - if let Ok(factory) = feature.downcast::() { - desc.push_str("Factory details:\n"); - desc.push_str("Name:"); - desc.push_str(&factory.name()); - desc.push('\n'); - - let element_keys = factory.metadata_keys(); - for key in element_keys { - let val = factory.metadata(&key); - if let Some(val) = val { - desc.push_str(""); - desc.push_str(&key); - desc.push_str(":"); - desc.push_str(>k::glib::markup_escape_text(&val).to_string()); - desc.push('\n'); - } - } - let feature = factory.upcast::(); - let plugin = gst::PluginFeature::plugin(&feature); - if let Some(plugin) = plugin { - desc.push('\n'); - desc.push_str("Plugin details:"); - desc.push('\n'); - desc.push_str("Name:"); - desc.push_str(""); - desc.push_str(gst::Plugin::plugin_name(&plugin).as_str()); - desc.push('\n'); - desc.push_str("Description:"); - desc.push_str(""); - desc.push_str(>k::glib::markup_escape_text(&plugin.description()).to_string()); - desc.push('\n'); - desc.push_str("Filename:"); - desc.push_str(""); - desc.push_str( - >k::glib::markup_escape_text( - &plugin.filename().unwrap().as_path().display().to_string(), - ) - .to_string(), - ); - desc.push('\n'); - desc.push_str("Version:"); - desc.push_str(""); - desc.push_str(>k::glib::markup_escape_text(&plugin.version()).to_string()); - desc.push('\n'); - } - } - Ok(desc) - } - - pub fn pads(element_name: &str, include_on_request: bool) -> (u32, u32) { - let feature = Pipeline::element_feature(element_name).expect("Unable to get feature"); - let mut input = 0; - let mut output = 0; - - if let Ok(factory) = feature.downcast::() { - if factory.num_pad_templates() > 0 { - let pads = factory.static_pad_templates(); - for pad in pads { - if pad.presence() == gst::PadPresence::Always - || (include_on_request - && (pad.presence() == gst::PadPresence::Request - || pad.presence() == gst::PadPresence::Sometimes)) - { - if pad.direction() == gst::PadDirection::Src { - output += 1; - } else if pad.direction() == gst::PadDirection::Sink { - input += 1; - } - } - } - } - } - (input, output) - } - - pub fn element_type(element_name: &str) -> NodeType { - let pads = Pipeline::pads(element_name, true); - let mut element_type = NodeType::Source; - if pads.0 > 0 { - if pads.1 > 0 { - element_type = NodeType::Transform; - } else { - element_type = NodeType::Sink; - } - } else if pads.1 > 0 { - element_type = NodeType::Source; - } - - element_type - } - - fn value_as_str(v: &glib::Value) -> Option { - match v.type_() { - glib::Type::I8 => Some(str_some_value!(v, i8).to_string()), - glib::Type::U8 => Some(str_some_value!(v, u8).to_string()), - glib::Type::BOOL => Some(str_some_value!(v, bool).to_string()), - glib::Type::I32 => Some(str_some_value!(v, i32).to_string()), - glib::Type::U32 => Some(str_some_value!(v, u32).to_string()), - glib::Type::I64 => Some(str_some_value!(v, i64).to_string()), - glib::Type::U64 => Some(str_some_value!(v, u64).to_string()), - glib::Type::F32 => Some(str_some_value!(v, f32).to_string()), - glib::Type::F64 => Some(str_some_value!(v, f64).to_string()), - glib::Type::STRING => str_opt_value!(v, String), - _ => None, - } - } - - pub fn element_properties(element_name: &str) -> anyhow::Result> { - let mut properties_list = HashMap::new(); - let feature = Pipeline::element_feature(element_name).expect("Unable to get feature"); - - let factory = feature - .downcast::() - .expect("Unable to get the factory from the feature"); - let element = factory.create(None)?; - let params = element.class().list_properties(); - - for param in params { - GPS_INFO!("Property_name {}", param.name()); - if (param.flags() & glib::ParamFlags::READABLE) == glib::ParamFlags::READABLE - || (param.flags() & glib::ParamFlags::READWRITE) == glib::ParamFlags::READWRITE - { - let value = Pipeline::value_as_str(&element.property(param.name()).unwrap()) - .unwrap_or_else(|| String::from("")); - properties_list.insert(String::from(param.name()), value); - } else if let Some(value) = Pipeline::value_as_str(param.default_value()) { - properties_list.insert(String::from(param.name()), value); - } else { - GPS_INFO!("Unable to add property_name {}", param.name()); - } - } - Ok(properties_list) - } - - pub fn element_is_uri_src_handler(element_name: &str) -> bool { - let feature = Pipeline::element_feature(element_name).expect("Unable to get feature"); - - let factory = feature - .downcast::() - .expect("Unable to get the factory from the feature"); - let element = factory - .create(None) - .expect("Unable to create an element from the feature"); - match element.dynamic_cast::() { - Ok(uri_handler) => uri_handler.uri_type() == gst::URIType::Src, - Err(_e) => false, - } - } - // Render graph methods fn process_gst_node( &self, diff --git a/src/main.rs b/src/main.rs index 2651046..54b2eae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,7 +26,7 @@ mod config; mod graphmanager; #[macro_use] mod logger; -mod pipeline; +mod gps; mod plugindialogs; mod settings; use gtk::prelude::*; diff --git a/src/meson.build b/src/meson.build index c45ce25..5ba8b64 100644 --- a/src/meson.build +++ b/src/meson.build @@ -28,7 +28,10 @@ rust_sources = files( 'logger.rs', 'macros.rs', 'main.rs', - 'pipeline.rs', + 'gps/pipeline.rs', + 'gps/element.rs', + 'gps/pad.rs', + 'gps/mod.rs', 'plugindialogs.rs', 'settings.rs', diff --git a/src/plugindialogs.rs b/src/plugindialogs.rs index a08c27c..2f33150 100644 --- a/src/plugindialogs.rs +++ b/src/plugindialogs.rs @@ -17,9 +17,8 @@ // // SPDX-License-Identifier: GPL-3.0-only use crate::app::GPSApp; +use crate::gps::ElementInfo; use crate::logger; -use crate::pipeline::ElementInfo; -use crate::pipeline::Pipeline; use gtk::glib; use gtk::prelude::*; use gtk::TextBuffer; @@ -93,7 +92,7 @@ pub fn display_plugin_list(app: &GPSApp, elements: &[ElementInfo]) { if let Some((model, iter)) = selection.selected() { let element_name = model .get::(&iter, 1); - let description = Pipeline::element_description(&element_name).expect("Unable to get element description from GStreamer"); + let description = ElementInfo::element_description(&element_name).expect("Unable to get element description from GStreamer"); text_buffer.set_text(""); text_buffer.insert_markup(&mut text_buffer.end_iter(), &description); } @@ -133,7 +132,7 @@ pub fn display_plugin_properties(app: &GPSApp, element_name: &str, node_id: u32) .expect("Couldn't get box-plugin-properties"); let update_properties: Rc>> = Rc::new(RefCell::new(HashMap::new())); - let properties = Pipeline::element_properties(element_name).unwrap(); + let properties = ElementInfo::element_properties(element_name).unwrap(); for (name, value) in properties { let entry_box = Box::new(gtk::Orientation::Horizontal, 6); let label = Label::new(Some(&name));